进阶C语言:指针和数组训练题
在开始之前首先要熟知下面几点:
1.数组名和&数组名
2.字符指针类型的字符串如何存储
3.sizeof操作符的用法
4.strlen函数的用法
1.一维数组
//代码输出结果
#include <stdio.h>
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //1
printf("%d\n", sizeof(a + 0)); //2
printf("%d\n", sizeof(*a)); //3
printf("%d\n", sizeof(a + 1)); //4
printf("%d\n", sizeof(a[1])); //5
printf("%d\n", sizeof(&a)); //6
printf("%d\n", sizeof(*&a)); //7
printf("%d\n", sizeof(&a + 1)); //8
printf("%d\n", sizeof(&a[0])); //9
printf("%d\n", sizeof(&a[0] + 1)); //10
return 0;
}
解析:
1. printf("%d\n", sizeof(a));
a是数组名,数组名除了&数组名和sizeof(数组名)都表示数组首元素的地址,所以sizeof(a)表示的是整个数组的大小,a是一个整形数组,有四个元素,所以1代码的结果为 16
2. printf("%d\n", sizeof(a + 0));
a是数组名,表示数组首元素地址,a是一个整形数组,加0表示地址跳过0个整形,因此地址没变,还是首元素的地址,地址就是指针,指针的大小是4个或8个字节,所以2代码的结果为 4/8
3.printf("%d\n", sizeof(*a));
a是数组名,*a表示的是解引用数组首元素地址从而得到首元素,因此sizeof(*a)计算的是首元素的大小,所以3代码的结果是 4
4.printf("%d\n", sizeof(a + 1));
a数组名,a+1表示数组首元素的地址加1,跳过了1整形大小,从而得到了数组第二个元素的地址,所以计算的也是指针的大小,所以4代码的结果是 4/8
5.printf("%d\n", sizeof(a[1]));
a[1]采用的下标引用的操作,得到的是数组下标为1的元素,所以计算的是数组中某一个元素的大小,所以5代码的结果是 4
6. printf("%d\n", sizeof(&a));
a是数组名,&数组名得到的是整个数组的地址,所以&a表示a数组的地址,因此计算的是地址的大小,所以6代码的结果是 4/8
7. printf("%d\n", sizeof(*&a));
&a拿到数组的地址,然后解引用得到的是整个数组,因此计算的是整个数组的大小,所以7代码的结果是 16
8.printf("%d\n", sizeof(&a + 1));
&a拿数组的地址,然后加1,跳过一个数组,但是还是地址,也就是计算的是指针的大小,所以代码8的结果是 4/8
9. printf("%d\n", sizeof(&a[0]));
a[0]表示下标为0的元素,然后对其进行取地址操作,取出了数组下标为0的元素的地址,所以计算的是指针的大小,因此9代码的结果是 4/8
10.printf("%d\n", sizeof(&a[0] + 1));
&a[0]表示的是a数组中下标为0的元素的地址,然后对其加1,表示跳过一个整形大小,得到的是下表为1元素的地址,计算的也是指针的大小,所以10代码的结果是 4/8
2.字符数组
//代码结果:
#include <stdio.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //1
printf("%d\n", sizeof(arr + 0)); //2
printf("%d\n", sizeof(*arr)); //3
printf("%d\n", sizeof(arr[1])); //4
printf("%d\n", sizeof(&arr)); //5
printf("%d\n", sizeof(&arr + 1)); //6
printf("%d\n", sizeof(&arr[0] + 1)); //7
return 0;
}
解析:
1. printf("%d\n", sizeof(arr));
arr是一个char类型的数组,sizeof(arr)表示的是arr数组的整体大小,所以1代码的结果是 6
2.printf("%d\n", sizeof(arr + 0));
arr是数组名,数组名是数组首元素的地址,所以arr在这里指向的是字符‘a’的地址,加0表示跳过0个字符,所以还是指向的是字符‘a’,因此计算的就是地址的大小,也就是指针的大小,所以2代码的结果是 4/8
3. printf("%d\n", sizeof(*arr));
arr在这里指向数组的第一个元素,对其进行解引用得到的是第一个元素,因此计算的是数组第一个元素的大小,所以3代码的结果是 1
4.printf("%d\n", sizeof(arr[1]));
arr[1]在这里使用了下标的引用,表示的是arr数组中下标为1的元素,因此计算的是arr数组中下标为1的元素的大小,所以4代码的结果是 1
5.printf("%d\n", sizeof(&arr));
&数组名表示取出整个数组的地址,那既然是地址,所以计算的就是指针的大小,所以5代码的结果是 4/8
6.printf("%d\n", sizeof(&arr + 1));
&arr取出整个数组的地址,然后加1,地址跳过了整个数组,那还是地址,所以计算的也是指针的大小,因此6代码的结果是 4/8
7. printf("%d\n", sizeof(&arr[0] + 1));
&arr[0],表示取出arr数组中的下标为0的元素的地址,然后跳过一个字符的地址,所以计算的也是地址值的大小,所以7代码的结果是 4/8
//代码结果
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr)); //1
printf("%d\n", strlen(arr + 0)); //2
printf("%d\n", strlen(*arr)); //3
printf("%d\n", strlen(arr[1])); //4
printf("%d\n", strlen(&arr)); //5
printf("%d\n", strlen(&arr + 1)); //6
printf("%d\n", strlen(&arr[0] + 1)); //7
return 0;
}
解析:
strlen函数是计算字符串的长度的,它统计的是‘\0’之前的字符的个数,不包括‘\0’
size_t strlen(const char* string);它的参数是一个指针
1. printf("%d\n", strlen(arr));
arr是一个字符数组,在这个数组中有6个元素,而没有‘\0’,而strlen要找‘\0’,但是没有‘\0’就会一直往后面寻找‘\0’,直到找到‘\0’,因此1代码的结果是一个 随机值
2.printf("%d\n", strlen(arr + 0));
arr表示首元素的地址,加0表示跳过0个字符,所以还是指向首元素,然后从后开始找‘\0’,所以2代码的结果还是 随机值
3. printf("%d\n", strlen(*arr));
strlen函数的参数是一个const char* 的指针,而这个代码传递的是一个值,所以 程序错误
4. printf("%d\n", strlen(arr[1]));
这个参数是数组中下标为1的元素,而不是地址,所以 程序错误
5.printf("%d\n", strlen(&arr));
&arr和arr的地址是一样的,这个在进阶C里面讲到过,所以5代码的结果还是一个 随机值
6. printf("%d\n", strlen(&arr + 1));
&arr指向的是首元素,但是+1之后就跳过了整个数组,所以strlen计算的是跳过整个数组之后的地址然后再往后面找‘\0’,所以6代码的结果是一个 随机值, 但是这个随机值相比1代码的随机值就小6
7. printf("%d\n", strlen(&arr[0] + 1));
&arr[0]取出数组中下标为0的元素的地址,然后加1,地址跳过了1个元素,然后strlen计算的是跳过一个元素之后的地址再往后面找‘\0’,所以7代码的结果也是一个 随机值,但是这个随机值相比于1代码的随机值小1
//代码结果
#include <stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //1
printf("%d\n", sizeof(arr + 0)); //2
printf("%d\n", sizeof(*arr)); //3
printf("%d\n", sizeof(arr[1])); //4
printf("%d\n", sizeof(&arr)); //5
printf("%d\n", sizeof(&arr + 1)); //6
printf("%d\n", sizeof(&arr[0] + 1)); //7
return 0;
}
解析:
arr是一个char类型的数组,里面存放的是一个字符串,字符串的后面自带一个‘\0’
sizeof计算的是所占内存空间的大小,不管内存里面是什么,包括‘\0’
1.printf("%d\n", sizeof(arr));
数组名单独放在sizeof中计算的是整个数组的大小,因此1代码的结果是 7(包含\0)
2.printf("%d\n", sizeof(arr + 0));
arr在这里表示的是数组首元素的地址,+0表示跳过0个单位,所以指向的还是首元素的地址,因此是地址就是指针,所以2代码的结果是 4/8
3. printf("%d\n", sizeof(*arr));
arr没有单独在sizeof内部,所以表示数组的首元素的地址,然后对其解引用,找到的是数组的首元素,因此3代码的结果是 1
4.printf("%d\n", sizeof(arr[1]));
arr[1]表示数组下标为1的元素,所以4代码的结果是 1
5.printf("%d\n", sizeof(&arr));
&arr取出的是整个数组的地址,所以计算的是指针的大小,所以5代码的结果是 4/8
6.printf("%d\n", sizeof(&arr + 1));
&arr取出整个数组的地址,然后加1,地址跳过整个数组,指向新的位置,所以计算的是指针的大小,指针的大小是4/8个字节,所以6代码的结果是 4/8
7.printf("%d\n", sizeof(&arr[0] + 1));
&arr[0]取出数组中下标为0的元素的地址,然后+1,地址跳过一个元素,指向新的地址,所以计算的也是指针的大小,所以7代码的结果是 4/8
//代码结果
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); //1
printf("%d\n", strlen(arr + 0)); //2
printf("%d\n", strlen(*arr)); //3
printf("%d\n", strlen(arr[1])); //4
printf("%d\n", strlen(&arr)); //5
printf("%d\n", strlen(&arr + 1)); //6
printf("%d\n", strlen(&arr[0] + 1)); //7
return 0;
}
解析:
1.printf("%d\n", strlen(arr));
strlen计算的时候不包括‘\0’,所以1代码的结果是 6
2.printf("%d\n", strlen(arr + 0));
这里的arr表示的数组的首元素的地址,+0表示跳过0个元素,所以还是指向的是首元素的地址,然后从首元素的位置开始先后面找,统计‘\0’之前的元素个数。所以2代码的结果是 6
3. printf("%d\n", strlen(*arr));
arr表示首元素的地址,然后解引用拿到首元素,传递给strlen函数,但是strlen函数需要指针类型的参数,所以 程序错误
4. printf("%d\n", strlen(arr[1]));
arr[1]找到的是数组中下标为1的元素,所以类型也不是指针类型,所以 程序错误
5. printf("%d\n", strlen(&arr));
&arr表示的是取出整个数组的地址,但是它的指向还是指向的是数组的首元素的地址,因此从首元素的位置开始进行计算,所以5代码的结果是 6
6.printf("%d\n", strlen(&arr + 1));
&arr取出整个数组的地址,然后+1,地址跳过整个数组,指向的是‘\0’后面的位置,所以要从‘\0’后面的位置开始找‘\0’,所以6代码的结果是一个 随机值
7.printf("%d\n", strlen(&arr[0] + 1));
&arr[0]取出的是数组下标为0的元素的地址,然后+1,地址跳过1个字节,指向了第二个元素的地址,然后往后面找‘\0’,所以7代码的结果是 5
//代码运行结果
#include <stdio.h>
#include <string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p)); //1
printf("%d\n", sizeof(p + 1)); //2
printf("%d\n", sizeof(*p)); //3
printf("%d\n", sizeof(p[0])); //4
printf("%d\n", sizeof(&p)); //5
printf("%d\n", sizeof(&p + 1)); //6
printf("%d\n", sizeof(&p[0] + 1)); //7
return 0;
}
解析:
p是一个char类型的指针,p里面存放的是地址,因此p在存储字符串的时候存储的是首字符的地址,也就是把‘a’的地址存放在了char类型的p里面
1. printf("%d\n", sizeof(p));
p是一个指针变量,计算的是指针变量的大小,所以1代码的结果是 4/8
2. printf("%d\n", sizeof(p + 1));
p是一个指针变量,p指向的是首字符的地址,p+1表示跳过一个字符,然后指向了‘b’,所以得到的是‘b’的地址,所以还是一个指针,因此计算的是指针的大小,所以2代码的结果是 4/8
3.printf("%d\n", sizeof(*p));
*p表示对p进行解引用,p里面存放的是首字符的地址,对其解引用操作得到的是首字符,首字符的大小就为1,所以3代码的结果是 1
4.printf("%d\n", sizeof(p[0]));
p[0]可以写成指针的形式:*(p + 0),表示的就是p跳过0个字符,然后解引用,所以得到的是字符a,所以4代码的结果是 1
5. printf("%d\n", sizeof(&p));
p本身是一个指针类型的变量,再&p取出p的地址,也就是计算二级指针的大小,二级指针的大小也是指针,所以5代码的结果是 4/8
6.printf("%d\n", sizeof(&p + 1));
&p是一个二级指针,然后+1,跳过一个p变量的地址,所以还是计算指针的大小,所以6代码的结果是 4/8
7.printf("%d\n", sizeof(&p[0] + 1));
如果把字符串看成一个数组,那么&p[0]就是取出了字符数组中下标为0的元素的地址,然后+1跳过一个字符,得到的就是‘b’的地址,所以7代码的结果是 4/8
//代码运行结果
#include <stdio.h>
#include <string.h>
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p)); //1
printf("%d\n", strlen(p + 1)); //2
printf("%d\n", strlen(*p)); //3
printf("%d\n", strlen(p[0])); //4
printf("%d\n", strlen(&p)); //5
printf("%d\n", strlen(&p + 1)); //6
printf("%d\n", strlen(&p[0] + 1)); //7
return 0;
}
解析:
1.printf("%d\n", strlen(p));
p存放的是a的地址,所以strlen函数求长度时会从a地址开始往后面找\0,所以1代码的结果是 6
2.printf("%d\n", strlen(p + 1));
p指向的a的地址,p+1指向的就是b的地址,然后从后面找\0,所以2代码的结果是 5
3. printf("%d\n", strlen(*p));
p是地址,指向的是首字符a的地址,对p解引用,得到的是字符a变量,将a变量传递给strlen函数, 程序错误
4.printf("%d\n", strlen(p[0]));
p[0]可以写成->*(p+0),所以表示的是字符a变量,将变量传递给strlen函数也会导致 程序错误
5.printf("%d\n", strlen(&p));
&p是一个二级指针,指向的是p的地址,然后从p的地址开始往后面找\0,虽然p变量存放的a的地址,但是p变量的地址又是什么,不可预测,所以也不知道\0在哪里,因此5代码的结果是 随机值
6. printf("%d\n", strlen(&p + 1));
p的地址不可预测,那p+1的地址更不可预测,所以6代码的结果是 随机值
注:5代码和6代码的两个随机值没有任何关系,因为我们无法预测在p的地址里面都存放了哪些值
7.printf("%d\n", strlen(&p[0] + 1));
&p[0]取出的是a的地址,然后+1跳过一个字符,所以指向的是b的地址,因此strlen函数求长度是从b的地址开始向后面找\0,所以7代码的结果是 5
3.二维数组
//代码运行结果
#include <stdio.h>
#include <string.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //1
printf("%d\n", sizeof(a[0][0])); //2
printf("%d\n", sizeof(a[0])); //3
printf("%d\n", sizeof(a[0] + 1)); //4
printf("%d\n", sizeof(*(a[0] + 1))); //5
printf("%d\n", sizeof(a + 1)); //6
printf("%d\n", sizeof(*(a + 1))); //7
printf("%d\n", sizeof(&a[0] + 1)); //8
printf("%d\n", sizeof(*(&a[0] + 1))); //9
printf("%d\n", sizeof(*a)); //10
printf("%d\n", sizeof(a[3])); //11
return 0;
}
解析:
a是一个二维数组,二维数组的数组名表示的是二维数组第一行的地址
1.printf("%d\n", sizeof(a));
a数组名,表示数组第一行的地址,但是放在sizeof内部,所以计算的是整个数组的大小,因此1代码的结果是 48
2.printf("%d\n", sizeof(a[0][0]));
a[0][0]使用的是二维数组的下标访问,得到的是a数组中第一行第一列的元素,又因为a数组是一个整形数组,所以2代码的结果是 4
3.printf("%d\n", sizeof(a[0]));
a[0]表示的是第一行的数组名,单独放在sizeof内部,因此计算的就是第一行的总的元素的大小。所以3代码的结果是 16
4.printf("%d\n", sizeof(a[0] + 1));
a[0]没有单独放在sizeof内部,因此a[0]表示的是首元素的地址,也就相当于&[0][0],然后加1,得到的是第一行第二个元素的地址,所以4代码的结果是 4/8
5. printf("%d\n", sizeof(*(a[0] + 1)));
a[0]+1表示第一行第二个元素的地址,对其进行解引用得到的是第一行第二个元素,所以5代码的结果是 4
6.printf("%d\n", sizeof(a + 1));
a没有单独放在sizeof内部,所以它表示的是数组第一行的地址,然后+1表示跳过一行,指向了第二行,所以就得到了第二行的地址,因此6代码的结果是 4/8
7. printf("%d\n", sizeof(*(a + 1)));
a+1表示是的第二行的地址,然后对其解引用,得到的是第二行的元素,因此7代码的结果是 16
8.printf("%d\n", sizeof(&a[0] + 1));
&a[0]取出的是第一行的地址,然后加一,跳过第一行,地址指向了第二行,所以计算指针大小,所以8代码的结果是 4/8
9.printf("%d\n", sizeof(*(&a[0] + 1)));
&a[0]+1表示第二行的地址,然后解引用拿到第二行的元素,所以9代码的结果是 16
10.printf("%d\n", sizeof(*a));
a没有单独放在sizeof内部,所以表示的是第一行的地址,对第一行解引用,得到的是第一行的元素,所以10代码的结果是 16
11.printf("%d\n", sizeof(a[3]));
a[3]表示的是第四行的数组名,但是在a这个二维数组中没有第四行,那是不是越界访问了呢?答案并不是的,sizeof内部的表达式是不会真的去执行,因此并不会通过sizeof来间接的访问第四行的元素,但是如果有第四行,那它的大小就是16,所以11代码的结果是 16
总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。
本期分享到此结束,喜欢文章的可以留下一个小小的赞,感谢大家的支持!