指针的本质
指针就是地址,而指针用指针变量来存储
指针变量和数组没有关系
指针变量也是一种变量类型,比如char *p 就是指向char类型的指针,所以指针类型的定义方法是
- type + *
若是 char **p 就可以拆开理解为指向char* 的指针p.
由于数组可以使用下标访问和指针访问,所以通常会混淆以为数组就是指针,其实数组就是一种分配连续空间的变量类型,和指针变量是不同的。同时数组传参会降级形成指向数组内部元素类型的指针。
指针数组
是一个数组,里面存放的都是指针
char* arr[10];
数组指针
是一个指针,指向的是数组
char (*arr)[10];
利用括号优先级确定了arr是指针类型,指向一个大小为10的数组
这个在指向二维数组的时候可以用到
int arr[3][4];
int (*p)[4];
多维数组
如上定义的数组指针为什么就可以指向二维数组?
因为二维数组若看作线性结构,其实就是一个长度为3的一维数组,只不过每个元素都是长度为4的数组,而不是平常的一个整数。
所以二维数组第一个元素就是一个长度为4的数组,定义一个指向数组元素的类型的指针就可以指向它了。
int arr[3][4];
a = sizeof(a[0]);
b = sizeof(a[0]+1);
c = sizeof(a +1);
a 也可以写作 *(a+0),就是二维数组第一个元素,就是第一个4个元素数组的数组名,所以是16.
b 相当于 *(a+0) +1,也就是 &a[0][1]
c 指向二维数组的第二个元素首地址
整型指针和字符指针的差别
int a = 1
int *pa = &a;
char *pb = "abc";
printf("%d",pa);
printf("%d",pb);
上述代码打印结果都是8,这个取决于电脑的位数(字长),32位机指针都是4字节,64位机8字节。也就是说所有指针的存储空间是一样的,而差异在于存取权限不一样,比如:
char a[]="abce";
char *pa = a;
int *pb = (int *) a;
*pa = '1'; //数组会变成1bce
*pb = '1'; //数组会变成1
说明了char * 的存储权限是1个字节,而int *的权限是4个字节.
深度理解数组名和数组元素地址
char* pa="abc";
char* pb="abc";
//以上 pa = pb ,相同的字符串存同一份
数组名作为整个数组的两种情况
- sizeof(arr)
- &arr
其他情况均为数组首元素地址,以此作为判断条件,区分以下式子的值
注意sizeof和strlen的区别在于sizeof计算空间字节数,strlen是从当前地址向后数字符个数,直至\0停止。
而sizeof的值的判断只有两种,值和地址。
#include <stdio.h>
#include <string.h>
int main(){
char arr[] = {'a','b','c','d','e'}; //后面不会补 \0 的
printf("%d\n",strlen(arr)); //随机 整个数组字符数
printf("%d\n",strlen(arr+0)); //随机 首元素开始往后数
// printf("%d\n",strlen(*arr)); //err
// printf("%d\n",strlen(arr[1])); //err
printf("%d\n",strlen(&arr)); //随机 整个数组字符数
printf("%d\n",strlen(&arr+1)); //随机 跳过整个数组
printf("%d\n",strlen(&arr[0]+1)); //随机 从第二个元素向后数
printf("\n");
printf("%d\n",sizeof(arr)); //5 数组名单独作为整个数组(不是地址)
printf("%d\n",sizeof(arr+0)); //8 +0后降级作为首元素地址
printf("%d\n",sizeof(*arr)); //1 首元素解引用
printf("%d\n",sizeof(arr[1])); //1 首元素
printf("%d\n",sizeof(&arr)); //8 整个数组的地址
printf("%d\n",sizeof(&arr+1)); //8 指向整个数组后的地址
printf("%d\n",sizeof(&arr[0]+1)); //8 第二个元素地址
char arr[] = "abcdef";
printf("%d\n",strlen(arr)); //6 整个数组字符数
printf("%d\n",strlen(arr+0)); //6 首元素开始往后数
// printf("%d\n",strlen(*arr)); //err
// printf("%d\n",strlen(arr[1])); //err
printf("%d\n",strlen(&arr)); //6 整个数组字符数
printf("%d\n",strlen(&arr+1)); //随机 跳过整个数组
printf("%d\n",strlen(&arr[0]+1)); //5 从第二个元素向后数
printf("%d\n",sizeof(arr)); //7 数组名单独作为整个数组(不是地址)
printf("%d\n",sizeof(arr+0)); //8 +0后降级作为首元素地址
printf("%d\n",sizeof(*arr)); //1 首元素解引用
printf("%d\n",sizeof(arr[1])); //1 首元素
printf("%d\n",sizeof(&arr)); //8 整个数组的地址
printf("%d\n",sizeof(&arr+1)); //8 指向整个数组后的地址
printf("%d\n",sizeof(&arr[0]+1)); //8 第二个元素地址
char *p = "abcdef"; //p存放的是地址,指向a
printf("%d\n",sizeof(p)); //8 首元素地址
printf("%d\n",sizeof(p+1)); //8 次元素地址
printf("%d\n",sizeof(*p)); //1 首元素解引用
printf("%d\n",sizeof(p[0])); //1 相当于*(p+0)
printf("%d\n",sizeof(&p)); //8 取出指针p的地址
printf("%d\n",sizeof(&p+1)); //8 指针p地址的下一块地址
printf("%d\n",sizeof(&p[0]+1)); //8 第二个元素地址
printf("\n");
printf("%d\n",strlen(p)); //6 从首地址往后数
printf("%d\n",strlen(p+1)); //5 从第二个元素往后数
// printf("%d\n",strlen(*p)); //错 传入的是第一个字符
// printf("%d\n",strlen(p[0])); //错
printf("%d\n",strlen(&p)); //随机值 是指针的地址
printf("%d\n",strlen(&p+1)); //随机值 指针的地址的下一块地址
printf("%d\n",strlen(&p[0]+1)); //5 从次元素地址往后数
//二维数组首元素是第一行地址
int a[3][4] = {0};
printf("%d\n",sizeof(a)); //48 整个数组大小
printf("%d\n",sizeof(a[0][0])); //4 第一行第一个元素大小
printf("%d\n",sizeof(a[0])); //16 是第一行的数组名,单独放在sizeof里,所以是第一行大小
printf("%d\n",sizeof(a[0]+1)); //8 第一行第二个元素地址
printf("%d\n",sizeof(a+1)); //8 数组地址+1降为第二行地址
printf("%d\n",sizeof(&a[0]+1)); //8 跳过整个第一行,成为第二行地址
printf("%d\n",sizeof(*a)); //16 第一行数组名单独放
printf("%d\n",sizeof(a[3])); //16 越界,但由于计算的是类型属性不会报错
}
函数指针
函数也是代码,代码就会有地址,那么函数的地址用函数指针变量来存储。
函数指针的声明
void (*p)(int); //返回值是void 参数是int
函数指针的调用
p(1) //或者 *p(1)
void (*signal(int, void(*)(int)))(int);
关于上面这个函数指针详解可以看http://blog.sina.com.cn/s/blog_4850a7880100hnam.html
函数指针可以当作参数传入函数,指向回调函数,函数指针封装在结构体中还可以使c语言面向对象编程。
函数指针数组
int (*)() arr[10];
上面的意思是定义一个数组,指向函数指针,对吗?
其实可以这样理解但是语法规则规定了,名字必须在中间
int (*arr[10])();
//正如指针数组的定义
int *p[10];
函数指针数组可以作为转移表
//把几个函数放进函数指针数组,形成转移表
int (*p[5])(int , int ) = {0, sum , sub, mul, div};
//使用转移表可以方便的调用其中的函数
int input = 1;
int ret = (*p[input])(a, b); //调用sum
指向 函数指针数组 的指针
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)[10])(const char*) = &pfunArr;
return 0;
}