文章介绍:
大多数人学习【C语言】指针时多多少少会出现一些比较复杂的指针类型,大多数人一遇到复杂的指针就可能头痛,所以这一章我们探讨指针的经典笔试题,让大家更加深刻了解指针,从而也拿下【C语言】几道经典指针笔试题!
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表示首元素的地址。
- 地址是4/8个字节,在32位(x86)平台下是4个字节,在64位(x64)平台下是8个字节
- sizeof计算的是占用内存空间的大小,单位是字节,不关注内存中到底存放的是什么。
- sizeof不是函数,是操作符
- strlen是函数
- strlen是针对字符串的,求的是字符串的长度本质上统计的是\0之前出现的字符个数。
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));
解析:int a[ ] = {1,2,3,4}
printf(“%d\n”,sizeof(a));
数组名代表首元素地址,但数组名单独放在sizeof内部,计算的是整个数组的大小单位是字节,答案是-> 16printf(“%d\n”,sizeof(a+0));
数组名代表首元素地址,首元素地址+0 还是首元素的地址, 地址就是4/8个字节,答案是->4 / 8printf(“%d\n”,sizeof(*a));
数组名代表首元素地址,首元素地址解引用就是数组a[0]第一个元素1,1是int类型占4个字节,答案是->4
(*a == *&a[0] == a[0])printf(“%d\n”,sizeof(a+1));
数组名代表首元素,首元素地址+1就是数组第二个元素地址,地址就是4/8个字节,答案是->4 / 8printf(“%d\n”,sizeof(a[1]));
这个比较简单,就是a数组第二个元素 2,2是int类型占4个字节,答案是->4printf(“%d\n”,sizeof(&a));
&数组名,取出的是整个数组的地址,但是数组的地址也是地址呀。地址就是4/8个字节,答案是->4 / 8
(int(*)[4] = &a)printf(“%d\n”,sizeof(*&a));
&a取出a整个数组的地址,数组的地址解引用访问整个数组,sizeof计算的就是数组的大小单位是字节,答案是->16 (其实这里可以看作 * &相互抵消,剩下sizeof(a)同样也是16个字节)printf(“%d\n”,sizeof(&a+1));
&a取出a整个数组的地址,,&a+1虽然地址跳过整个数组,但还是地址,地址还是是4/8个字节,答案->4 / 8
(&a = int (*)[4] ) ( &a+1 跳过一个数组)printf(“%d\n”,sizeof(&a[0]));
这里取出数组第一个元素的地址a[0] 元素1的地址,地址就是4/8个字节,答案是->4 / 8printf(“%d\n”,sizeof(&a[0]+1));
&a[0]+1 是第二个元素的地址,地址就是4/8个字节,答案是->4 / 8
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));
解析:char arr[] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘f’};
printf(“%d\n”, sizeof(arr));
数组名单独放在sizeof内部,计算的是整个数组的大小单位是字节,答案是->6printf(“%d\n”, sizeof(arr+0));
首元素地址加0还是首元素地址,地址就是4/8个字节,答案是->4 / 8printf(“%d\n”, sizeof(*arr));
数组名代表首元素地址,首元素地址解引用就是数组第一个元素arr[0]->a,答案是->1printf(“%d\n”, sizeof(arr[1]));
arr[1]就是第二个元素->b 答案是->1printf(“%d\n”, sizeof(&arr));
&arr取出整个数组的地址,但还是地址,地址还是是4/8个字节,答案是->4/8个字节printf(“%d\n”, sizeof(&arr+1));
&arr取出整个数组的地址,+1跳过一个数组的地址,地址 4/8 个字节,答案是->4/8个字节printf(“%d\n”, sizeof(&arr[0]+1));
&arr[0] 取出第一个元素的地址,+1跳过一个元素地址arr[1]的地址,地址 4/8个字节,答案是->4/8个字节
char arr[] = {'a','b','c','d','e','f'};
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[] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘f’};
注意审题哦! 这里是strlen了printf(“%d\n”, strlen(arr));
strlen遇到 \0 才会停止,但arr数组并没有存放\0进去,因为不知道\0的位置,所以是随机值 答案->随机值printf(“%d\n”, strlen(arr+0));
arr+0,从第一个元素地址开始找 \0 ,不知道\0的位置,所以也是随机值 答案->随机值printf(“%d\n”, strlen(*arr));
strlen函数的参数应该是指针类型的不能接引用 ,只能计算指针或者地址,所以我们解引用 *arr相当于把字符’a’ASCll当成地址了,这个时候程序会崩溃的,代码会报错的 答案是->(代码错误error)
printf(“%d\n”, strlen(arr[1]));
该题和上题一样,arr[1]把字符’b’ASCll当成地址了,解引用程序会崩溃的,代码会报错的 答案是->(代码错误error)printf(“%d\n”, strlen(&arr));
&arr,从第一个元素地址开始找 \0, ,因为不知道\0的位置,所以是随机值 答案->随机值
printf(“%d\n”, strlen(&arr+1));
&arr是指向第一个元素地址位置,&arr+1跳过一个arr数组大小,指向了f后面那个元素,因为也不知道\0的位置,所以是随机值 答案->随机值.(其实也可以说是随机值-6,因为arr+1已结跳过了6个元素)
printf(“%d\n”, strlen(&arr[0]+1));
&arr[0]第一个·元素地址,第二个元素地址,也不知道\0的位置 答案->随机值. (也可以说是随机值-1,因为&arr【0】+1已结跳过了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));
解析:char arr[] = “abcdef”;
注意!现在arr是一个字符串,后面默认放着一个\0的相当于是[ abcdef\0 ]printf(“%d\n”, sizeof(arr));
sizeof计算的是占用内存空间的大小,单位是字节,不关注内存中到底存放的是什么,所以也会把\0算上 答案是->7个字节printf(“%d\n”, sizeof(arr+0));
首元素地址加0号是首元素地址,地址大小是4/8个字节 答案是->4/8printf(“%d\n”, sizeof(*arr));
数组名代码首元素地址,首元素地址解引用a[0]->a, 答案是->1printf(“%d\n”, sizeof(arr[1]));
arr[1]数组第二个元素b 答案是->1printf(“%d\n”, sizeof(&arr));
&arr取出整个数组的地址,但还是地址,地址大小是4/8个字节,答案是-> 4 / 8printf(“%d\n”, sizeof(&arr+1));
&arr+1是跳过整个数组后的地址,地址大小是4/8个字节,答案是-> 4/8printf(“%d\n”, sizeof(&arr[0]+1));
&arr[e]+1 第二个元素的地址,地址大小是4/8个字节,答案是-> 4/8
char arr[] = "abcdef";
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”;
注意!现在arr是一个字符串,后面默认放着一个\0的相当于是[ abcdef\0 ]printf(“%d\n”, strlen(arr));
strlen计算 \0之前出现的字符个数,答案是->6printf(“%d\n”, strlen(arr + 0));
arr+0 还是首元素地址,首元素地址往后面找\0,答案是->6printf(“%d\n”, strlen(*arr));
strlen函数的参数只能计算指针或者地址,如果解引用 * arr相当于把字符’a’ASCll当成地址了,这个时候程序会崩溃的,代码会报错的 答案是->(代码错误error)printf(“%d\n”, strlen(arr[1]));
同上题道理一样, * arr【1】把字符’b’ASCll当成地址了,程序会崩溃的,代码会报错的 答案是->(代码错误error)printf(“%d\n”, strlen(&arr));
&arr指向第一个元素地址先,向后找\0 答案->6
(&arr = char (*)[7] 数组指针)printf(“%d\n”, strlen(&arr + 1));
&arr一开始指向第一个元素地址,&arr+1跳过arr整个数组大小,等于指向了/0后面的位置
指向了\0后,因为不知道\0的位置,所以是随机值 答案->随机值
printf(“%d\n”, strlen(&arr[0] + 1));
&arr[0] +1 是第二个元素地址,从第二个元素地址后开始找\0,答案是->5
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));
解析: char* p = “abcdef”;
内存部图大概是这样的
printf(“%d\n”, sizeof ( p ));
p是一个指针变量,sizeof(p)计算的是指针变量的大小,该指针变量存的是地址,地址大小4/8个字节,答案是->4/8个字节.printf(“%d\n”, sizeof(p + 1));
p存放的是首元素a的地址,a的地址+1,char*指针+1跳过一个字符,其实是b的地址,地址是4/8个字节,答案是->4/8个字节.printf(“%d\n”, sizeof(*p));
p存放的是首元素地址,解引用拿到的就是首元素a,答案是->1printf(“%d\n”, sizeof(p[0]));
同样拿到的也是首元素a,答案是->1printf(“%d\n”, sizeof(&p));
&p拿到的是p空间的地址,跟abcdef那一块地址没有关系!地址4/8个字节,答案是4 / 8
(p–char*) (&p --char**)
(写成代码是这样的char* p; , char**pp = &p)
(&p其实是一个二级指针指向了那一块p空间,因为那一块是char*一级的)
printf(“%d\n”, sizeof(&p + 1));
&p+1跳过p的地址,终究还是个地址,地址4/8个字节,答案是->4/8printf(“%d\n”, sizeof(&p[0] + 1));
&p[0]取出第一个元素地址+1,跳过一个字符,等于b的地址,地址4/8个字节,答案是->4 / 8
char* p = "abcdef";
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”, strlen( p));
p代表首元素a的地址,strlen遇到 \0 才会停止,答案是-> 6printf(“%d\n”, strlen(p + 1));
p+1是’b’的地址 ,strlen从b的位置向后找 ’ \0 ’ 答案是->5printf(“%d\n”, strlen(*p));
strlen函数的参数只能计算指针或者地址,如果解引用 * p相当于把字符’a’ASCll当成地址了,造成非法访问,这个时候程序会崩溃的,代码会报错的 答案是->(代码错误error)printf(“%d\n”, strlen(p[0]))
跟上面题一样 p[0]等于把第一个元素解引用,把ASCll当成地址了,造成非法访问,程序会崩溃的,代码会报错 答案是->(代码错误error)printf(“%d\n”, strlen(&p));
&p是拿到了p那一块空间的地址,并不是abcdef那一块空间,p那一块空间 不知道\0的位置,所以也是随机值 答案->随机值printf(“%d\n”, strlen(&p + 1));
&p + 1把整个p空间那块数组跳过去了,跳过p一块空间,更不能预测\0的位置,所以也是随机值 答案->随机值
(&p 和 &p+1 没有任何关系,因为:可能p那块空间里面有\0,&p+1跳过p那一块数组,可能半天都找不到\0)
printf(“%d\n”, strlen(&p[0] + 1));
&p[0]+1是’b’的地址 从b的地址往后找\0 答案是->5
int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
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+1));
printf("%d\n", sizeof(a[3]));
解析:int a[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
printf(“%d\n”, sizeof(a));
数组名单独发放在sizeof内部,计算的是整个数组的大小。答案是->48printf(“%d\n”, sizeof(a[0][0]));
a[0][0]是第一行第一个元素,答案是->4printf(“%d\n”, sizeof(a[0]));
第一行的数组名,这时数组名单独放在sizeof内部了,计算的是数组的大小,单位是字节,答案是->16printf(“%d\n”, sizeof(a[0] + 1));
a[0]作为数组并没有单独放在sizeof内部,也没取地址,所以此时的a[0]就是第一行第一个元素的地址 - &a[0][0] , a[0] + 1 是第一行第2个元素的地址 &a[0][1] . 地址大小是4/8个字节 , 答案是->4 / 8printf(“%d\n”, sizeof(*(a[0] + 1)));
此题跟上题一样,只是解引用了 *(&a[0][1]),第一行第二个元素,大小是4个字节 , 答案-> 4printf(“%d\n”, sizeof(a + 1));
a作为二维数组的数组名并非单独放在sizeof内部,所以表示的是首元素地址,二维数组首元素地址即是第一行的地址->( int (*)[4])-> &(a[0]),a+1是跳过第一行,指向了第二行地址->(&a[1]) , 地址大小是 4/8,单位是字节, 答案是->4/8printf(“%d\n”,sizeof(*(a+1)));
此题跟上题类似,只是把第二行地址解引用了 (a[1]) ,计算第二行的大小,单位是字节,答案是->16printf(“%d\n”, sizeof(&a[0] + 1));
&a[0]是第一行的地址 , &a[0]+1跳过第一行,既是第二行的地址。地址大小是 4/8,单位是字节, 答案是->4/8printf(“%d\n”, sizeof(*(&a[0] + 1)));
a[0]+1,既是第二行数组 a[1],计算第二行的大小,单位是字节,答案是->16printf(“%d\n”, sizeof(*a));
a是首元素地址-第一行的地址,*a就是第一行,sizeof(*a)就是计算第一行的大小,答案是->16printf(“%d\n”, sizeof(*a+1));
a没有单独放在sizeof()内部,既表示的是首元素的地址,二维数组首元素地址即是第一行的地址->&a[0], 解引用即是第一行a[0], 但第一行a[0]加1了,他又不是单独放在sizeof()内部了,这时a[0]又变成首元素地址了->&a[0][0]的地址,在+1变成第一行第二个元素地址->&a[0][1],地址是4/8个字节,答案->4/8printf(“%d\n”, sizeof(a[3]));
可能大多数人跟我一样,哎刚开始看见a[3]不是造成越界访问了吗? 但答案并非如此!!
sizeof里面的表达式不参与真正的运算,只是根据他的类型属性计算他的大小,他不会真正访问a[3]这行 ,a[3]为第四行所有元素,大小为16,所以答案是->16
(sizeof只会根据他的类型来计算)
如果不是单独放在sizeof()内部,一般的操作都会使指针降一阶