揭秘C语言的心脏:深入探索指针与数组的奥秘

这篇具有很好参考价值的文章主要介绍了揭秘C语言的心脏:深入探索指针与数组的奥秘。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

揭秘C语言的心脏:深入探索指针与数组的奥秘

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C语言学习
贝蒂的主页:Betty‘s blog


揭秘C语言的心脏:深入探索指针与数组的奥秘

1. strlen()和sizeof的区别

名称 区别
sizeof 1. sizeof是操作符
2. sizeof计算操作数所占内存的⼤⼩,单位是字节
3. 不关注内存中存放什么数据
strlen 1. strlen是库函数,使⽤需要包含头⽂件 string.h
2. srtlen是求字符串⻓度的,统计的是 '\0' 之前字符的隔个数
3. 关注内存中是否有'\0' ,如果没有'\0',就会持续往后找,可能会越界

2. 数组名的理解

  1. sizeof(数组名),数组名单独放在括号里,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。
  2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表⽰⾸元素的地址。

3. 一维数组

3.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));
	return  0;
}

3.2 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

  • 该环境为VS2022,×64环境。后续也会在该环境下实验

3.3 解析

  1. sizeof(a),这⾥的a是数组名表⽰整个数组,计算整个数组的大小,数组每个元素是整型,一共有四个元素。所以4*4=16
  2. a+0是个表达式,这时a就是数组首元素地址,a+0仍是首元素地址,地址在×86环境下大小为4,在×64环境下大小为8
  3. a是数组首元素地址,对a解引用得到1,1是整型,所以大小为4
  4. a是数组首元素地址,a+1是第二个元素的地址,在×86环境下大小为4,在×64环境下大小为8
  5. a[1]是数组第二个元素2,是一个整型,大小为4
  6. &a是对数组名取地址,代表整个数组的地址,在×86环境下大小为4,在×64环境下大小为8
  7. &a的&和*相互抵消,相当于sizof(a),也就是第一步,大小为16
  8. &a+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,仍是地址,在×86环境下大小为4,在×64环境下大小为8
  9. &a[0]就是数组首元素的地址,在×86环境下大小为4,在×64环境下大小为8
  10. &a[0]+1就是第二个元素地址,与a+1等价,在×86环境下大小为4,在×64环境下大小为8

4. 字符数组

4.1 题目一

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));
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. sizeof(arr),这⾥的arr是数组名表⽰整个数组,计算整个数组的大小,数组每个元素是字符型,一共6个元素,1*6=6
  2. a+0是个表达式,这时a就是数组首元素地址,a+0仍是首元素地址,地址在×86环境下大小为4,在×64环境下大小为8
  3. arr是数组首元素地址,对arr解引用得到a,a是字符型,所以大小为1
  4. arr[1]是数组第二个元素b,大小为1
  5. &arr是对数组名取地址,代表整个数组的地址,在×86环境下大小为4,在×64环境下大小为8
  6. &arr+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,仍是地址,在×86环境下大小为4,在×64环境下大小为8
  7. &arr[0]+1就是第二个元素地址,与arr+1等价,在×86环境下大小为4,在×64环境下大小为8

4.2 题目二

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[0] + 1));
         printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. strlen是以'\0'为标志的,如果数组里没有,会继续往内存中寻找,直到找到'\0',所以是个随机值
  2. arr+0也是首元素地址,与1同理,所以也是随机值
  3. &arr是对数组名取地址,代表整个数组的地址,为了存储方便也会以数组首元素的地址表示,所以和1.2值相同,也是个随机值
  4. &arr+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,不知道'\0'在哪,所以也是随机值
  5. &arr[0]+1就是第二个元素地址,一直找到内存中的\0',是个随机值且会比1的长度少一
  6. strlen的参数要传地址进去,否则就会出错
  7. strlen的参数要传地址进去,否则就会出错

