C语言深入理解指针(4)--指针笔试题解析

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

C语言深入理解指针(4)

收录于专栏【C语言学习
本专栏旨在分享学习C语言学习的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

1. sizeof和strlen的对⽐

1.1 sizeof

1.2 strlen

1.3 sizeof 和strlen的对⽐

2. 数组和指针笔试题解析

2.1 ⼀维数组

2.2 字符数组

示例一:

示例二:

示例三:

示例四:

示例五:

实例六:

2.3 ⼆维数组

3. 指针运算笔试题解析

3.1 题⽬1:

3.2 题⽬2

3.3 题⽬3

3.4 题⽬4

3.5 题⽬5

3.6 题⽬6

3.7 题⽬7


1. sizeof和strlen的对⽐

1.1 sizeof

在学习操作符的时候,我们学习了 sizeofsizeof 计算变量所占内存内存空间⼤⼩的,单位是 字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。

sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。

比如:

#include <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof a);printf("%d\n", sizeof(int));return 0;
}

输出结果: 

1.2 strlen

strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:

size_t strlen ( const char * str );

 统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。

比如:


#include <stdio.h>
int main()
{char arr1[3] = { 'a', 'b', 'c' };char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr1));return 0;
}

输出结果:

注意35是随机的,当没有\0时,strlen 会⼀直向后找 \0 字符,直到找到为⽌,所以是不确定的

1.3 sizeof 和strlen的对⽐

sizeofstrlen

1. sizeof是操作符

2. sizeof计算操作数所占内存的⼤⼩, 单位是字节

3. 不关注内存中存放什么数据

1. strlen是库函数,使⽤需要包含头⽂件 string.h

2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数

3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可 能会越界

2. 数组和指针笔试题解析

2.1 ⼀维数组

int main()
{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));
}

输出结果:

解析如下:di

第一个:sizeof加数组名,在上一节说过,代表求整个数组的内存大小,一个整数为四个字节,四个整数即为16个字节

第二个:a+0代表的是首元素的地址,指针的大小只与平台有关,我这里是X64所以是8

第三个:对首元素的指针进行解引用,即求1的内存,所以为4个字节

第四个:代表的是首元素后面一个元素的地址大小,所以为8

第五个:求第二个元素的内存,所以为4

第六个:返回的是整个数组的地址,地址的内存只与平台有关,所以为8

第七个:对整个数组进行解引用,所以为16

第八个:求得是数组地址下一个地址,是地址内存就是固定得,为8

第九个:求得是地址,固定为8

第十个:也是一样,求的是地址,为8

2.2 字符数组

示例一:

int main()
{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));
}

输出结果:

解析如下:

第一个:sizeof加数组名,在上一节说过,代表求整个数组的内存大小,一个字符为1个字节,6个字符即为6个字节

第二个:a+0代表的是首元素的地址,指针的大小只与平台有关,我这里是X64所以是8

第三个:对首元素的指针进行解引用,即求1的内存,所以为1个字节

第四个:求第二个元素的内存,所以为1

第五个:返回的是整个数组的地址,地址的内存只与平台有关,所以为8

第六个:求得是数组地址下一个地址,是地址内存就是固定得,为8

第七个:求得是数组地址下一个地址,是地址内存就是固定得,为8

示例二:

int main()
{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));
}

  strlen 统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。

所以我们使用 strlen 时一定要注意这个问题!

示例三:

int main()
{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));
}

输出结果:

 调式结果:

注意:这样得初始化会自动补上"\0" 

 解析如下:

第一个:sizeof加数组名,在上一节说过,代表求整个数组的内存大小,一个字符为1个字节,7个字符即为7个字节(注意"\0"也算)

第二个:a+0代表的是首元素的地址,指针的大小只与平台有关,我这里是X64所以是8

第三个:对首元素的指针进行解引用,即求1的内存,所以为1个字节

第四个:求第二个元素的内存,所以为1

第五个:返回的是整个数组的地址,地址的内存只与平台有关,所以为8

第六个:求得是数组地址下一个地址,是地址内存就是固定得,为8

第七个:求得是数组地址下一个地址,是地址内存就是固定得,为8

示例四:

int main()
{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));
}

输出结果:

解析如下:

第一个:strlen加数组名,数组名就是首元素得地址,所以strlen会一直找,直到找到\0,所以为6

第二个:strlen接收得也是首元素得地址,所以也为6

第三个和第四个:传入得不是地址,而是一个值,所以打印不出结果

第五个:返回的是整个数组的地址,返会得其实也是地址首元素得地址,所以也为6

第六个:求得是数组地址下一个地址,内容不确定,随机

