数组名和数组名取地址的区别

《再再论指针》后记         在这篇后记中,笔者将对三个问题进行补充:一、关于数组名取地址的问题。c89、c99允许对数组名取地址,是由于数组符合一个对象的定义,按照一        个对象的语义,对其取地址是合理的。但矛盾在于,数组名是一个符号地址,是一个右值,对其取地址不        符合&运算符的语法。c89、c99委员会经过权衡,认为维护一个对象的合理性比一个运算符更重要、更合        理,因此 阅读详情

以下代码会打印出什么样的日志呢?

  1. #include <stdio.h>   
  2.   
  3. int a[2] = {1,2};  
  4. int main(){  
  5.         printf("a = %p\n", a); // I   
  6.         printf("&a = %p\n", &a); // II   
  7.         printf("a + 1 = %p\n", a + 1);// III   
  8.         printf("&a + 1 = %p\n", &a + 1);// IV   
  9.   
  10.         return 0;  
  11. }  
#include <stdio.h>

int a[2] = {1,2};
int main(){
        printf("a = %p\n", a); // I
        printf("&a = %p\n", &a); // II
        printf("a + 1 = %p\n", a + 1);// III
        printf("&a + 1 = %p\n", &a + 1);// IV

        return 0;
}
本机(linux)结果输出:
a = 0x804a014
&a = 0x804a014
a + 1 = 0x804a018
&a + 1 = 0x804a01c

没错,上面I 和 II打印出来的地址是一样的,IV 要比 III 大4个字节的地址空间。下面是我对这一现象的解释,如有不妥的地方请各位大虾一定给于指出:

首先引用《C和指针》p141中的理论:
在C中, 在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址。 它的类型取决于数组元素的类型: 如果它们是int类型,那么数组名的类型就是“指向int的常量指针“。
看到这里我想应该就知道为什么 会有I 和 III式的结果了。

对于II 和 IV 则是特殊情况,在《C和指针》p142中说到,在以下两中场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。 sizeof返回整个数组的长度,而不是指向数组的指针的长度。 取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。
所以&a后返回的指针便是指向数组的指针,跟a(一个指向a[0]的指针)在指针的类型上是有区别的。

然后我们用符号表和汇编代码来看看编译器到底是怎样区分&a 和 a, 并将其转换为汇编代码的:

通过 nm a.out 得到符号表如下:

  1. 。。。。。。。// 省略了一些与本主题无关的变量  
  2. 0804a01c A _edata  
  3. 0804a024 A _end  
  4. 080484ec T _fini  
  5. 08048508 R _fp_hw  
  6. 080482bc T _init  
  7. 08048330 T _start  
  8. 0804a014 D a // a 变量保存在虚拟地址0x0804a014 中  
  9. 0804a01c b completed.7021  
  10. 0804a00c W data_start  
  11. 0804a020 b dtor_idx.7023  
  12. 080483c0 t frame_dummy  
  13. 080483e4 T main // main函数的地址  
  14.          U printf@@GLIBC_2.0  
。。。。。。。// 省略了一些与本主题无关的变量
0804a01c A _edata
0804a024 A _end
080484ec T _fini
08048508 R _fp_hw
080482bc T _init
08048330 T _start
0804a014 D a // a 变量保存在虚拟地址0x0804a014 中
0804a01c b completed.7021
0804a00c W data_start
0804a020 b dtor_idx.7023
080483c0 t frame_dummy
080483e4 T main // main函数的地址
         U printf@@GLIBC_2.0

