【C语言】数组名的不同情况

news2024/9/19 23:03:37

前言 

在C语言中,数组名的行为在不同的上下文中有细微的区别。数组名本质上是一个指向数组第一个元素的指针,但在某些情况下,它的行为会有所不同。以下是一些关键点,帮助你理解数组名在什么情况下代表第一个元素,什么情况下不完全等同于第一个元素:

先给出结论,避免误导:

  • sizeof(数组名),数组名表示整个数组。计算的是整个数组的大小,单位是字节
  • &数组名,数组名表示整个数组。取出的是整个数组的地址

除此之外,所有的数组名都是数组首元素的地址!!!

一、情况分类(分) 

1. 数组名作为指针

  • 作为参数传递:当你将数组名作为参数传递给函数时,它会退化为一个指针,指向数组的第一个元素。这意味着你实际上传递的是数组的地址,而不是整个数组。
void printArray(int arr[]) {
    printf("%d\n", arr[0]); // arr 指向第一个元素
}

2. 数组名与下标运算

  • 访问元素:在下标运算中,数组名始终代表指向数组第一个元素的地址,你可以通过下标访问任何元素。
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[2]); // 访问第三个元素

3. 数组名与算术运算

  • 加减运算:当数组名与其他指针或整数进行加减运算时,它不再简单地表示第一个元素,而是根据操作执行相应的偏移。例如,arr + 1 指向第二个元素的位置,而不仅仅是第二个元素。
int arr[5];
(arr + 2)[0] = 10; // 相当于 arr[2] = 10

4. 数组名与sizeof运算符

  • 计算数组大小:当sizeof运算符应用于数组名时,结果是整个数组的字节大小,而不是单个元素的大小。但是,如果数组名是在函数参数列表中,则sizeof运算符返回的是指针的大小,因为此时数组名已经退化为指针。
int arr[5];
printf("%zu\n", sizeof(arr)); // 输出数组总大小


%zu 是 C99 标准引入的一个格式说明符,专门用于输出 size_t 类型的数值。
size_t 是一个无符号整数类型,通常用于表示对象的大小或数组的长度,
比如在 sizeof 运算符的结果中。

5. 数组名与类型转换

  • 强制类型转换:当数组名与其他类型进行转换时,它可能不再代表第一个元素,而是根据转换规则被解释为其他类型的指针。

6.小结

数组名在C语言中的行为主要取决于其使用的上下文。当用作指针、参与下标运算或进行算术运算时,它始终代表指向数组第一个元素的地址。然而,在涉及sizeof运算符或类型转换时,其含义可能会发生变化,需要根据具体情况分析。理解这些区别对于正确编写和调试C代码至关重要。

二、情况整理(总) 

1. 数组名作为指向第一个元素的指针

常见情况:

  • 函数传参: 当数组作为参数传递给函数时,数组名实际上是一个指向第一个元素的指针。

void printArray(int arr[], int size) {
    // arr 是一个指向数组第一个元素的指针
}
  • 数组元素访问: 在访问数组元素时,如arr[i],数组名arr实际上是第一个元素的地址,而i是偏移量。
int value = arr[0]; // 等同于 *(arr + 0)
  • 指针算术运算: 可以在数组名上进行指针运算,如arr + 1表示数组第二个元素的地址。

2. 数组名不是指向第一个元素的指针

例外情况:

  • sizeof 运算符: 使用sizeof运算符时,数组名表示整个数组,而不是指向第一个元素的指针。

int arr[5];
size_t size = sizeof(arr); // 返回数组的总字节数,不是指针大小

在这种情况下,sizeof(arr)等于数组元素类型的大小乘以数组的元素个数,而不是sizeof(int*)。

& 运算符: 使用地址运算符&时,&arr表示整个数组的地址,而不是数组第一个元素的地址。

int arr[5];
int (*ptr)[5] = &arr; // ptr 是指向数组的指针

在这种情况下,&arr的类型是int (*)[5],而不是int*。

