目录
1.常量字符串指针
char p[6]="abcdef"就是常量字符串
char* ptr=p 就是指向常量字符串的指针
“abcdef”常量字符串由char类型指针指向后,实际指针存放的是字符串的首元素地址,解指针后根据首地址找到整个字符串,需要输出时一直往后打印直到遇到\0才停止,而非把整个字符串存到指针内
根据32位大小的编辑器可知,指针大小只有4字节,如果是全部存放,那么p具有6字节是不可能存放的
由于是常量字符串,所以指针是不能修改其指向位置的
如:
常量字符串是指在程序中固定不变的字符串。它们在定义后不能被修改。在大多数编程语言中,常量字符串通常使用双引号或单引号括起来表示
但这样就可以修改:
int main()
{
char* p = "abcdefg";
//*p = "w";
char r[8] = "abcdef";
r[2] = 'w';
printf("%s", r);
printf("%s", p);
return 0;
}
这个常量字符串可以被修改是因为它被存储在字符数组中,而不是存储在只读的内存区域中。在这种情况下,r 是一个字符数组,它有足够的空间来存储 "abcdef" 这个字符串,而且可以通过索引访问和修改其中的字符。虽然字符串常量本身是不可修改的,但将其复制到字符数组中后,就可以对字符数组进行修改。
所以为了防止常量字符串被修改,通常要在指针或数组前加上const
2.指针数组和数组指针
指针数组就是一个数组,里面存储了多个指针
如:int*p[10] 这就是一个存储了10个int*类型指针的数组
通常用来通过一个一维数组存放多个数组的地址,以此维护多个数组
数组指针就是一个指针,指针指向的是数组,存放的是整个数组的地址,不是首元素的地址
如:int(*p)[10] 这就是一个指向一个具有10个int类型元素的数组的指针
区分一下整个数组的地址和数组首元素地址
arr是数组首元素地址
&arr[0]是数组首元素地址
&arr是数组地址
sizeof(arr)是计算整个数组大小
所以对数组指针赋值时,注意要取整个数组的地址赋给数组指针
为什么一定要取整个数组的地址?
因为数组指针需要指向数组的起始位置,以便能够访问整个数组的元素。数组名表示数组首元素的地址,而数组指针需要存放整个数组的地址,这样才能通过指针来访问数组中的每个元素。通过存放整个数组的地址,数组指针可以根据偏移量来访问数组中的不同元素。这样,我们可以通过数组指针来操作整个数组,而不仅仅是单个元素。所以,数组指针存放整个数组的地址是为了能够方便地操作整个数组的元素。
eg:
int arr[20] = { 1,2,3,4,5 };
int(*p)[20] = &arr;
printf("%d", (*p)[1]);
此时p是数组指针,对指针解引用就是数组arr
因为[ ]的优先级比*高,所以判断究竟是数组还是指针就是看先跟哪个符号结合
数组指针一般用于二维以上数组,一维数组地址建议直接用arr得到首元素地址由int型指针指向就好,而不是非得&arr用数组指针,用数组指针较难以理解且麻烦
eg:
int arr[20] = { 1,2,3,4,5 };
int* p = arr;
printf("%d", (*p+1));
当二维数组需要传参时,传的是首元素地址,即第一行一维数组的地址,函数接收的参数就应该是一个一维数组指针来接收,这就是数组指针的用处
eg:
void printfs(int x, int y, int(*p)[5])
{
printf("%d", (*p+1)[1]);
}
int main()
{
int arr[5][5] = { {1,2,3},{2,3,4},{3,4,5} };
printfs(5, 5, arr);
return 0;
}
此时(*p+1)为二维数组首元素地址即第一排数组地址加一,就是第二排
(*p+1)[ 1 ]就是第二排第二个元素,也就是3,所以输出结果就是3
3.函数指针
函数指针是一个指针,指向一个函数,内部存储函数的地址
eg:void( * p ) ( int , int ) 这就是一个函数指针
返回类型为void,函数的参数为两个int
接下来我们来看两个复杂的例子
int main()
{
(*(void (*)())0)();
void (*People(int, void(*)(int)))(int);
return 0;
}
1 . (*(void (*)())0)()
观察这个代码,对其由内到外根据优先级拆分
首先看到void(*)()这是一个函数指针
接着(void(*)())0这是对int类型的0进行强制类型转换为指针类型
(*(void(*)())0)这是解引用,表示对该指针解析
(*(void(*)())0)()这就是一个返回类型为地址为0的函数的一个函数
2 . void (*People(int, void(*)(int)))(int)
首先看到void(*)(int)这是一个函数指针
接着People(int,void(*)(int))这是一个名为People的函数,剩下的就是其返回类型
即void(* )(int),所以People的返回类型为一个函数指针
注意
正常返回类型是写在函数名People的前面的,即void People
但函数指针这种返回类型则必须将函数名People嵌套在里面
所以对于函数指针void(*)()这种类型,可以利用关键字typedef起别名使其代码简化
eg:
int main()
{
typedef void(*haha)();
haha People(int, haha);//上面两句合起来就是下面的表达
void (*Peope(int, void(*)(int)))(int);
return 0;
}
将函数指针定义为haha后就可以使返回类型照正常情况放在函数名前面
注意typedef时,也要按照函数指针方式将别名嵌套在类型里面,正常情况是将别名放在类型后面
4.函数指针数组
函数指针数组是一个数组,内部存放多个函数指针
eg:void(*p [ 4 ] )()这就是一个函数指针数组,内部存放4个函数指针
函数指针数组主要用于管理多个函数,将其地址存放在一个数组中,简化冗余代码