调用gcc -S xx.c得到汇编代码:

  1.     .file   "name_of_array.c"  
  2. .globl a  
  3.     .data  
  4.     .align 4  
  5.     .type   a, @object  
  6.     .size   a, 8 // 从这里我们便知道sizeof(a) 等于8   
  7. a:  
  8.     .long   1 // 从这里可以看出,编译器直接把 .c文件中的int 转化为long型   
  9.     .long   2  
  10.     .section    .rodata  
  11. .LC0:  
  12.     .string "a = %p\n"  
  13. .LC1:  
  14.     .string "&a = %p\n"  
  15. .LC2:  
  16.     .string "a + 1 = %p\n"  
  17. .LC3:  
  18.     .string "&a + 1 = %p\n"  
  19.     .text  
  20. .globl main  
  21.     .type   main, @function  
  22. main:  
  23.     pushl   %ebp  
  24.     movl    %esp, %ebp  
  25.     andl    $-16, %esp  
  26.     subl    $16, %esp  
  27.     movl    $.LC0, %eax // I 所对应的汇编代码   
  28.     movl    $a, 4(%esp)  
  29.     movl    %eax, (%esp)  
  30.     call    printf  
  31.     movl    $.LC1, %eax // II 所对应的汇编代码   
  32.     movl    $a, 4(%esp)  
  33.     movl    %eax, (%esp)  
  34.     call    printf  
  35.     movl    $.LC2, %eax // III 所对应的汇编代码   
  36.     movl    $a+4, 4(%esp)  
  37.     movl    %eax, (%esp)  
  38.     call    printf  
  39.     movl    $a+8, %edx // IV 所对应的汇编代码   
  40.     movl    $.LC3, %eax  
  41.     movl    %edx, 4(%esp)  
  42.     movl    %eax, (%esp)  
  43.     call    printf  
  44.     movl    $0, %eax  
  45.     leave  
  46.     ret  
  47.     .size   main, .-main  
  48.     .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"  
  49.     .section    .note.GNU-stack,"",@progbits  
	.file	"name_of_array.c"
.globl a
	.data
	.align 4
	.type	a, @object
	.size	a, 8 // 从这里我们便知道sizeof(a) 等于8
a:
	.long	1 // 从这里可以看出,编译器直接把 .c文件中的int 转化为long型
	.long	2
	.section	.rodata
.LC0:
	.string	"a = %p\n"
.LC1:
	.string	"&a = %p\n"
.LC2:
	.string	"a + 1 = %p\n"
.LC3:
	.string	"&a + 1 = %p\n"
	.text
.globl main
	.type	main, @function
main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	movl	$.LC0, %eax // I 所对应的汇编代码
	movl	$a, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	movl	$.LC1, %eax // II 所对应的汇编代码
	movl	$a, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	movl	$.LC2, %eax // III 所对应的汇编代码
	movl	$a+4, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	movl	$a+8, %edx // IV 所对应的汇编代码
	movl	$.LC3, %eax
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	movl	$0, %eax
	leave
	ret
	.size	main, .-main
	.ident	"GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
	.section	.note.GNU-stack,"",@progbits
