首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C语言探秘:深度解析指针与数组的奇妙输出结果

C语言探秘:深度解析指针与数组的奇妙输出结果

作者头像
折枝寄北
发布2024-12-24 11:16:09
发布2024-12-24 11:16:09
14000
代码可运行
举报
文章被收录于专栏:C语言学习专栏C语言学习专栏
运行总次数:0
代码可运行

一、数组理解辨析

在计算机科学和数据结构中,数组是一种基本且至关重要的数据结构。数组是一种线性数据结构,用于存储具有相同数据类型的元素集合。这些元素在内存中连续存储,并可以通过索引(通常是整数)快速访问。数组的概念不仅广泛应用于编程中的各个领域,如排序、搜索、动态规划等算法实现,也是理解更复杂数据结构(如链表、栈、队列和矩阵)的基础。

然而,数组的理解并不仅仅局限于其定义和基本操作。在实际应用中,数组的高效使用和优化往往涉及对存储空间的优化、时间复杂度的分析以及对特定编程语言中数组特性的深入理解。因此,辨析数组的不同类型(如静态数组、动态数组)、掌握数组的常用操作(如插入、删除、遍历)以及了解数组在不同场景下的应用,对于提高编程能力和解决复杂问题的能力至关重要。

在接下来的内容中,我们将深入探讨数组的基本概念、特性、操作以及在实际编程中的应用,旨在帮助读者更好地理解和掌握这一基础而强大的数据结构。

1.1一维数组

注:代码注释皆为辨析解释

代码语言:javascript
代码运行次数:0
运行
复制
指针和数组笔试题解析
一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
//4*4=16
printf("%d\n", sizeof(a + 0));
//a+0是数组首元素的地址,是地址就是4/8字节
printf("%d\n", sizeof(*a));
//*a是数组首元素,计算的是数组首元素的大小,单位是字节,4
printf("%d\n", sizeof(a + 1));
//数组第二个元素的地址
printf("%d\n", sizeof(a[1]));
//数组第二个元素的地址
printf("%d\n", sizeof(&a));
//&a是整个数组的地址,整个数组的地址也是地址,地址大小为4/8字节
//&a->类型:int(*)[4]数组指针

printf("%d\n", sizeof(*&a));
//&a是整个数组的地址,*&a就是拿到了数组,sizeof(*&a)==sizeof(a),16
printf("%d\n", sizeof(&a + 1));
//&a是整个数组的地址,&a+1跳过整个数组,指向数组后面的地址,是一个地址,4/8字节
printf("%d\n", sizeof(&a[0]));
//&a[0]首元素的地址,计算的是首元素地址的大小,4/8字节
printf("%d\n", sizeof(&a[0] + 1));
//&a[0]+1是数组第二个元素的地址,地址大小为4/8字节

1.2字符数组

代码语言:javascript
代码运行次数:0
运行
复制
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//arr单独放在sizeof内部,计算的是整个数组的大小,单位是字节,6
printf("%d\n", sizeof(arr + 0));//arr+0是数组首元素的地址,4/8字节
printf("%d\n", sizeof(*arr));//*arr是数组的首元素,计算的是首元素的大小,1字节
printf("%d\n", sizeof(arr[1]));//arr[1]是数组第二个元素,大小为1字节
printf("%d\n", sizeof(&arr));//&arr取出的是整个数组的地址,数组的地址也还是地址,是地址大小为4/8字节
printf("%d\n", sizeof(&arr + 1));//&arr+1是跳过整个数组,指向数组后面的地址,地址大小为4/8字节
printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是数组第二个元素的地址


printf("%d\n", strlen(arr));//随机值(至少为6),strlen只有遇到\0才会停止
printf("%d\n", strlen(arr + 0));//随机值,同上
printf("%d\n", strlen(*arr));//strlen('a')->strlen(97)非法访问error
printf("%d\n", strlen(arr[1]));//非法访问,同上 
printf("%d\n", strlen(&arr));//随机值,取出整个数组及其后面的地址
printf("%d\n", strlen(&arr + 1));//随机值,&arr是整个数组的地址,&a+1是跳过了整个数组的地址
printf("%d\n", strlen(&arr[0] + 1));//随机值,&arr[0] + 1是第二个元素的地址
return 0;