4.3 题目三

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));
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. sizeof(arr),计算整个数组的大小,字符串默认的结束标志为'\0',所以数组一共有7个元素,每个元素都是字符型,1*7=7
  2. arr+0是个表达式,这时arr数组首元素地址,arr+0仍是首元素地址,地址在×86环境下大小为4,在×64环境下大小为8
  3. arr是数组首元素的地址,对其解引用等到第一个元素a,a为字符型,大小为1
  4. arr[1]是数组第二个元素b,大小也为1
  5. &arr是对数组名取地址,代表整个数组的地址,在×86环境下大小为4,在×64环境下大小为8
  6. &arr+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,仍是地址,在×86环境下大小为4,在×64环境下大小为8
  7. &arr[0]+1就是第二个元素地址,与arr+1等价,在×86环境下大小为4,在×64环境下大小为8

4.4 题目四

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[0] + 1));
	printf("%d\n", strlen(*arr));
	printf("%d\n", strlen(arr[1]));
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. strlen是以'\0'为标志的,字符串默认结束标志位'\0',所以是6
  2. arr+0也是首元素地址,与1同理,所以也是6
  3. &arr是对数组名取地址,代表整个数组的地址,为了存储方便也会以数组首元素的地址表示,所以和1.2值相同,也是6
  4. &arr+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,不知道'\0'在哪,所以是随机值
  5. &arr[0]+1就是第二个元素地址,一直找到内存中的\0',会比1的长度少一,是5
  6. strlen的参数要传地址进去,否则就会出错
  7. strlen的参数要传地址进去,否则就会出错

4.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 + 1));
	printf("%d\n", sizeof(&p[0] + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. 字符串存储的是首元素地址,所以p相当于首元素地址,在×86环境下大小为4,在×64环境下大小为8
  2. 同理p+1是第二个元素的地址,在×86环境下大小为4,在×64环境下大小为8
  3. 对p取地址,相当于得到还是地址,在×86环境下大小为4,在×64环境下大小为8
  4. &arr+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,仍是地址,在×86环境下大小为4,在×64环境下大小为8
  5. &p[0]+1就是第二个元素地址,在×86环境下大小为4,在×64环境下大小为8
  6. *p与p[0]都是第一个元素,大小为1

4.6 题目六

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 + 1));
	printf("%d\n", strlen(&p[0] + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));

	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. strlen是以'\0'为标志的,字符串默认结束标志位'\0',所以是6
  2. p+1就是第二个元素地址,一直找到内存中的\0',会比1的长度少一,是5
  3. &p是对数组名取地址,代表整个数组的地址,为了存储方便也会以数组首元素的地址表示,所以和1值相同,也是6
  4. &p+1指的是以整个数组的地址为单位,跳过整个数组之后的地址,不知道'\0'在哪,所以是随机值
  5. &p[0]+1就是第二个元素地址,一直找到内存中的\0',会比1的长度少一,是5
  6. *p与p[0]都是第一元素。strlen的参数要传地址进去,否则就会出错

5. 二维数组

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]));

	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. sizeof(a),这⾥的a是数组名表⽰整个数组,计算整个二维数组的大小,数组每个元素是整型型,一共12个元素,12*4=48
  2. a[0][0]是指二维数组第一个元素,是个整型,大小为4
  3. a[0]相当于第一排首元素的地址,在二维数组中这里可以抽象理解为一维数组的数组名,单独放在sizeof中,所以4*4=16
  4. a[0]+1是一个表达式,代表第一排第二个元素的地址,在×86环境下大小为4,在×64环境下大小为8
  5. a[0]相当于第一排首元素的地址,*a[0]就是首元素,大小为4
  6. a第一排的地址,a+1是第二排地址,在×86环境下大小为4,在×64环境下大小为8
  7. *(a+1)等价于a[1],相当于第二排的数组名,4*4=16
  8. &a[0] + 1等价于a=1,即第二排地址,在×86环境下大小为4,在×64环境下大小为8
  9. *(&a[0] + 1))等价于*(a+1),与7相同,大小为16
  10. *a相当于第一排的数组名,大小也为16
  11. 因为sizeof只是根据类型判断,所以不会管是否越界,所以仍相当于一排的数组名,大小为16