I所对应的汇编代码 movl $a, 4(%esp)
$表示取地址,通过符号表我们知道a对应地址为0x0804a014, 所以这段代码将会打印0x0804a014。但是我们明明在代码里写的是printf("a = %p\n", a), (如果a不为数组名而是一般意义的int变量,相应的汇编码应为movl a, 4(%esp) 怎么编译后的汇编代码会是对a取地址呢? 本人猜测为编译器自动给a 加了一个取值符,从而翻译为$a。
结论: 对于用户没有明确给出&的编码,编译器翻译自动给变量a加上取值符$, 其中取a的地址得到的指针类型由数组元素决定。

II 略过

III movl $a+4, 4(%esp)
对a加上取值符得到$a,因为数组元素类型为int,所以指针每次需要移动四个字节的地址空间。 所以c代码 a + 1 翻译为汇编 $a + 4 

IV  movl $a+8, %edx 
所对应用户代码为printf("a = %p\n", &a + 1), 根据《C和指针》中的理论,当a前面有&操作符时,编译器将会把a对应符号表中的地址看作指向数组的指针,sizeof(a) 为8,
从而&a + 1 将会翻译为$a + 8
结论: 对于用户明确给出&的编码,编译器将会把取a的地址得到的指针类型看作指向数组的指针。

总结:编译器通过用户是否给出&,来决定指针变量的类型,进而翻译为相应的汇编码。 或者换句话说,&符只是用来表明变量a取地址后得到的值,被看作什么类型的指针,而不是用来表示对a进行取地址操作。

从平衡车到寻迹小车:IMU963RA九轴陀螺仪实战避坑与四元数融合指南 本文详细解析了IMU963RA九轴陀螺仪在机器人姿态控制中的应用,重点探讨了从六轴升级到九轴系统的技术路径及四元数融合算法。通过实战案例,揭示了如何利用地磁计补偿陀螺积分漂移,并提供硬件集成、传感器校准及算法实现的完整解决方案,有效将8小时漂移控制在2度以内。 阅读详情

相关推荐

彻底解决Win11 Android模拟器驱动权限问题:手动修改silent_install.bat的保姆级教程

本文提供了一份详细的Win11 Android模拟器驱动权限问题解决方案,重点介绍了手动修改silent_install.bat脚本的两种方法。通过分析问题根源并给出具体代码修改方案,帮助开发者彻底解决Android Studio在Windows 11上安装时遇到的UAC循环弹窗问题,确保android-emulator-hypervisor-driver顺利安装。

dengliugong3918的博客 461

解析:数组名a、数组名取地址&a、数组首地址&a[0]、数组指针*p

数组和指针向来就是傻傻分不清,当他们一起出现的时候就更加懵逼。使用实例详细解析:数组名a、数组名取地址&a、数组首地址&a[0]、数组指针*p

loongkingwhat的博客 5万+

Ta-Lib源码解析(三):蜡烛图指标 (Candlestick Indicator) #(附Python重构代码和示意图)(补充中)

蜡烛图指标为识别K线形态的指标,笔者认为重点在于定义方式所提供的思路,背后的投资逻辑可以借鉴但不建议直接参考文档的翻译内容和源码逻辑存在不一致的现象,笔者分别列出中文百科中的定义和源码实际逻辑,供大家参考技术分析理论中的一些模糊词,ta-lib中给出了精确的定义源码

量化就是探索生活 3254

数组名a、数组名取地址&a、数组首地址&a[0]、数组指针*p

数组名a、数组名取地址&a、数组首地址&a[0]、数组指针*p

zxf347085420的博客 5554

S7-1200五轴伺服系统PLC控制方案与调试技巧

工业自动化领域中,多轴伺服控制是实现精密加工的核心技术。基于PROFINET实时通信协议和PLCopen运动控制标准,现代PLC(如西门子S7-1200)能够实现多轴高精度同步控制。其技术原理是通过等时同步模式确保各轴运动的时序一致性,结合PID调节和电子齿轮比实现位置闭环。这种方案在数控机床、3D打印等场景具有重要应用价值。本文以五轴联动系统为例,详细解析了S7-1200与V90伺服驱动器的硬件组态方案,包括PROFINET环网配置和IRT同步设置。在软件层面,重点介绍了基于TIA Portal的工艺对象

weixin_34332905的博客 415

2026深度破解:OpenClaw开源AI框架架构与实战全景解析

2026年初,一款名为OpenClaw的开源AI Agent框架以惊人的速度席卷全球开发者社区,短短数周GitHub星标突破20万,成为史上增长最快的开源项目。本文从架构设计、核心原理、代码实现和工程实战四个维度,深度剖析OpenClaw的技术内核,帮助开发者从理解到实践,快速掌握这一革命性的AI开发框架。

fox0329的博客 2373

C语言中的数组名取地址

数组名取地址,数组指针

freeline的博客 8389

基于滤波反投影的图像重建算法matlab仿真

基于滤波反投影的图像重建算法是一种用于医学影像重建的计算机辅助技术。该算法可以从投影数据中重建出高质量的三维图像,被广泛应用于医学影像学、CT、PET等领域。本文将详细介绍基于滤波反投影的图像重建算法的原理、步骤和应用。一、原理介绍基于滤波反投影的图像重建算法基于X射线CT成像原理,它的主要思想是通过对投影数据进行处理,反推出被成像物体的三维形状。其核心原理是投影数据与目标物体之间的线性关系,即通过估计目标物体的吸收率来计算投影数据的变化,从而反推出目标物体的三维形状。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 386

数组a[]的数组名a和对数组名取地址&a的区别

二者区别 如:对于数组int a[10]; a: 数组名a是指数组首元素的地址(以数组中的一个元素大小为单位) &a: 数组名取地址&a是指整个数组的首地址(以整个数组大小为单位) #include <iostream> using namespace std; int main() { int a[10] = { 0,1,2,3,4,5,6,7,8,9 }; ...

weixin_41926709的博客 988

开发simulink 以及stm32半物理仿真碰到的问题,以及处理方法,Error:External Mode Open Protocol Connect command failed(实测有用)

串口实现的,但是在测试的时候发现,这个串口可以正常发送和接收数据,但是一旦把这个串口只让其进行版物理仿真就报错。直接刷写程序是可以的,最后查到问题了。我要实现半物理仿真,就是通过matlab实现可以直接控制stm32板子,实际上是通过xcp on。然后点击simulink上的观测按钮就可以直接和stm32平台进行通讯了。3,还是使用串口3,但是重新需要设置一下com口,因为重新插拔了一下,按照这个设置方法就可以。2,重新加载ioc文件。

pvmsmfchcs的博客 1499

嵌入式工程师必学(52):储能系统ESS

能量存储系统(ESS,Energystoragesystems)存在于各种各样的尺寸、形状和技术中。储能系统的技术,即基本的储能机制,自然会影响其重要特性,包括成本、安全性、性能、可靠性和寿命。然而,尽管基础技术很重要,但一个成功的储能项目依赖于对该技术的彻底和深思熟虑的实施,以实现项目的目标。成功的实施取决于储能系统的架构和组装情况。系统的体系结构可以决定其性能和可靠性,与它所使用的技术一致,甚至与之无关。

码农17年的博客 1443

指针 数组 数组名和数组名取地址的区别

首先指针数组 和数组指针叫法本身是种误导 指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针 数组指针:a pointer to an array,即指向数组的指针 其次他们本质一个是数组,一个是指针 int* a[4]     表示是一个数组                      数组a中的元素都为int型指

浪子的专栏 2944

【Block总结】DiffAttention,差分注意力融入CLIP架构,抑制噪声,增强对相关特征的关注|即插即用

DiffCLIP通过微小的结构改动实现了显著的性能提升,为多模态模型的轻量化与高效化提供了新范式。

AI浩 1561

数组名和指针的区别和联系、数组名取地址&a

数组a和指针p的构成是很相似的。它们实际存的都是一个地址。但它俩自身又有不同,指针本身是一个对象,在内存中是为其分配了空间的;但数组名在内存空间中没有分配到空间(这将导致`&a`操作的效果可能和预想的不大一样)

anlian523的博客 3673

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

数组名和数组名取地址& 的区别

在C中, 在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址。 它的类型取决于数组元素的类型: 如果它们是int类型,那么数组名的类型就是“指向int的常量指针“。        在以下两种场合下,数组名并不是用指针常量来表示,就是当数组名作为sizeof操作符和单目操作符&的操作数时。 sizeof返回整个数组的长度,而不是指向数组的指针的长度。 取一

wangkeyen的专栏 4396

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【2015-02-16】【转】数组名和数组名取地址的区别

转载的C语言深入讲解,分析数组名a与&a在类型和值上的区别:a是首元素指针(int *),&a是整个数组的指针(int (*)[n]),两者值相同但类型不同,通过反汇编输出直观验证区别。

cstriker1407的专栏 90

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

上一篇: 关于数组
下一篇: Linux基本命令
SmartBrave
博客等级 码龄12年 25粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值