第七个:求得是数组地址首元素得下一个地址,所以为5

示例五:

int main()
{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));
}

输出结果:

解析如下:

第一个:sizeof求得时指针p得大小,所以为8

第二个:求得是字符指针p下一个地址,是地址内存就是固定得,为8

第三个:对字符指针p进行解引用,即求a的内存,所以为1个字节

第四个:求第一个元素的内存,所以为1

第五个:对指针p进行取地址,地址内存就是固定得,为8

第六个:求得是对指针p进行取地址得下一个地址,是地址内存就是固定得,为8

第七个:求得是字符数组首元素地址下一个地址,是地址内存就是固定得,为8

实例六:

int main()
{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));
}

输出结果:

 

第一个:strlen加字符数组指针名,字符数组指针名就是首元素得地址,所以strlen会一直找,直到找到\0,所以为6

第二个:strlen接收得也是首元素下一个地址,所以为5

第三个和第四个:传入得不是地址,而是一个值,所以打印不出结果

第五个:传入p得地址,strlen会一直找,直到找到\0,所以为6

第六个:传入p地址的下一个,随机不确定

第七个:传入p首元素的下一个地址,所以为5

2.3 ⼆维数组

int main()
{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])); 

sizeof加数组名求得是整个数组得内存,而这里求得是第一行所有元素得内存,即为16
    printf("%d\n", sizeof(a[0] + 1));

这里应该a[0] + 1先算所以,他这里返回得是地址,答案为8

数组名的意义:

1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。

2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表⽰⾸元素的地址。

3. 指针运算笔试题解析

3.1 题⽬1:

#include <stdio.h>
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;
}
//程序的结果是什么?

程序的结果是什么?

这里得ptr指向的是整个数组地址的下一个,应为&a取得是整个数组的地址

所以它打印的结果应该为2,5

*(a + 1):数组名代表首元素的地址,再加一,即取得是第二个元素得地址,在进行解引用:

*(ptr - 1) :

输出结果:

3.2 题⽬2

 在X86环境下

假设结构体的⼤⼩是20个字节

程序输出的结构是啥?

struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}
  • p + 0x1:这是一个指针运算,它将 p 指针向后移动了一个结构体的大小(20 字节),这里注意: 0x100014 是16进制。因此,它将指向 0x100014 地址。
  • (unsigned long)p + 0x1:这是一个类型转换,将指针 p 转换为 unsigned long 类型,然后再加上 0x1。这将得到 0x100001
  • (unsigned int*)p + 0x1:这也是一个类型转换,将指针 p 转换为 unsigned int* 类型,然后再加上 0x1。这将得到 0x100004

输出结果:

3.3 题⽬3

#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int* p;p = a[0];printf("%d", p[0]);return 0;
}
  1. 首先,我们定义了一个二维整数数组 a,其中包含了三个子数组,每个子数组有两个整数元素。注意,这里的初始化方式有点特殊,使用了逗号运算符,但实际上只有最后一个值被赋给了数组元素。因此,a 的内容实际上是 {1, 3, 5}
  2. 然后,我们创建了一个指向整数的指针 p,并将其指向了 a[0],即第一个子数组的首地址。
  3. 接下来,我们打印了 p[0] 的值。由于 p 指向了 a[0],所以 p[0] 实际上是 a[0][0],即第一个子数组的第一个元素,也就是 1

输出结果:

3.4 题⽬4

 //假设环境是x86环境,程序输出的结果是啥?#include <stdio.h>
int main()
{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;
}

解析:

这里注意a和p的类型是不一样的

a的类型为int(*)[5]

p的类型为int(*)[4]

当返回%d时,就是返回了两个地址相差的距离,也是就-4

而用地址打印时,就是返回-4的补码,用16进制表示 

3.5 题⽬5
 

#include <stdio.h>int main(){char *a[] = {"work","at","alibaba"};char**pa = a;pa++;printf("%s\n", *pa);return 0;}
  1. 首先,我们定义了一个字符型指针数组 a[],其中包含三个字符串元素:“work”、“at” 和 “alibaba”。
  2. 然后,我们定义了一个指向字符型指针的指针 pa,并将其初始化为 a 数组的首元素地址,即 pa = a;
  3. 接下来,我们执行了 pa++ 操作,将 pa 指向了数组 a 的第二个元素,即指向了字符串 “at” 的首元素地址。
  4. 最后,我们使用 printf("%s\n", *pa); 打印了 *pa 所指向的字符串,即输出了 “at”。

输出结果

3.6 题⽬6

