当前位置: 首页 > article >正文

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

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


http://www.kler.cn/a/2773.html

相关文章:

  • 大厂的 404 页面都长啥样?看看你都见过吗~~~
  • OpenGL ES 共享上下文实现多线程渲染
  • HarmonyOS SDK下的实践与探索
  • 94个属于一区且接受医工交叉领域投稿的期刊汇总|个人观点·24-11-13
  • 【pytorch】常用强化学习算法实现(持续更新)
  • 并发基础:(淘宝笔试题)三个线程分别打印 A,B,C,要求这三个线程一起运行,打印 n 次,输出形如“ABCABCABC....”的字符串【举一反三】
  • 第四章 保护模式入门
  • XCIE-HUAWEI-超级完整的BGP-6-路由选路(三)+团体属性+BGP选路总结
  • Python流星雨代码
  • 算法设计与分析 实验五 贪心算法
  • Apache Kafka JNDI注入(CVE-2023-25194)漏洞复现浅析
  • JS数组reduce()方法详解及高级技巧
  • typedef uint8_t u8;(stm32数据类型)
  • springboot+mybatis plus+vue+elementui+axios 表格分页查询demo
  • # 机械设备故障的靶向迁移诊断与OpenAI分析
  • 把星巴克吓出冷汗,6580家门店扭亏为盈
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • 代码误写到master分支(或其他分支),此时代码还未提交,如何转移到新建分支?
  • 【机器学习】综述:机器学习中的模型评价、模型选择与算法选择
  • 微信小程序可以进行dom操作吗?
  • 蓝桥杯Web前端练习-----渐变色背景生成器
  • 读《Multi-level Wavelet-CNN for Image Restoration》
  • 冲击蓝桥杯-时间问题(必考)
  • 【Linux】安装DHCP服务器
  • 国产芯片方案——红外测温体温计方案
  • Linux内核Socket通信原理和实例讲解