注:

  1. sizeof只关注占用内存的大小,单位是字节,不关心内存中存放的是什么
  2. sizeof是关键字
  3. strlen是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到\0才算结束,所以可能出现越界访问 strlen是库函数

1.3二维数组

代码语言:javascript
代码运行次数:0
运行
复制
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//3*4*4=48
	printf("%d\n", sizeof(a[0][0]));//4
	printf("%d\n", sizeof(a[0]));//a[0]是第一行的数组名,数组名单独放在sizeof内部,计算的是第一行的大小,16字节
	printf("%d\n", sizeof(a[0] + 1));//a[0]作为第一行的数组名,没有单独放在sizeof内部,没有取地址,表示的是数组首元素的地址
	//那就是a[0][1]的地址,a[0]+1就是第一行第二个元素的地址,是地址大小就是4/8字节
	printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)是第一行第二个元素,计算的是元素的大小---4个字节
	printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,数组名表示首元素的地址,就是第二行的地址,a + 1就是第二行的地址
	//第二行的地址也是地址,是地址就是4/8字节  a+1类型为--->int(*)[4]
	printf("%d\n", sizeof(*(a + 1)));//*(a + 1)就是第二行的全部元素,16字节
	printf("%d\n", sizeof(&a[0] + 1));//&a[0] + 1就是第二行的地址,是地址,地址大小为4/8字节
	printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1)是对第二行地址解引用,得到第二行的数组,计算第二行的大小
	printf("%d\n", sizeof(*a));//a是首元素的地址,就是第一行的地址,*a就是计算第一行的大小,16字节
	//*a---*(a+0)--a[0]
	printf("%d\n", sizeof(a[3]));
	//如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小,16字节
 //放在sizeof内部的表达式,不会进行运算操作

二、指针理解辨析

在编程世界的浩瀚宇宙中,指针如同一把双刃剑,既锋利无比,又需谨慎握持。作为C、C++等编程语言的核心特性之一,指针提供了一种直接操作内存地址的能力,使得程序能够以更高效、更灵活的方式管理数据和资源。指针不仅是对内存单元的抽象表示,更是连接程序与计算机底层硬件的桥梁,为开发者提供了前所未有的控制力。

通过这段旅程,期望能够帮助读者建立起对指针的深刻认识,不仅学会如何“舞剑”,更懂得如何“避锋”,从而在编程的征途中更加游刃有余。

2.1 指针典例1

代码语言:javascript
代码运行次数:0
运行
复制
//1.
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };//&a--->int(*)[5]
	int* ptr = (int*)(&a + 1);//强制类型转换
	printf("%d,%d", *(a + 1), *(ptr - 1));//2  5
	return 0;
}
//程序的结果是什么?

2.2 指针典例2

代码语言:javascript
代码运行次数:0
运行
复制
2.
//结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	p=(struct Test*)0x100000;
	printf("%p\n", p + 0x1);
	//0x00100014
	printf("%p\n", (unsigned long)p + 0x1);
	//0x00100001
	printf("%p\n", (unsigned int*)p + 0x1);
	//0x00100004
	return 0;
}

2.3 指针典例3

代码语言:javascript
代码运行次数:0
运行
复制
//3.小端,x86环境下
int main()
{
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);

	printf("%x,%x", ptr1[-1], *ptr2);//1,2
	
	return 0;
}

2.3 指针典例4

代码语言:javascript
代码运行次数:0
运行
复制
//4.
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//注意逗号表达式结果为最后一个数的值
	//数组实际存储为1 3 5,其余为未初始化的0
	int* p;
	p = a[0];
	printf("%d", p[0]);//结果为1
	return 0;
}

2.5 指针典例5

代码语言:javascript
代码运行次数:0
运行
复制
//5.
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]);
	//结果为:fffffffc,-4
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、数组理解辨析
    • 1.1一维数组
    • 1.2字符数组
    • 1.3二维数组
  • 二、指针理解辨析
    • 2.1 指针典例1
    • 2.2 指针典例2
    • 2.3 指针典例3
    • 2.3 指针典例4
    • 2.5 指针典例5
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档