3. 特殊情况

  • 字符串字面量: 在使用字符串字面量初始化字符数组时,数组名也是数组的地址。

char str[] = "hello";
str指向字符串"hello"的第一个字符,但如果取&str,则是整个字符数组的地址。

4.小结

通常,数组名在大多数上下文中表示数组第一个元素的地址,即数组名等同于指向第一个元素的指针。然而,在使用sizeof运算符和&运算符时,数组名表示整个数组(通常是,但不完全),而不是简单的指针。这些例外情况是理解C语言中数组行为的关键。

  • 对于 sizeof 运算符,数组名是否表示整个数组取决于上下文:在声明处,它表示整个数组;而在函数参数中,它表示指针。
  • 对于 & 运算符,它确实返回整个数组的地址,但这个地址的类型和用途在实践中较为有限,通常我们使用的是数组名作为指向首元素的指针。

三、总结

在C语言中,数组名在绝大多数情况下表现为指向数组第一个元素的指针。具体而言,当数组名出现在表达式中(除了作为函数参数时的特殊情况),它会被视为指向数组首元素的常量指针。这意味着你可以使用下标运算符来访问数组中的元素,也可以进行指针算术来遍历数组。

然而,当数组名作为函数参数被传递时,它会退化为一个普通指针,失去了与原始数组大小和边界的相关信息。在函数内部,这个指针仅仅指向了传入数组的第一个元素,而不携带任何有关整个数组的信息,除非另外传递数组的大小。

此外,当使用 `&` 运算符获取数组的地址时,虽然从技术上讲这是获取整个数组的地址,但实际上这个地址通常不能直接用来访问数组元素,因为C语言中没有标准的语法来解引用指向整个数组的指针。相反,数组名自身作为指针使用时,就已经足够用于访问和操作数组的元素了。

为了避免误导,简而言之,除了作为函数参数时和在特定的地址运算符使用场景中,数组名在C语言中一般都等价于指向其第一个元素的指针。即:

  • sizeof(数组名),数组名表示整个数组。计算的是整个数组的大小,单位是字节
  • &数组名,数组名表示整个数组。取出的是整个数组的地址

除此之外,所有的数组名都是数组首元素的地址!!!

四、更多 

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>

1. sizeof(数组名),数组名表示整个数组。计算的是整个数组的大小,单位是字节
2. &数组名,数组名表示整个数组。取出的是整个数组的地址
除此之外,所有的数组名都是数组首元素的地址


sizeof 是操作符,只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么,
sizeof 运算符的结果是一个 size_t 类型的值,这是无符号整数类型,通常定义在 <stddef.h> 头文件中,即:sizeof ----> size_t -> unsigned int

strlen是库函数,是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到\0才算结束,所以可能存在越界访问的

size_t strlen(const char* str)
 
 
int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };//4*4=16
	printf("%d\n", sizeof(a));//16
	printf("%d\n", sizeof(a + 0));//a+0 其实是数组第一个元素的地址,是地址就是4/8字节
	printf("%d\n", sizeof(*a));//*a是数组首元素,计算的是数组首元素的大小,单位是字节,4
	printf("%d\n", sizeof(a + 1));//a+1是第二个元素的地址,是地址大小就是4/8
	printf("%d\n", sizeof(a[1]));//a[1]是第二个元素,计算的是第二个元素的大小-4-单位是字节
	printf("%d\n", sizeof(&a));//&a是整个数组的地址,整个数组的地址也是地址,地址的大小就是4/8字节
	//&a---> 类型:int(*)[4]

	printf("%d\n", sizeof(*&a));//&a是数组的地址,*&a就是拿到了数组,*&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字节
	return 0;
}




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));//strlen('a')->strlen(97),非法访问-err(错误)
	printf("%d\n", strlen(arr[1]));//'b'-98,和上面的代码类似,是非法访问 - err(错误)
	printf("%d\n", strlen(&arr));//&arr虽然是数组的地址,但是也是从数组起始位置开始的,计算的还是随机值
	printf("%d\n", strlen(&arr + 1));
	//&arr是数组的地址,&arr+1是跳过整个数组的地址,求字符串长度也是随机值
	printf("%d\n", strlen(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址,是'b'的地址,求字符串长度也是随机值

	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));//取出的数组的地址,数组的地址也是地址,是地址大小就是4/8
	printf("%d\n", sizeof(&arr + 1));//&arr+1是跳过整个,指向数组后边空间的地址,4/8
	printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是数组第二个元素的地址,是地址4/8字节

	return 0;
}



