前言:
除了sizeof()单独放数组名和&数组名,其他的数组名都仅仅代表首元素地址,
接下来我们就靠一些练习来巩固一下对指针的学习吧.
1.练习1 :一维数组
int main() { //一维数组 int a[] = { 1,2,3,4 };//4个元素,每个元素是int类型 printf("%d\n", sizeof(a)); //sizeof里面直接放数组名,计算的是整个数组的大小,所以是16 printf("%d\n", sizeof(a + 0));//这里数组名表示的是首元素地址,地址都是4或者8个字节 printf("%d\n", sizeof(*a)); //4个字节,相当于a[0] printf("%d\n", sizeof(a + 1));//表示第二个元素的地址,所以是4个或者8个字节 printf("%d\n", sizeof(a[1]));//第二个元素的大小,4个字节 printf("%d\n", sizeof(&a));//取到的是整个数组的地址,由于是地址,所以是4/8个字节 printf("%d\n", sizeof(*&a)); //16 对数组指针解引用访问一个数组的大小,单位是字节,等价于 sizeof(a) printf("%d\n", sizeof(&a + 1));//由于&a是一个指针数组类型,+1跳过一个数组,指向最后数组的后一个元素的地址,所以是4/8 printf("%d\n", sizeof(&a[0]));//取出的是第一个元素的地址,地址的大小是4/8 printf("%d\n", sizeof(&a[0] + 1));//取出的是第二个元素的地址,地址的大小是4/8 return 0; }
2.练习2:字符数组
//字符数组 char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", sizeof(arr)); //数组的大小,6个字节 printf("%d\n", sizeof(arr + 0));//首元素地址+1,是地址就是4/8个字节 printf("%d\n", sizeof(*arr));//首元素的大小,类型是char,所以是1个字节 printf("%d\n", sizeof(arr[1]));//第二个元素的大小,一个字节 printf("%d\n", sizeof(&arr));//整个数组的地址,4/8字节 printf("%d\n", sizeof(&arr + 1));//跳过整个数组后面的地址,地址就是4/8字节 printf("%d\n", sizeof(&arr[0] + 1));//跳过第一个元素的地址,地址就是4/8字节 //strlen是求字符串长度的,统计的是\0之前所有的元素个数 printf("%d\n", strlen(arr)); //arr是首元素地址,因为字符数组中没有手动添加\0,所以我们不知道后面到什么地方才会有\0出现,所以大小是随机值 大于等于6 printf("%d\n", strlen(arr + 0)); //随机值,同上 printf("%d\n", strlen(*arr)); // *arr是首元素,等价于字符a->97,strlen就把97当做一个地址 //97作为地址直接进行访问就是非法访问 printf("%d\n", strlen(arr[1])); //字符b,同样是非法访问 printf("%d\n", strlen(&arr)); //把数组指针传给了char*,类型发生变化但是值没变,随机值 printf("%d\n", strlen(&arr + 1)); //随机值 printf("%d\n", strlen(&arr[0] + 1));//随机值
3.练习3:字符数组2
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); //加上\0,一共7个字节 printf("%d\n", sizeof(arr + 0));//地址的大小就是4/8字节 printf("%d\n", sizeof(*arr));//a的大小是1个字节 printf("%d\n", sizeof(arr[1]));//b大小也是一个字节 printf("%d\n", sizeof(&arr));//取出整个数组的地址,地址就是4/8字节 printf("%d\n", sizeof(&arr + 1));//跳过数组,也是4/8字节 printf("%d\n", sizeof(&arr[0] + 1));//地址,4/8字节 printf("%d\n", strlen(arr)); //6字节 printf("%d\n", strlen(arr + 0));//同上 printf("%d\n", strlen(*arr)); //传进去的是97会报错 printf("%d\n", strlen(arr[1]));//同上 printf("%d\n", strlen(&arr));//6字节 printf("%d\n", strlen(&arr + 1));//跳过了整个数组,所以是随机值 printf("%d\n", strlen(&arr[0] + 1));//从第二个元素开始,所以是5字节
4.练习4:字符数组3
char* p = "abcdef"; //把字符串的首地址存在p里面 printf("%d\n", sizeof(p)); //首元素地址,所以是4/8字节 printf("%d\n", sizeof(p + 1)); //4/8 本质上就是地址+1,仍然是地址 printf("%d\n", sizeof(*p)); //1 *p == 'a' printf("%d\n", sizeof(p[0])); //同上 printf("%d\n", sizeof(&p)); //4/8 p变量的起始地址 类型是char**二级指针类型 printf("%d\n", sizeof(&p + 1)); //跳过一个char*,还是地址,4/8 printf("%d\n", sizeof(&p[0] + 1)); //地址,4/8 printf("%d\n", strlen(p)); //6字节 printf("%d\n", strlen(p + 1)); //跳过首元素,所以是五个字节 printf("%d\n", strlen(*p)); //err printf("%d\n", strlen(p[0])); //err printf("%d\n", strlen(&p)); //char**,这个地址有没有\0不可知,所以是随机值 printf("%d\n", strlen(&p + 1)); //随机值 printf("%d\n", strlen(&p[0]+1)); //5
5.练习5:二维数组
int a[3][4] = { 0 }; printf("%zd\n", sizeof(a)); //48 数组名单独放在sizeof内部,表示整个数组,所以计算的是整 个数组的大小,单位是字节 48 printf("%zd\n", sizeof(a[0][0]));//4 printf("%zd\n", sizeof(a[0])); //16 二维数组是数组的数组,a[0]单独放在sizeof内部,就计算的 是整个第一行的大小 printf("%zd\n", sizeof(a[0] + 1)); //4/8 a[0]并非单独放在sizeof内部,所以a[0]表示数组首元 素的地址, 也就是第一行第一个元素的地址,a[0] <-->&a[0][0],所以 a[0]+1表示&a[0][1] printf("%zd\n", sizeof(*(a[0] + 1)));//4 对第一行第二个元素进行解引用 printf("%zd\n", sizeof(a + 1)); //4/8 第二行地址 printf("%zd\n", sizeof(*(a + 1))); //16 第二行的大小 printf("%zd\n", sizeof(&a[0] + 1)); //4/8 第二行的地址 printf("%zd\n", sizeof(*(&a[0] + 1)));//16 第二行的大小 printf("%zd\n", sizeof(*a)); //16 第一行大小 printf("%zd\n", sizeof(a[3])); //16 不会越界,因为编译器会根据变量的类型直接进行判断,并不会 真的去访问 //表达式有两个属性,一个是值属性,一个是类型属性
6.小结
总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
其他数组名都表示首元素的地址.
7.几个关于指针的小题目
7.1 难度: *
int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1)); return 0; } //a这里表示首元素的地址,+就是第二个元素的地址,解引用得到2 //ptr指向数组的最后一个元素的后面的一个int的地址,-1则指向最后一个元素,解引用答案是5 //程序的结果是什么? //2 5
7.2 难度: **
//注:默认是x86的环境 struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main() { //考点:指针+1加几个字节? 取决于指针的类型 //注:十六进制 printf("%p\n", p + 0x1); //0x00100014 printf("%p\n", (unsigned long)p + 0x1); //0x00100001 printf("%p\n", (unsigned int*)p + 0x1); //0x00100004 return 0; }
7.3 难度: *** 破题点:画图
int main() { int a[4] = { 1, 2, 3, 4 }; int* ptr1 = (int*)(&a + 1); int* ptr2 = (int*)((int)a + 1); printf("%x,%x", ptr1[-1], *ptr2); //默认小端字节序 //内存 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 //ptr2指向01后面的00 Ox 02 00 00 00 还原出来就是2000000 //答案 4,2000000 return 0; }
7.4 难度: * 考点:逗号表达式
int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int* p; p = a[0]; printf("%d", p[0]); //1 return 0; }
7.5 难度: ***
int main() { int a[5][5]; int(*p)[4]; p = a; //类型不合适 //a - int(*)[5] //p - int(*)[4] //画图 printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); //%p打印就是把-4转成补码形式再转16进制 //指针相减是表示两个指针之间的元素个数 //10000000000000000000000000000100 //11111111111111111111111111111011 //11111111111111111111111111111100 -->地址FFFFFFFC //答案是FFFFFFFC,-4 return 0; }
7.6 难度: **
int main() { int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&aa + 1); int* ptr2 = (int*)(*(aa + 1)); printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //10 5 return 0; }
7.6 难度 **
int main() { char* a[] = { "work","at","alibaba" }; //a的每个元素是char*的,存的是首字符地址 //答案 at char** pa = a; pa++; printf("%s\n", *pa); return 0; }
7.7 难度 : ****
int main() { //++的优先级高于*号 char* c[] = { "ENTER","NEW","POINT","FIRST" }; char** cp[] = { c + 3,c + 2,c + 1,c }; char*** cpp = cp; printf("%s\n", **++cpp); //p的地址 POINT printf("%s\n", *-- * ++cpp + 3); //得到的是e的地址再+3打印出来就是ER printf("%s\n", *cpp[-2] + 3); //ST printf("%s\n", cpp[-1][-1] + 1);//EW return 0; }