进阶C语言:指针和数组训练题

简介: 进阶C对指针专门总结的练习题,你也快来试试吧!
在开始之前首先要熟知下面几点: 1.数组名和&数组名 2.字符指针类型的字符串如何存储 3.sizeof操作符的用法 4.strlen函数的用法

紫蓝色几何渐变科技互联网微信公众号封面 (1).gif

1.一维数组

//代码输出结果#include <stdio.h>intmain()
{
inta[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));       //1printf("%d\n", sizeof(a+0));   //2printf("%d\n", sizeof(*a));      //3printf("%d\n", sizeof(a+1));   //4printf("%d\n", sizeof(a[1]));    //5printf("%d\n", sizeof(&a));      //6printf("%d\n", sizeof(*&a));     //7printf("%d\n", sizeof(&a+1));  //8printf("%d\n", sizeof(&a[0]));   //9printf("%d\n", sizeof(&a[0] +1)); //10return0;
}

解析:

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>intmain()
{
chararr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));        //1printf("%d\n", sizeof(arr+0));    //2printf("%d\n", sizeof(*arr));       //3printf("%d\n", sizeof(arr[1]));     //4printf("%d\n", sizeof(&arr));       //5printf("%d\n", sizeof(&arr+1));   //6printf("%d\n", sizeof(&arr[0] +1)); //7return0;
}

解析:

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>intmain()
{
chararr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));        //1printf("%d\n", strlen(arr+0));    //2printf("%d\n", strlen(*arr));       //3printf("%d\n", strlen(arr[1]));     //4printf("%d\n", strlen(&arr));       //5printf("%d\n", strlen(&arr+1));   //6printf("%d\n", strlen(&arr[0] +1)); //7return0;
}

解析:

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>intmain()
{
chararr[] ="abcdef";
printf("%d\n", sizeof(arr));         //1printf("%d\n", sizeof(arr+0));     //2printf("%d\n", sizeof(*arr));        //3printf("%d\n", sizeof(arr[1]));      //4printf("%d\n", sizeof(&arr));         //5printf("%d\n", sizeof(&arr+1));    //6printf("%d\n", sizeof(&arr[0] +1)); //7return0;
}

解析:

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>intmain()
{
chararr[] ="abcdef";
printf("%d\n", strlen(arr));         //1printf("%d\n", strlen(arr+0));     //2printf("%d\n", strlen(*arr));        //3printf("%d\n", strlen(arr[1]));      //4printf("%d\n", strlen(&arr));        //5printf("%d\n", strlen(&arr+1));    //6printf("%d\n", strlen(&arr[0] +1)); //7return0;
}

解析:

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>intmain()
{
char*p="abcdef";
printf("%d\n", sizeof(p));       //1printf("%d\n", sizeof(p+1));  //2printf("%d\n", sizeof(*p));     //3printf("%d\n", sizeof(p[0]));   //4printf("%d\n", sizeof(&p));      //5printf("%d\n", sizeof(&p+1));  //6printf("%d\n", sizeof(&p[0] +1)); //7return0;
}

解析:

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>intmain()
{
char*p="abcdef";
printf("%d\n", strlen(p));         //1printf("%d\n", strlen(p+1));     //2printf("%d\n", strlen(*p));        //3printf("%d\n", strlen(p[0]));      //4printf("%d\n", strlen(&p));        //5printf("%d\n", strlen(&p+1));    //6printf("%d\n", strlen(&p[0] +1)); //7return0;
}

解析:

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>intmain()
{
inta[3][4] = { 0 };
printf("%d\n", sizeof(a));               //1printf("%d\n", sizeof(a[0][0]));         //2printf("%d\n", sizeof(a[0]));            //3printf("%d\n", sizeof(a[0] +1));        //4printf("%d\n", sizeof(*(a[0] +1)));     //5printf("%d\n", sizeof(a+1));           //6printf("%d\n", sizeof(*(a+1)));        //7printf("%d\n", sizeof(&a[0] +1));       //8printf("%d\n", sizeof(*(&a[0] +1)));    //9printf("%d\n", sizeof(*a));              //10printf("%d\n", sizeof(a[3]));            //11return0;
}

解析:

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. 除此之外所有的数组名都表示首元素的地址。

本期分享到此结束,喜欢文章的可以留下一个小小的赞,感谢大家的支持!

目录
相关文章
|
3天前
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
|
6天前
|
C语言
C语言指针带代码
C语言指针带代码
18 3
|
20小时前
|
存储 编译器 C语言
C语言进阶——位段
C语言进阶——位段
|
21小时前
|
算法 C语言
C语言——oj刷题——字符串左旋和轮转数组
C语言——oj刷题——字符串左旋和轮转数组
|
21小时前
|
C语言
C语言——oj刷题——调整数组使奇数全部都位于偶数前面
C语言——oj刷题——调整数组使奇数全部都位于偶数前面
|
21小时前
|
存储 C语言 索引
C语言数组:从入门到进阶
C语言数组:从入门到进阶
|
22小时前
|
存储 C语言
C语言数组指针和指针数组的区别及使用方法
C语言数组指针和指针数组的区别及使用方法
|
2天前
|
存储 编译器 数据库
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
【再识C进阶5(上)】详细介绍C语言文件操作——文件是用于存储数据
|
5天前
|
存储 搜索推荐 编译器
C语言数组深入解析与实战应用
C语言数组深入解析与实战应用
12 0
|
6天前
|
存储 C语言
深入理解C语言中的指针
深入理解C语言中的指针
14 0