int main()
{
	char arr[] = "abcdef";//数组是7个元素
	//[a b c d e f \0]
	printf("%d\n", strlen(arr));//6,arr是数组首元素的地址,strlen从首元素的地址开始统计\0之前出现的字符个数,是6
	printf("%d\n", strlen(arr + 0));//arr + 0是数组首元素的地址,同第一个,结果是6
	printf("%d\n", strlen(*arr));//*arr是'a',是97,传给strlen是一个非法的地址,造成非法访问
	printf("%d\n", strlen(arr[1]));//err(错误)
	printf("%d\n", strlen(&arr));//6
	printf("%d\n", strlen(&arr + 1));//&arr + 1是跳过数组后的地址,统计字符串的长度是随机值
	printf("%d\n", strlen(&arr[0] + 1));//&arr[0]+1是b的地址,从第二个字符往后统计字符串的长度,大小是5

	printf("%d\n", sizeof(arr));//7 - 数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节
	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是第二个元素的地址,是4/8字节

	return 0;
}



int main()
{
	const char* p = "abcdef";
	
	printf("%d\n", strlen(p));//6- 求字符串长度
	printf("%d\n", strlen(p + 1));//p + 1是b的地址,求字符串长度就是5
	printf("%d\n", strlen(*p));//err(错误),*p是'a'
	printf("%d\n", strlen(p[0]));//err(错误) - 同上一个
	printf("%d\n", strlen(&p));//&p拿到的是p这个指针变量的起始地址,从这里开始求字符串长度完全是随机值
	printf("%d\n", strlen(&p + 1));//&p+1是跳过p变量的地址,从这里开始求字符串长度也是随机值
	printf("%d\n", strlen(&p[0] + 1));//&p[0] + 1是b的地址,从b的地址向后数字符串的长度是5

	printf("%d\n", sizeof(p));//p是指针变量,大小就是4/8字节
	printf("%d\n", sizeof(p + 1));//p + 1是b的地址,是地址,就是4/8个字节
	printf("%d\n", sizeof(*p));//*p是'a',sizeof(*p)计算的是字符的大小,是1字节
	printf("%d\n", sizeof(p[0]));//p[0]-->*(p+0) --> *p  就同上一个,1字节
	printf("%d\n", sizeof(&p));//&p是二级指针,是指针大小就是4/8
	printf("%d\n", sizeof(&p + 1)); //&p + 1是跳过p变量后的地址,4/8字节
	printf("%d\n", sizeof(&p[0] + 1));//p[0]就是‘a’,&p[0]就是a的地址,+1,就是b的地址,是地址就是4/8

	return 0;
}



//int main()
//{
//	printf("%d\n", sizeof('a'));
//	return 0;
//}



int main()
{
	//二维数组
	int a[3][4] = { 0 };
	//printf("%p\n", &a[0][0]);
	//printf("%p\n", a[0]+1);

	printf("%d\n", sizeof(a));//48 = 3*4*4
	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][0]的地址,a[0]+1就是第一行第二个元素的地址,是地址就是4/8个字节
	printf("%d\n", sizeof(*(a[0] + 1)));//*(a[0] + 1)是第一行第2个元素,计算的是元素的大小-4个字节
	printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,数组名表示首元素的地址,就是第一行的地址,a+1就是第二行的地址
	//第二行的地址也是地址,是地址就是4/8   
	//a - int (*)[4]
	//a+1--> int(*)[4]
	printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1)表示的就是第二行,*(a+1)--a[1]  //16
	printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1是第二行的地址,地址的大小就是4/8
	printf("%d\n", sizeof(*(&a[0] + 1)));//*(&a[0] + 1) 是对第二行的地址解引用,得到的就是第二行,计算的就是第二行的大小
	printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址,*a就是第一行,计算的就是第一行的大小
	//*a -- *(a+0)--a[0]
	printf("%d\n", sizeof(a[3]));//16字节 int[4]
	//如果数组存在第四行,a[3]就是第四行的数组名,数组名单独放在sizeof内部,计算的是第四行的大小

	//int a = 10;
	//printf("%d\n", sizeof(int));

	return 0;
}



