指针的进阶
一、字符指针
(一)字符指针
字符指针指向的是字符串的首元素地址。
(二)常量字符串和字符数组
常量字符串不可以被修改并且在内存中(只读数据区)只会存在一份,而字符数组可以被修改。
二、指针数组和数组指针
(一)指针数组 int *p1[10]
指针数组是存放指针的数组。
代码中,arr数组就是指针数组。
使用指针数组,传参打印时使用二级指针接收。
(二)数组指针 int (*p2)[10]
数组指针是存放数组地址的指针 &arr
二位数组的数组名是第一行元素的地址。
传参打印时使用数组指针接收。
三、函数指针
(一)函数指针
1、概念
指向函数的指针,记得要给指针加上()否则指针变量会和后面的函数调用操作符相结合,编译错误。
2、两段有趣的代码
(1)( * ( void(*)()0) )();
本质上是调用了0地址处的那个函数。
(2)void (signal(int, void ()(int)))(int);
(二)函数指针数组
存放函数指针的数组叫函数叫做函数指针数组。
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
转移表
利用函数指针数组,我们可以将switch语句改成转移表,这样的代码往往是比较简洁的。
(三)指向函数指针数组的指针
四、回调函数
(一)定义
当把一个函数的地址作为参数传递给另外一个函数,另一个函数用函数指针类型来接收,被调用的函数就是回调函数。
利用回调函数,我们往往可以简化代码,达到泛型编程。
(二)qsort函数
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
这里我们使用了回调函数,只需要按需求写一个比较方式,qsort将会可以给任何类型的数据进行排序。
struct stu
{
char name[20];
int age;
};
int cmp_stu_by_name(const void* p1, const void* p2)
{
return strcmp(((struct stu*)p1)->name,
((struct stu*)p2)->name);
//记得要强制类型转化,指针才会按照字节数进行访问对应地址
}
(三)qsort自定义实现
五、sizeof 和 strlen 的剖析
(一)概念
sizeof :是操作符,统计的是字节数量,包括字符串末尾的’\0’。
strlen:字符串函数,遇到’\0’结束,统计字符串长度,如果没有’\0’,结果是随机值。
(二)sizeof例题
8/4 表示在32位平台是4个字节,64位平台是8个字节。
int main()
{
int a[] = { 1,2,3,4 };
printf("%zd\n", sizeof(a));// 16
printf("%zd\n", sizeof(a + 0));// 8/4
printf("%zd\n", sizeof(*a));// 4
printf("%zd\n", sizeof(a + 1));// 8/4
printf("%zd\n", sizeof(a[1]));// 4
printf("%zd\n", sizeof(&a));// 8/4
printf("%zd\n", sizeof(*&a));// 16
printf("%zd\n", sizeof(&a + 1));// 8/4
printf("%zd\n", sizeof(&a[0]));// 8/4
printf("%zd\n", sizeof(&a[0] + 1));//8/4
char* p = "abcdef";
printf("%zd\n", sizeof(p));//p是指针变量。计算的是指针变量p的大小,4/8个字节
printf("%zd\n", sizeof(&p));//&p是指针变量p的地址,4/8个字节
//&p -- char** -- 二级指针
return 0;
}
(三)strlen例题
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%zd\n", strlen(arr));//随机值
printf("%zd\n", strlen(arr + 0));//随机值
//printf("%zd\n", strlen(*arr));//arr是数组名表示首元素的地址
//*arr 是首元素 -- 'a' - 97 ,传递给strlen后,strlen 会认为97就是地址,然后去访问内存,err
//printf("%zd\n", strlen(arr[1]));//'b' -98 //err
printf("%zd\n", strlen((char*) & arr));//随机值
printf("%zd\n", strlen((char*) & arr + 1));//随机值
printf("%zd\n", strlen(&arr[0] + 1));//随机值
return 0;
}
六、指针杂论
(一)指针大小
指针的大小只和环境有关系。在32位平台下是4个字节,64位平台下是8个字节。
(二)指针类型的作用
不同的指针类型在指针的加减,*(解引用),和访问权限上面有差异
1、指针的加减
我们发现指针相加减的规则是
(类型)*p = a;
p + n = p + n * (sizeof(类型))
2、解引用和访问权限
这里要将int类型的地址转换成char*操作,需要进行强制类型转换。
下面这串代码看似正确,实际上,char*指针的访问权限只有一个字节,所以如果数组中值变大,就会发生错误。
3、强大的void指针
void指针相当于万能指针,可以将任何类型的变量赋值给它,但是之后指针变量不可以再重新赋值或者直接相加减。
(三)const修饰指针
const放在后面,指针指向区域不能改动,而放在前面,指针的内容不可修改。
(四)数组名的理解
一维数组:数组名其实是数组的首元素地址。
二维数组:数组名是第一排元素的地址。
但是有两种情况除外。
1、sizeof(数组名) 代表的是整个数组的大小。
2、&(数组名)代表整个数组的地址。
八、结束语
相信通过这篇文章,能对指针有更深刻的理解,对指针的应用也有提升。这是小编所希望看到的。大家如果也觉得有帮助,那就动动小手支持下吧!