九 指针和数组笔试题解析
(1)代码1展示
1. #include <stdio.h> 2. int main() 3. { 4. int a[] = { 1,2,3,4 }; 5. printf("%d\n", sizeof(a));//16(整个数组的大小) 数组名a单独放在sizeof内部,计算的是整个数组的大小 4*4=16 6. printf("%d\n", sizeof(a + 0));//4(首元素地址大小) 数组名没有单独放在sizeof内部,也没有&,所以这里的数组名是指的是数组首元素地址 7. // a+0 还是数组首元素地址,地址的大小4/8 在VS2019的环境内,是4 8. printf("%d\n", sizeof(*a));//4(首元素的大小) a表示首元素地址,*a 对首元素地址进行解引用,就是首元素。首元素的大小是int类型是4个字节 9. printf("%d\n", sizeof(a + 1));//4(第二个元素地址的大小) a表示首元素地址,a+1表示第二个元素的地址,4/8 在VS2019是4 10. printf("%d\n", sizeof(a[1]));//4(第二个元素的大小) 表示数组的第二个元素2,int类型 4 11. printf("%d\n", sizeof(&a));//4(整个数组的地址)a表示整个数组的大小,&a 整个数组的地址的大小 4/8 在VS2019是4 12. printf("%d\n", sizeof(*&a));//16(整个数组的大小) & 和 *相互抵消,相当于sizeof(a),所以是16; 13. printf("%d\n", sizeof(&a + 1));//4(地址的大小) &的优先级大于+的优先级 &a(数组的地址) 跨过一个数组的地址,也是地址 4/8 在VS2019是4 14. printf("%d\n", sizeof(&a[0]));//4 1这个元素的地址 4/8 在VS2019是4 15. printf("%d\n", sizeof(&a[0] + 1));//4 2 这个元素的地址 4/8 在VS2019是4 16. return 0; 17. }
(1)数组名是数组首元素地址,但是有两个例外:(1)sizeof(数组名),这里的数组名表示整个数组的大小,sizeof(数组名),计算的是整个数组的大小(单位是字节)(2)&数组名,这里的数组名,也表示整个数组,取出的是整个数组。 除了上面两种特殊情况外,剩下的所有数组名都是数组首元素地址。
(2)代码2展示
1. #include <stdio.h> 2. #include <string.h> 3. int main() 4. { 5. char arr[] = { 'a','b','c','d','e','f' }; 6. printf("%d\n", sizeof(arr));//6(整个数组的大小) 数组名arr单独放在sizeof内部,计算的是整个数组的大小 1*6=6 7. printf("%d\n", sizeof(arr + 0));//4(首元素地址大小) 数组名没有单独放在sizeof内部,也没有&,所以这里的数组名是指的是数组首元素地址 8. // arr+0 还是数组首元素地址,地址的大小4/8 在VS2019的环境内,是4 9. printf("%d\n", sizeof(*arr));//1 10. printf("%d\n", sizeof(arr[1]));//1 11. printf("%d\n", sizeof(&arr));//4/8 在VS2019是4 12. printf("%d\n", sizeof(&arr + 1));//4/8 在VS2019是4 跳过整个数组的地址 13. printf("%d\n", sizeof(&arr[0] + 1));//4/8 在VS2019是4 跳过一个元素的地址 第二个元素的地址 14. 15. //这个数组有6个元素是毫无疑问的,这个数组的后面和前面放的是什么,我们是不知道的,strlen关注的是\0,有\0才停止。 16. printf("%d\n", strlen(arr));// arr没有& 没有sizeof内部 所以arr是首元素地址,但是数组中没有\0,计算的时候不知道什么时候停止,所以就是随机值 17. printf("%d\n", strlen(arr + 0));// arr首元素地址,arr+0还是首元素地址, 所以就是随机值 18. printf("%d\n", strlen(*arr));//err, strlen需要的是一个地址,从这个地址开始向后找字符,直到\0,统计字符个数 19. //但是*arr是数组首元素,也就是'a',这时传给strlen就是'a'的ASCII值97,strlen函数会把97作为起始地址(违规地址),统计字符串,会形成内存访问冲突 20. printf("%d\n", strlen(arr[1]));// err 和上一个一样 内存访问冲突 21. printf("%d\n", strlen(&arr));// 随机值 整个数组的地址,arr代表整个数组,地址的类型是 char(*)[6],不是char*,所以会出现错误,虽然地址指针类型是不同的, 22. //但是 传参过去,按照strlen的类型开始运行,会出现随机值 23. printf("%d\n", strlen(&arr + 1));// 跳过整个数组的地址 随机值 24. printf("%d\n", strlen(&arr[0] + 1));// 第二个元素的地址 25. return 0; 26. }
strlen 求字符串的长度 关注的就是‘\0’前有多少个字符
sizeof()只关注占用内存空间大小,单位是字节
sizeof 不关注类型
sizeof 是操作符
strlen(const char*)只关注\0的位置,计算的是\0之前出现了多少个字符,
strlen 针对字符串
strlen 是库函数
(3)代码3展示
1. #include <stdio.h> 2. #include <string.h> 3. int main() 4. { 5. char arr[] = "abcdef"; 6. printf("%d\n", sizeof(arr));//arr代表整个数组 sizeof 计算的是整个数组的大小 ,还有一个\0 7 7. printf("%d\n", sizeof(arr + 0));//arr首元素地址 4/8 4 8. printf("%d\n", sizeof(*arr));// arr首元素地址 1 9. printf("%d\n", sizeof(arr[1]));//首个元素 1 10. printf("%d\n", sizeof(&arr));// 整个数组的地址 4/8 4 11. printf("%d\n", sizeof(&arr + 1));// 跨过一个数组的地址 4/8 4 12. printf("%d\n", sizeof(&arr[0] + 1));// 跨过一个元素的地址 4/8 4 13. 14. printf("%d\n", strlen(arr));//arr首元素地址, \0之前的元素 6 15. printf("%d\n", strlen(arr + 0));//6 16. //printf("%d\n", strlen(*arr));//err 17. //printf("%d\n", strlen(arr[1]));//err 18. printf("%d\n", strlen(&arr));//整个数组的地址 地址参数不同 6 19. printf("%d\n", strlen(&arr + 1));// 跨过整个数组的地址 地址参数不同 随机值 20. printf("%d\n", strlen(&arr[0] + 1));//跨过一个元素的地址 5 21. return 0; 22. }
(4)代码4展示
1. #include <stdio.h> 2. #include <string.h> 3. int main() 4. { 5. char* p = "abcdef";//字符指针,存放'a'的地址 6. printf("%d\n", sizeof(p));//p是一个指针,sizeof计算的是指针变量的大小 4/8 7. printf("%d\n", sizeof(p + 1));// b的地址 4/8 8. printf("%d\n", sizeof(*p));// 字符'a' 1 9. printf("%d\n", sizeof(p[0]));//p[0]等价于*(p+0) 字符'a' 1 10. printf("%d\n", sizeof(&p));//二级地址 4/8 11. printf("%d\n", sizeof(&p + 1));// 二级地址 4/8 指向的是char*的地址,+1跨过一个字符指针(所有元素的地址)(四个字节)(也就是&p这个地址再加上四个字节),就是现在的地址 12. //例如 数组 &arr + 1,(arr里有3个元素(int)12个字节),那么&arr+1 就是&arr这个地址,再加上12个字节,就是现在的地址 13. printf("%d\n", sizeof(&p[0] + 1));//字符'b'的地址 4/8 14. 15. printf("%d\n", strlen(p));// 6 16. printf("%d\n", strlen(p + 1));// 5 17. //printf("%d\n", strlen(*p));// err 18. //printf("%d\n", strlen(p[0]));//err 19. printf("%d\n", strlen(&p));// 随机值 20. printf("%d\n", strlen(&p + 1));// 随机值 21. printf("%d\n", strlen(&p[0] + 1));//5 22. return 0; 23. }
sizeof 地址 4/8
strlen 地址 找 \0
(5)代码5展示
1. #include <stdio.h> 2. int main() 3. { 4. int a[3][4] = { 0 };//三行四列 5. printf("%d\n", sizeof(a));// 48 6. printf("%d\n", sizeof(a[0][0]));//4 7. printf("%d\n", sizeof(a[0]));//a[0]表示第一行数组名,a[0]作为数组名,存放在sizeof里,16 8. printf("%d\n", sizeof(a[0] + 1));//a[0] 没有取地址,没有放在sizeof内部,表示第一行首元素地址, 表示第二个元素的地址,4/8 9. printf("%d\n", sizeof(*(a[0] + 1)));//4 10. printf("%d\n", sizeof(a + 1));//a表示数组首元素地址(也就是第一行的地址),第二行的地址 4/8 11. printf("%d\n", sizeof(*(a + 1)));// 第二行地址是 &第二行数组名 解引用 是第二行数组名 16 12. printf("%d\n", sizeof(&a[0] + 1));// a[0]表示第一行的数组名,&第一行的数组名,就是第一行的地址, 表示第二行的地址 4/8 13. printf("%d\n", sizeof(*(&a[0] + 1)));//第二行的数组名 16 14. printf("%d\n", sizeof(*a));// 第一行的数组名 16 15. printf("%d\n", sizeof(a[3]));//感觉是越界了,但是a[3],就不会去访问,就不会去计算,仅仅看类型 第四行的数组名,16 16. return 0; 17. }
二维数组的数组名,代表二维数组的首元素地址,就是第一行的地址,就是&第一行数组名。
a[i] 第i行数组名
十 指针笔试题
笔试题(1)
1. #include <stdio.h> 2. int main() 3. { 4. int a[5] = { 1, 2, 3, 4, 5 }; 5. int* ptr = (int*)(&a + 1); 6. printf("%d,%d", *(a + 1), *(ptr - 1)); 7. return 0; 8. }
2 5
笔试题(2)
1. #include <stdio.h> 2. struct Test 3. { 4. int Num; 5. char* pcName; 6. short sDate; 7. char cha[2]; 8. short sBa[4]; 9. }*p;//p是结构体指针,这个结构体有20个字节 4+4+2+1*2+2*4=20 10. int main() 11. { 12. p = (struct test*)0x100000; 13. printf("%p\n", p + 0x1);//指针+1 14. printf("%p\n", (unsigned long)p + 0x1);//整数+1 15. printf("%p\n", (unsigned int*)p + 0x1);//指针+1 16. printf("%x\n", (unsigned long)p + 0x1); 17. printf("%x\n", (unsigned int*)p + 0x1); 18. return 0; 19. }//考点 +1 ,加到哪里
指针+1,跨过多少个字节,取决于指针的类型。
%p 以地址的形式打印,不省略
%x 打印16进制,省略0;
笔试题(3)
1. #include <stdio.h> 2. int main() 3. { 4. int a[4] = { 1, 2, 3, 4 }; 5. int* ptr1 = (int*)(&a + 1); 6. int* ptr2 = (int*)((int)a + 1); 7. printf("%x,%x", ptr1[-1], *ptr2); 8. return 0; 9. }
4 2000000
大小端
笔试题(4)
1. #include <stdio.h> 2. int main() 3. { 4. int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式 结果为 1 2 3 5. int* p; 6. p = a[0]; 7. printf("%d", p[0]); 8. return 0; 9. }
1
笔试题(5)
1. #include <stdio.h> 2. int main() 3. { 4. int a[5][5]; 5. int(*p)[4];//p是一个数组指针,指向的是4个元素的数组 6. p = a;//p和a地址一样 7. printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);p[4][2]等价于*(*(p+4)+2) 8. //指针p的类型是int(*)[4] p+1,指针+1,跨过指针指向的类型,p+4跨过16*4个字节,解引用后,指针的类型是int类型,+2,跨过4*2个字节。 p[4][2]是这个二维数组的第18个元素 a[4][2]是这个二维数组的第22个元素,指针相减,得到的是中间元素的个数,4,因为是低地址减去高地址,所以是-4,%p打印的是地址(也就是补码)所以是ff ff ff fc 9. return 0; 10. }
ff ff ff fc (-4的16进制)(补码) -4
%p打印的是内存中的补码
两个地址相减,得到的是中间元素的个数,
笔试题(6)
1. #include <stdio.h> 2. int main() 3. { 4. int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 5. int* ptr1 = (int*)(&aa + 1); 6. int* ptr2 = (int*)(*(aa + 1)); 7. printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); 8. return 0; 9. }
10 5
笔试题(7)
1. #include <stdio.h> 2. int main() 3. { 4. char* a[] = { "work","at","alibaba" };//存放字符指针的数组 5. char** pa = a;//a是数组首元素地址 ,首元素地址里面存放的是w的地址, 6. pa++;//跳过一个字符指针类型的地址,就是数组第二个元素的地址 7. printf("%s\n", *pa);//得到a的地址,打印字符串 8. return 0; 9. }
at
笔试题(8)
1. #include <stdio.h> 2. int main() 3. { 4. char* c[] = { "ENTER","NEW","POINT","FIRST" };//c存放的是这四个字符串首元素地址 5. char** cp[] = { c + 3,c + 2,c + 1,c };//存放的是数组c的地址,四个字符存放位置的地址(倒过来) 6. char*** cpp = cp;//数组cp的首元素地址 7. printf("%s\n", **++cpp);//前置++,先++,后使用, cpp+1得到的是cp第二个元素的地址,*得到的是 8. //cp第二个元素,第二个元素又是c第三个元素的地址,*得到的是c的第三个元素,%s打印,得到POINT 9. printf("%s\n", *-- * ++cpp + 3);//在第一个printf的时候,cpp发生了变化,++的优先级大于+的优先级,所以cpp+1得到的是cp的第三个元素的地址, 10. //*的优先级大于+的优先级,解引用后得到cp的第三个元素,是c第二个元素的地址,--的优先级大于+的优先级,--第一个元素的地址,解引用,第一个元素,是字符指针,+3,因为是字符指针,指向的是字符,所以跨越3个字符的地址,来到了E的地址,打印结果为 ER 11. printf("%s\n", *cpp[-2] + 3);//因为上面两个printf,cpp现在指的是cp的第三个元素的地址,cpp[-2] 等价于 *(cpp-2),得到的是cp的第一个元素,就是c的的第四个元素的地址,*后,得到字符指针F的地址,+3,就是S的地址,打印结果为ST 12. printf("%s\n", cpp[-1][-1] + 1);//cpp还是cp第三个元素的地址,cpp[-1][-1] 等价于 13. // *(*(cpp-1) - 1),打印结果为 EW(NEW) 14. return 0; 15. }
指针进阶就到此结束了!!!