揭秘C语言的心脏:深入探索指针与数组的奥秘

6. 指针深度理解

6.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;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. a是首元素的地址,a+1是指第二个元素的地址,对其解引用就是第二个元素,也就是2
  2. &a+1跳过整个数组,在被强制类型转换为int*,减1指向5,解引用就是5

揭秘C语言的心脏:深入探索指针与数组的奥秘

6.2 题目二

#include<stdio.h>
//在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;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. p的地址是0x100000,是十六进制表示,加1跳过一个结构体大小(20),十六进制表示就是00100014
  2. p被强制类型转换为无符号长整型,加1相当于加上数字1,就为00100001
  3. p被强制类型转换为无符号整型的指针,加1跳过一个整型,为00100004

6.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) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. 括号表达式从左往右依次计算,取最后一次的值,所以数组中元素简化为1,3,5
  2. p[0]就是数组的首元素,为1

6.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;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. 首先我们得知道数组在内存中是连续存储的
  2. p的类型为int(*p)[4],p[4][2]等价于*(*(p+4)+2),也就是说把p跳过4个以int(*p)[4]类型的距离,跳过2个整型
  3. 示意图如下,蓝色代表p[4][2],红色代表a[4][2]

揭秘C语言的心脏:深入探索指针与数组的奥秘

  1. &p[4][2] - &a[4][2]之间差四个元素,值为-4,又因为地址是无符号的整数,所以发生整型提升

揭秘C语言的心脏:深入探索指针与数组的奥秘

6.5 题目五

#include <stdio.h>
//输出什么?
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. 这是一个指针数组,每个元素类型为char*,分别指向一个字符串,如图

揭秘C语言的心脏:深入探索指针与数组的奥秘

  1. pa++跳过一个char*的地址指向第二个元素,对其解引用得到at

6.6 题目六

#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;
}

(1) 输出结果

揭秘C语言的心脏:深入探索指针与数组的奥秘

(2) 解析

  1. 由图可知cpp++指向c+2,再两次解引用得到"POINT"的首元素地址

揭秘C语言的心脏:深入探索指针与数组的奥秘

  1. cpp先++指向c+1,解引用得到c+1,再--得到c解引用得到ENTER的首元素地址,+3得到E的地址

揭秘C语言的心脏:深入探索指针与数组的奥秘

  1. *cpp[-2]等价于**(cpp-2),cpp-2指向c+3,两次解引用得到FIRST的首元素地址,+3得到S的地址

揭秘C语言的心脏:深入探索指针与数组的奥秘

  1. cpp[1][-1]等价于*(*(cpp-1)-1),这时候cpp指向的是第一个c,cpp-1再解引用得到c+2,再-1解引用得到NEW的地址,最好加1得到E的地址

揭秘C语言的心脏:深入探索指针与数组的奥秘文章来源地址https://www.toymoban.com/news/detail-825052.html