C语言中,表达式有2个属性:2+3
值属性:5
类型属性:int
//int main()
//{
//	short s = 3;
//	int a = 10;
//	s = a + 2;
//	printf("%d\n", sizeof(s = a + 2));//2
//	printf("%d\n", s);//3
//
//	return 0;
//}

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

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

相关文章

前端江湖:从菜鸟到大侠的修炼手册

在这个数字编织的梦幻世界里&#xff0c;前端&#xff0c;这个听起来就带着几分仙气与神秘感的词汇&#xff0c;实则是每一位互联网探险家手中的魔法杖。它不仅连接着代码的冰冷逻辑与用户的炽热情感&#xff0c;更在无数次的点击与滑动间&#xff0c;绘制出一幅幅绚丽多彩的交…

智慧港口整体解决方案

1. 智慧港口概况与建设意义 智慧港口建设对创新驱动和转型发展具有重要推动作用&#xff0c;是港口发展的主要方向。它通过物联网、移动互联网、云计算、人工智能等高新技术与港口功能的融合&#xff0c;提升港口的智能化和信息化水平&#xff0c;对国家可持续发展具有重要意义…

一键将桌面资料存到d盘的工具,小巧、绿色、免费、免安装

为了提升我们的系统稳定性以及资料的安全性&#xff0c;建议大家将电脑桌面的资料默认路径设置为D盘或其他磁盘&#xff0c;这样不仅会减少系统盘的占用空间&#xff0c;在系统盘出现故障时我们还可以通过pe工具备份桌面的资料。虽然我们也可以通过一些操作来修改桌面文件以及我…

视频主题Qinmei 3.0视频站源码_WordPress影视视频主题/附详细安装教程

Qinmei 3.0主题主要是将 wordpress 改造成纯 api 的站点&#xff0c;以便实现前后端分离的技术栈&#xff0c;目前的进度已经大致完成&#xff0c;唯一的问题就是需要安装 JWT token 插件。 功能介绍&#xff1a; 支持豆瓣以及 bangumi 的一键获取信息, 豆瓣 api 目前使用的是…

科普文:分布式架构中的三高:高并发、高性能、高可用

关于高并发 高并发场景 互联网应用以及云计算的普及&#xff0c;使得架构设计和软件技术的关注点从如何实现复杂的业务逻 辑&#xff0c;转变为如何满足大量用户的高并发访问请求。 一个简单的计算处理过程&#xff0c;如果一旦面对大量的用户访问&#xff0c;整个技术挑战就…