#include <stdio.h>
int main()
{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;
}
  1. 首先,我们定义了一个二维整型数组 aa[2][5],并初始化了其中的元素。
  2. 然后,我们定义了两个指针 ptr1 和 ptr2
    • ptr1 的值被设置为 (&aa + 1),即指向了 aa 数组之后的内存位置。
    • ptr2 的值被设置为 *(aa + 1),即指向了 aa 数组的第二行的首元素地址。
  3. 接下来,我们使用 printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); 打印了两个值:
    • *(ptr1 - 1) 表示 ptr1 指向的内存位置之前的整型值。
    • *(ptr2 - 1) 表示 ptr2 指向的地址之前的整型值。

现在让我们具体分析一下:

  • ptr1 指向了 aa 数组之后的内存位置,因此 *(ptr1 - 1) 实际上是访问了 aa 数组之后的内存位置之前的整型值。由于 aa 数组是一个二维数组,其内存布局是连续的,所以这个值应该是 10
  • ptr2 指向了 aa 数组的第二行的首元素地址,即指向了整型值 6 的地址。因此,*(ptr2 - 1) 实际上是访问了 6 之前的整型值,即 5

因此,程序的输出结果应该是 10,5

 

3.7 题⽬7

#include <stdio.h>
int main()
{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;
}

 

这里注意CPP指针,只有在++,--时发生了改变,CPP-2并未对指针发生改变 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/836560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【机器学习300问】79、Mini-Batch梯度下降法的原理是什么?

Mini-Batch梯度下降法是一种将训练数据集分成小批次进行学习的优化方法&#xff0c;通过这种方式&#xff0c;可以有效地解决内存限制问题并加速学习过程。 一、为什么要使用Mini-Batch&#xff1f; 在机器学习尤其是深度学习中&#xff0c;我们常常面临海量数据处理的问题。如…

吴恩达 深度学习 神经网络 softmax adam 交叉验证

神经网络中的层&#xff1a;输入层&#xff08;layer 0&#xff09;、隐藏层、卷积层&#xff08;看情况用这个&#xff09;、输出层。&#xff08;参考文章&#xff09; 激活函数&#xff1a; 隐藏层一般用relu函数&#xff1b; 输出层根据需要&#xff0c;二分类用sigmoid&…

ExcelVBA在选择区域(有合并)中删除清除空行

【问题】 关于删除空行&#xff0c;以前是用函数来完成工作的&#xff0c; 今天有人提出问题&#xff0c;传来这个文件&#xff0c; 现有数据&#xff0c;1w多行&#xff0c;其中有部分列有不同合并单元格&#xff0c;跨行也不一样。如果要进行筛选删除空行&#xff0c;有一定的…

matlab使用教程(70)—修改坐标区属性

1.控制坐标轴长度比率和数据单位长度 您可以控制 x 轴、y 轴和 z 轴的相对长度&#xff08;图框纵横比&#xff09;&#xff0c;也可以控制一个数据单位沿每个轴的相对长度&#xff08;数据纵横比&#xff09;。 1.1图框纵横比 图框纵横比是 x 轴、y 轴和 z 轴的相对长度。默认…

Python-VBA函数之旅-sum函数

目录 一、sum函数的常见应用场景 二、sum函数使用注意事项 三、如何用好sum函数&#xff1f; 1、sum函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://myelsa1024.blog.csdn.net/ 一、sum函数的常…

CSS:盒子模型

目录 ▐ box—model概述 ▐ 盒子的组成 ▐ 内容区 ▐ 内边距 ▐ 边框 ▐ 外边距 ▐ 清除浏览器默认样式 ▐ box—model概述 • CSS处理网页时&#xff0c;它认为每个标签都包含在一个不可见的盒子里. • 如果把所有的标签都想象成盒子&#xff0c;那么我们对网…

远程桌面如何连接?

远程桌面连接是一种可以在不同地点之间共享电脑桌面的技术。通过远程桌面连接&#xff0c;用户可以在远程的计算机上操作另一台计算机&#xff0c;就像是直接坐在前者的前面一样。这种技术可以帮助用户解决在不同地点之间共享数据、协同办公、设备管理等问题。 【天联】的使用场…

浅谈@Controller注解和其他四大注解的区别

各位大佬光临寒舍&#xff0c;希望各位能赏脸给个三连&#xff0c;谢谢各位大佬了&#xff01;&#xff01;&#xff01; 目录 1.Spring五大注解的使用约定 2.Controller注解的特别之处 3.总结 1.Spring五大注解的使用约定 Spring的五大注解&#xff08;Controller&#x…

14.CAS原理