到了这里,关于揭秘C语言的心脏:深入探索指针与数组的奥秘的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • “深入解析JVM内部结构与工作原理:揭秘Java虚拟机的奥秘“

    标题:深入解析JVM内部结构与工作原理:揭秘Java虚拟机的奥秘 摘要:本文将深入探讨Java虚拟机(JVM)的内部结构和工作原理,帮助开发者更好地理解JVM的运行机制,从而提高Java程序的性能和稳定性。 正文: 一、JVM概述 Java虚拟机(Java Virtual Machine)是Java程序的运行环境,

    2024年02月11日
    浏览(36)
  • 深入解析C++中sizeof和strlen的奥秘:区别、应用与技巧全揭秘!

      sizeof  和  strlen  是 C++ 中用于处理字符串的两个不同的操作符,它们的作用和使用场景有很大的区别。 sizeof  是一个运算符,不是一个函数,用于获取一个类型或变量的字节大小。 对于数组, sizeof  返回整个数组的字节大小。 对于指针, sizeof  返回指针本身的字节大小

    2024年01月25日
    浏览(43)
  • 【Go 基础篇】深入探索:Go语言中的二维数组

    在计算机编程中,数组是一种基本的数据结构,用于存储相同类型的元素。而二维数组作为数组的一种扩展,允许我们以类似表格的方式存储和处理数据。在Go语言中,二维数组是一个重要的概念,本文将深入探讨Go语言中的二维数组,包括定义、初始化、遍历以及应用场景等

    2024年02月10日
    浏览(30)
  • 探索Java通信面试的奥秘:揭秘IO模型、选择器和网络协议,了解面试中的必备知识点!

    TCP(Transmission Control Protocol)是一种面向连接的可靠的传输协议。类似于打电话,它通过建立一个连接和保证数据的可靠传输来提高通信的可靠性。然而,由于要确保数据的可靠性,TCP协议会增加网络负担,效率相对较低。 UDP(User Datagram Protocol)是一种无连接、不可靠的传输

    2024年02月15日
    浏览(37)
  • “深入探索JVM:Java虚拟机背后的奥秘“

    标题:深入探索JVM:Java虚拟机背后的奥秘 摘要:本文将深入探索Java虚拟机(JVM)的内部工作原理和关键组成部分,揭示JVM背后的奥秘。通过对类加载机制、内存管理、垃圾回收、即时编译等方面的详细介绍,帮助读者更好地理解和优化Java程序的性能。 Java虚拟机(JVM)是

    2024年02月11日
    浏览(37)
  • 深入 Pinia:从代码出发探索 Vue 状态管理的奥秘

    🍍 项目地址:https://github.com/mk965/read-pinia 🧑🏼‍💻 本节代码:https://github.com/mk965/read-pinia/tree/article_1 🚗源码地址: github.com/vuejs/pinia 📦打包文件: rollup.config.js 🚪入口文件: packages/pinia/src/index.ts 将 pinia/packages/pinia/src 目录下的所有文件复制到我们之前生成项目的 /src

    2023年04月25日
    浏览(84)
  • “深入解析JVM内部机制:探索Java虚拟机的奥秘“

    标题:深入解析JVM内部机制:探索Java虚拟机的奥秘 JVM(Java虚拟机)是Java程序的核心执行环境,它负责将Java字节码转换为机器码并执行。了解JVM的内部机制对于理解Java程序的执行过程和性能优化至关重要。本文将深入解析JVM内部机制,帮助读者更好地理解Java虚拟机。 JVM的

    2024年02月13日
    浏览(39)
  • 探索数据的奥秘:一份深入浅出的数据分析入门指南

    书籍推荐 入门读物 深入浅出数据分析 啤酒与尿布 数据之美 数学之美 数据分析 Scipy and Numpy Python for Data Analysis Bad Data Handbook 集体智慧编程 Machine Learning in Action 机器学习实战 Building Machine Learning Systems with Python 数据挖掘导论 Machine Learning for Hackers 专业读物 Introduction to Semi-Su

    2024年01月21日
    浏览(35)
  • Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!

    关注+点赞+评论,评论区回复“Spring Boot源码解读与原理剖析:深入探索Java开发的奥秘!” 每篇最多 评论3条 !!采用抽奖助手自动拉取评论区有效评论送书两本, 开奖时间:9月11号 承载着作者的厚望,掘金爆火小册同名读物 《Spring Boot源码解读与原理剖析》 正式出书!

    2024年02月10日
    浏览(39)
  • 流式数据处理与高吞吐消息传递:深入探索Kafka技术的奥秘

    Kafka 是一种高吞吐量、分布式、基于发布/订阅的消息系统,最初由 LinkedIn 公司开发,使用Scala 语言编写,目前是 Apache 的开源项目。 Kafka 概念 Zookeeper 集群是一个基于主从复制的高可用集群,每个服务器承担如下三种角色中的一种 ZooKeeper中常见的角色: 领导者(Leader): 

    2024年02月09日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包