Java1.0标准之重要特性及用法实例(十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列…

aspeed 2600适配u-boot/kernel

1.说明 本文采取aspeed sdk v09.01版本来适配自己的实际的硬件开发平台。!!非采取qemu模拟方式!! 2.采用的镜像文件 2.1 采用网站编译好的镜像 采用网站: https://github.com/AspeedTech-BMC/openbmc/releases的文件: 1.ast2600-default-515-obmc.tar.gz.选择里面的文件ima…

【初阶数据结构篇】单链表的实现(赋源码)

文章目录 单链表的实现代码位置概念与结构概念&#xff1a;结构&#xff1a; 链表的性质链表的分类单链表的实现单链表的创建和打印及销毁单链表的创建单链表的打印单链表的销毁 单链表的插入单链表头插单链表尾插单链表在指定位置之前插入数据单链表在指定位置之后插入数据 单…

ChatTTS(文本转语音) 一键本地安装爆火语音模型

想不想让你喜欢的文章&#xff0c;有着一个动听的配音&#xff0c;没错&#xff0c;他就可以实现。 ChatTTS 是一款专为对话场景设计的文本转语音模型&#xff0c;例如 LLM 助手对话任务。它支持英语和中文两种语言。 当下爆火模型&#xff0c;在Git收获23.5k的Star&#xff…

Flink-CDC解析(第47天)

前言 本文主要概述了Flink-CDC. 1. CDC 概述 1.1 什么是CDC&#xff1f; CDC是&#xff08;Change Data Capture 变更数据获取&#xff09;的简称 &#xff0c;在广义的概念上&#xff0c;只要是能捕获数据变更的技术&#xff0c;都可以称之为 CDC。 核心思想是&#xff0c…

【C语言】【数据结构】二分查找(数组的练习)

目录 一、什么是二分查找 二、算法思想 2.1、概述 2.2、举例 &#xff08;1&#xff09;查找3&#xff08;数组里面存在的数&#xff09; &#xff08;2&#xff09;查找12&#xff08;数组里面不存在的数&#xff09; 三、代码实现 四、计算mid公式的优化 一、…

二阶段测试:

二阶段测试&#xff1a; 架构&#xff1a; 服务器类型部署组件ip地址DR1调度服务器 主&#xff08;ha01&#xff09;KeepalivedLVS-DR192.168.60.30DR2调度服务器 备 (ha02)KeepalivedLVS-DR192.168.60.40web1节点服务器 (slave01)NginxTomcatMySQL 备MHA managerMHA node192.…

Open3D 点云按xyz轴等距切片

目录 一、概述 1.1原理 1.2实现步骤 1.3应用 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2按x轴切片 3.3按y轴切片 3.4按z轴切片 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff…

计算机网络通信基础概念

目录 1、网络通信的本质 2、网络的发展 3、网络协议&#xff08;TCP\IP协议&#xff09; 3.1 协议实现通信的原理 3.2 协议的具体概念 3.3 协议的模型 4、数据链路层 5、网络协议栈和操作系统的关系 6、网络协议通信过程 6.1 通信过程的封装与解包 7、以太网通信…

助力樱桃智能自动化采摘,基于嵌入式端超轻量级模型LeYOLO全系列【n/s/m/l】参数模型开发构建果园种植采摘场景下樱桃成熟度智能检测识别系统

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经渗透到我们生活的方方面面&#xff0c;从智能家居到自动驾驶&#xff0c;再到医疗健康&#xff0c;其影响力无处不在。然而&#xff0c;当我们把目光转向中国的农业领域时&#xff0c;一个令人惊讶的…

【AI】SpringCloudAlibaba AI 学习

Spring Cloud Alibaba AI 简介 Spring Cloud Alibaba AI 以 Spring AI 为基础&#xff0c;并在此基础上提供阿里云通义系列大模型全面适配&#xff0c;让用户在 5 分钟内开发基于通义大模型的 Java AI 应用。 官网&#xff1a; https://sca.aliyun.com/ https://sca.aliyun.co…

理解 HTTP 请求中 Query 和 Body 的异同

本文将深入探讨HTTP请求中的两个关键要素&#xff1a;查询参数&#xff08;Query&#xff09;和请求体&#xff08;Body&#xff09;。我们将阐明它们之间的差异&#xff0c;并讨论在何种情况下使用每一种。 HTTP 请求概述 HTTP 请求是客户端&#xff08;如浏览器&#xff09…

知道秘密的人

一、力扣题目&#xff1a; 二、理论分析 由于 天数是一天一天变化的&#xff0c;用 数组的下标代表天数i, 数组中的 数据代表知道秘密在第i天的人数 假设在某个人在知道秘密的第3天开始传播&#xff0c;在第6天忘记&#xff0c;由于 第1天1个人发现了秘密 spread为能传播秘密的…

MATLAB-bode图编程

num[1 1];den [2 1];tf(num,den)bode(tf(num,den));hold on