6.指针和数组笔试题
环境:32 位机器
第一组
int a[] = {1,2,3,4}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(a[1])); printf("%d\n",sizeof(&a)); printf("%d\n",sizeof(*&a)); printf("%d\n",sizeof(&a+1)); printf("%d\n",sizeof(&a[0])); printf("%d\n",sizeof(&a[0]+1));
|
答案:
printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(a[1])); printf("%d\n",sizeof(&a)); printf("%d\n",sizeof(*&a)); printf("%d\n",sizeof(&a+1)); printf("%d\n",sizeof(&a[0])); printf("%d\n",sizeof(&a[0]+1));
|
第二组
char arr[] = {'a','b','c','d','e','f'}; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
|
答案:
printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
|
第三组
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
|
答案:
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr+0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr+1)); printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr+0)); printf("%d\n", strlen(*arr)); printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr+1)); printf("%d\n", strlen(&arr[0]+1));
|
第四组
char *p = "abcdef"; printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p+1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p+1)); printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p)); printf("%d\n", strlen(p+1)); 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));
|
答案:
char *p = "abcdef"; printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p+1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p+1)); printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p)); printf("%d\n", strlen(p+1)); 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));
|
指针为什么也可以用 []
运算符?
对于指针 int* p = “abc”;
p[1]
等价于 *(p + 1)
这是因为数组很多时候可以隐式转换成指针。
重点注意:printf("%d\n", strlen(&p));
&p
的类型是 char**
,但是C语言会将其隐式类型转换成 char*
,但是 strlen 访问的是地址p的内存空间,那这其实是未定义行为。
第五组
int a[3][4] = {0}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a[0][0])); printf("%d\n",sizeof(a[0])); printf("%d\n",sizeof(a[0]+1)); printf("%d\n",sizeof(*(a[0]+1))); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(*(a+1))); printf("%d\n",sizeof(&a[0]+1)); printf("%d\n",sizeof(*(&a[0]+1))); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a[3]));
|
答案:
int a[3][4] = {0};
printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a[0][0])); printf("%d\n",sizeof(a[0])); printf("%d\n",sizeof(a[0]+1)); printf("%d\n",sizeof(*(a[0]+1))); printf("%d\n",sizeof(a+1)); printf("%d\n",sizeof(*(a+1))); printf("%d\n",sizeof(&a[0]+1)); printf("%d\n",sizeof(*(&a[0]+1))); printf("%d\n",sizeof(*a)); printf("%d\n",sizeof(a[3]));
|
重点注意:
printf("%d\n",sizeof(a[0]+1))
printf("%d\n",sizeof(&a[0]+1))
a[0] 与 &a[0] 的差异比较:
int a[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {5, 10, 11, 12}, };
printf("%d\n", *(a[0] + 1)); printf("%d\n", **(&a[0] + 1));
|
printf("%d\n",sizeof(*(&a[0]+1)));
我们来一步一步分析:
a[0] -> int[4] ; &a[0] -> int (\*)[4] ; &a[0] + 1 -> int (\*)[4] ; *(&a[0] + 1) -> int[4]
printf("%d\n",sizeof(a[3]))
sizeof
是一个运算符,并不是函数。它在预编译时期替换。而我们说的“数组下标访问越界”前提条件是 内存访问越界,这个时期是程序运行时。a[3] 就是 int[4] 类型,所以就是 16。哪怕你写 a[100]都可以。
printf("%d\n", 16)
是程序运行时执行的语句。
关于 const
int num; const int* p = # int const* p = # int* const p = #
|
对于第一种写法,*p 是不能改变的;对于第三种写法,地址 p 是不能被改变的。
7. 指针笔试题
Ⅰ
int main(void) { int a[5] = { 1, 2, 3, 4, 5 }; int *ptr = (int *)(&a + 1); printf( "%d,%d", *(a + 1), *(ptr - 1)); return 0; }
|
a + 1
:a 隐式转换成 指针,指向 首地址后移 4 个字节。(a 隐式转换后是 int* 类型,它指向的 int 大小是 4 个字节,所以后移 4 个字节)
&a
的类型是 int(*)[5]
,所以 &a + 1
后移 int[5] 的长度
所以最后输出的是:2,5
Ⅱ
struct Test { int Num; char *pcName; short sDate; char cha[2]; short sBa[4]; }*p;
int main(void) { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
|
p + 0x1
p 加十六进制的 1,p 所指向的结构体大小是 20,所以 p 会增加 20 。但是注意 %p
输出的是 16 进制的地址,所以输出的是 0x100014
(unsigned long)p + 0x1
p 被强转成了一个数,所以输出的就是 0x100001
(unsigned int*)p + 0x1
p 被强转成了一个 int* 类型的指针,所以输出的是 0x100004
Ⅲ
int main(void) { int a[4] = { 1, 2, 3, 4 }; int *ptr1 = (int *)(&a + 1); int *ptr2 = (int *)((int)a + 1); printf( "%x,%x", ptr1[-1], *ptr2); return 0; }
|
ptr1[-1]
: 前面我们说过,这个操作相当于 *(ptr1 - 1)
(int)a + 1
是将 a 先强转为 int 然后再加 1,所以 a 仅仅增加了 1 个字节

Ⅳ
#include <stdio.h> int main(void) { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int *p; p = a[0]; printf( "%d", p[0]); return 0; }
|
p[0] -> a[0] [0] ,所以输出的是 0 吗?
并不是,注意看 a[3] [2]大括号内的内容,里面是圆括号而不是大括号,这是逗号表达式。
所以,a[0] [0] == 1
Ⅴ
int main(void){ int a[5][5]; int(*p)[4]; p = a; printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
|
指针(同类型)相减的意义是两个指针之间间隔的元素个数
&p[4][2]
-> 数组中的第 19 个元素(4 * 4 + 3)
&a[4][2]
-> 数组中的第 23 个元素 (4 * 5 + 3)
答案:FFFFFFFC,-4
Ⅵ
int main(void) { 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)); return 0; }
|
&aa
的类型是 int(*)[2][5]
,所以 &aa + 1
指向的是整个数组后面的内存 。所以 *(ptr1 - 1)
的值是 10
aa
aa + 1 让 aa 隐式转换为 int(*)[5]
,所以 aa + 1
指向的是元素 6 所在的地址。所以 *(ptr2 - 1)
的值是 5
Ⅶ
#include <stdio.h> int main(void) { char *a[] = {"work","at","alibaba"}; char**pa = a; pa++; printf("%s\n", *pa); return 0; }
|

Ⅷ
int main(void) { char *c[] = {"ENTER","NEW","POINT","FIRST"}; char** cp[] = {c+3,c+2,c+1,c}; char***cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *--*++cpp+3); printf("%s\n", *cpp[-2]+3); printf("%s\n", cpp[-1][-1]+1); return 0; }
|

单目运算符从右向左依次运算。
char* p = "ENTER"; printf("%s", p + 3);
|