文章目录 CAS原理1.什么是CAS2.Unsafe类中的CAS方法2.1.获取UnSafe实例2.2.调用UnSafe提供的CAS方法2.3.调用Unsafe提供的偏移量相关2.4.CAS无锁编程2.4.1.使用cas进行无锁安全自增案例 CAS原理 由于JVM的synchronized重量级锁设计操作系统内核态下的互斥锁的使用&#xff0c;其…

二分判定+选插冒排序+归并快速堆希尔+计数排序

二分力扣题 一&#xff1a;搜索二维矩阵 74. 搜索二维矩阵 按照题意&#xff1a;直接利用二维数组转换成一维数组进行求解 方法一&#xff1a;普通等于的二分查找 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {t…

io_uring的使用示例及其解释

io_uring的使用示例及其解释 1 io_uring机制1.1 io_uring机制1.2 io_uring系统调用接口功能介绍1.2.1 io_uring_setup()&#xff1a;1.2.2 io_uring_enter()&#xff1a;1.2.3 io_uring_register()&#xff1a; 2 liburing2.1 liburing简介2.2 liburing编译2.2.1 liburing的代码…

基础ArkTS组件:导航栏组件(HarmonyOS学习第三课【3.8】)

Navigation 官方文献 Navigation 组件一般作为页面布局的根容器&#xff0c;它提供了一系列属性方法来设置页面的标题栏、工具栏以及菜单栏的各种展示样式。 Navigation 除了提供了默认的展示样式属性外&#xff0c;它还提供了 CustomBuilder 模式来自定义展示样式 说明 该…

OCR技术在历史文献数字化中的革命性作用

随着数字化技术的不断发展&#xff0c;历史文献的数字化已成为保存和传播文化遗产的重要途径。其中&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术在历史文献数字化中发挥了革命性的作用&#xff0c;为研究者提供了更广阔的研究空间&#xff0c;推动了历史学研究的发…

kafka安装及收发消息

kafka需要与zookeeper配合使用&#xff0c;但是从2.8版本kafka引入kraft&#xff0c;也就是说在2.8后&#xff0c;zookeeper和kraft都可以管理kafka集群&#xff0c;这里我们依然采用zookeeper来配合kafka。 1、首先我们下载zookeeper 下载地址为 https://zookeeper.apache.org…

三. TensorRT基础入门-剖析ONNX架构并理解ProtoBuf

目录 前言0. 简述1. 执行一下我们的python程序2. ONNX是什么&#xff1f;3. onnx中的各类Proto3.1 理解onnx中的ValueInfoProto3.2 理解onnx中的TensorProto3.3 理解onnx中的NodeProto3.4 理解onnx中的AttributeProto3.5 理解onnx中的GraphProto3.6 理解onnx中的ModelProto 4. …

算法提高之单词接龙

算法提高之单词接龙 核心思想&#xff1a;dfs 预处理每两个字符串之间最短的公共部分长度 求最短公共 最终字符串是最长 dfs所有开头字符串 #include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N 25;int g[N][N…

Feign 和 OpenFeign 的区别

Feign 和 OpenFeign 都是用来进行服务间调用的客户端库&#xff0c;它们旨在简化HTTP API客户端的编写过程&#xff0c;使得编写对外部服务的接口就像调用本地方法一样简单。尽管它们有相似之处&#xff0c;但也存在一些关键差异&#xff1a; 归属和演进&#xff1a; Feign 最初…

大规模 RGB LED灯控系统 Lumos:创新与智能化的融合

灯控系统&#xff1a;创新与智能化的融合 在现代照明技术不断进步的背景下&#xff0c;灯控系统的应用已经从简单的开关控制&#xff0c;发展到能够进行复杂程控操作的智能化管理。我们推出的新一代灯控解决方案&#xff0c;凭借其高度的可配置性和跨平台兼容性&#xff0c;已…

虚拟化数据恢复—误还原虚拟机快照怎么办?怎么恢复最新虚拟机数据?

虚拟化技术原理是将硬件虚拟化给不同的虚拟机使用&#xff0c;利用虚拟化技术可以在一台物理机上安装多台虚拟机。误操作或者物理机器出现故障都会导致虚拟机不可用&#xff0c;虚拟机中的数据丢失。 虚拟化数据恢复环境&#xff1a; 有一台虚拟机是由物理机迁移到ESXI上面的&a…

pikachu靶场(xss通关教程)

&#xff08;注&#xff1a;若复制注入代码攻击无效&#xff0c;请手动输入注入语句&#xff0c;在英文输入法下&#xff09; 反射型xss(get型) 1.打开网站 发现有个框&#xff0c;然后我们在框中输入一个“1”进行测试&#xff0c; 可以看到提交的数据在url处有显示&#xf…