【C/C++】一道试题,深入理解数组和指针
在x86平台下分析下面的代码输出结果
int main(void)
{
int a[] = {, , , };
int *ptr1=(int *)(&a+);
int *ptr2=(int *)((int)a+);
printf("%x, %x/n", ptr1[-], *ptr2);
return ;
}
&a+1
首先明确,a是一个具有4个整型变量的数组的名字,具体地说是这种数组的首元素的首地址,而&a是数组的首地址,请注意措辞。而关于指针加1,则需要指针运算的知识。
下面就是关于指针运算你需要知道的事实:
就像上面的例子那样,式子&a+1表示的是指针加法运算,而不是普通的数值加法运算,之所以会这样是因为&a是一个指针而非普通数值(虽然它本质上也是一个整数)。那么:假如此时&a=0xFFFF5700,那么&a+1是多少呢?答案是:取决于&a的类型 。
- 如果&a是一个指向char型的指针,那么&a+1 = 0xFFFF5701
- 如果&a是一个指向short型的指针,那么&a+1 = 0xFFFF5702
- 如果&a是一个指向int型的指针,那么&a+1 = 0xFFFF5704 (32位机器)
- 如果&a是一个指向某种结构体struct foo的指针,那么&a+1 = 0xFFFF5700+sizeof(struct foo)
……
指针加1不是指针内容简单地加1,而是让指针指向下一个数据 ,加2就是让指针指向下两个数据,这个数据的类型就是指针指向的类型,所以指针的加法究竟会让这个指针指向哪里,取决于这个指针指向的数据类型。
因此,综上所述,当&a与整数1做加法时,实际上是指针的加法,加1的含义是:令指针a指向下一个数据 ,下一个数据是啥?当然是紧挨着的下一个具有4个整型变量的数组了(因为&a的类型是指向具有4个整型变量的数组的指针嘛),于是a的指向了4的下一个地址,在用此值初始化ptr1,因此ptr1的指向如图所示:
由于在ptr1初始化的时候,令&a+1强制转换成整型指针,因此ptr1[-1]相当于把ptr1往前挪一个整型大小,即4个字节。 如下图:
显然,打印出的第一个数字是a[3]的内容,即数值4.
还必须说明一个事实:数组下标是可以为负数的,实际上,取下标符“[ ]”的内部实现,就是指针运算!比如a[2],等价于*(a+2),即以a地址为基址,取偏移量为2的地址的值。所以ptr1[-1]等价于*(ptr-1)。
(int*)((int)a+1)
先把数组名a强制转换成整型变量,然后再加1,然后再强制转换成整型指针!真罗嗦,但不要紧,咱有的是耐性,无非就是让ptr2指向a[0]的第二个字节罢了,此时ptr2指向如下图所示:

此时打印的内容就是ptr2所指向的往后4个字节的内容了,也就是a{0}的后三个字节和a[1]的第一个字节,那究竟会打印出啥玩意儿呢?
要把上图中每一个字节的内容都打印出来没问题,但是先要知道字节序的概念,字节序分两种,一种叫大端字节序(big-endian) ,当然除此之外必然有小端 字节序(little-endian)
官方解释:
- 所谓大端(big-endian)序,就是高优先位 对应高有效位 。
- 所谓小端(little-endian)序,就是高优先位 对应低有效位 。
民间解释:
- 所谓大端(big-endian)序,就是读取或者存放数据时,最低 位 对应 高地址 。
- 所谓小端(big-endian)序,就是读取或者存放数据时,最低 位 对应 低地址 。
回到原来的问题,此时ptr2指向了a[0]的第二个字节。以x86平台为例(小端序),此时其内部数据分布是这样的:
由于x86平台是小端序的,小端序的存取时最低位 对应 低地址 ,因此将会打印出0200 0000,如果没有说明在x86平台,那答案是不确定的,取决于具体的平台,例如ARM平台就是大端序的。
关于a 和&a
考虑这个定义:
int a[] = {, , , };
这时我们必须明确,编译器根据我们提供的类型和数组大小,为我们分配了适当大小的存储区域,并且把这块存储区域叫做a.
请注意 :
- &a,就像我们上面提到的,它的类型是“指向具有4个整型变量数组的指针“,简而言之&a是一个数组指针。
- 这个数组的名字a,当它作为右值时代表的是数组首元素的首地址,而不是数组的首地址。此时a的意义跟&a[0]是等价的,都是指向首元素的指针。
- 数组的名字会在另一个地方与指针等价,那就是函数参数表,当出现在函数的参数表里面的时候,不管写的是数组,还是指针,一旦进入函数内部,通通变成指针。
【C/C++】一道试题,深入理解数组和指针的更多相关文章
- C语言核心之数组和指针详解
指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...
- (C/C++)区别:数组与指针,指针与引用
1.数组跟指针的区别 数组要么在静态存储区被创建(如全局数组),要么在栈上被创建.数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变. 指针可以随时指向任意类型 ...
- C语言数组与指针总结
寒假要开始猛刷<剑指offer>,先回顾一下C语言基础做个热身. 指针 相信大家对下面的代码不陌生: ; int *p; p=&i; 这是最简单的指针应用,也是最基本的用法.再来熟 ...
- 一道试题引发的血案 int *ptr2=(int *)((int)a+1);
某日,看到一道比较恶心的C语言的试题,考了很多比较绕的知识点,嘴脸如下: int main(void) { int a[4] = {1, 2, 3, 4}; int *ptr1=(int *)(&am ...
- 力扣Leetcode 面试题56 - I. 数组中数字出现的次数
面试题56 - I. 数组中数字出现的次数 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度是O(n),空间复杂度是O(1). 示例 ...
- 从PHP底层源码去深入理解数组,并用C模拟PHP关联数组(原创)
PHP是一门入门容易,使用范围广泛的语言,以其灵活性以及web后端开发被很多人熟知,也被很多人戏称“PHP是世界上最好的语言”.本人是一名“忠实”的PHPer,相信用过PHP的程序员都会体会到PHP数 ...
- C语言教学--二维数组和指针的理解
对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ...
- 关于c语言二维数组与指针的个人理解及处理办法。
相信大家在学习C语言时,对一维数组和指针的理解应该是自信的,但是,我在学习过程中,看到网上一些博文,发现即便是参加工作的一些专业编程人员,突然碰到二维数组和指针的问题时,也可能会遇到难以处理的诡异问题 ...
- C语言数组和指针的理解_在取地址运算上的操作_指针加减操作_a 和&a 的区别
1.一个实例+理论分析 在了解数组和指针的访问方式前提下,下面再看这个例子: main() { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); pr ...
随机推荐
- mysql--SQL编程(关于mysql中的日期,实例,判断生日是否为闰年) 学习笔记2.1
关于日期处理的实例: 从mysql给出的 example 这个是官方源码下载以及导入,http://dev.mysql.com/doc/employee/en/employees-installati ...
- Redis 学习之路 (010) - redis命令手册
Redis 键(key) 命令 命令 描述 Redis DEL 命令 该命令用于在 key 存在是删除 key. Redis Dump 命令 序列化给定 key ,并返回被序列化的值. Redis E ...
- Mac 下查看网络端口占用情况
1.Mac 下查看网络端口占用情况 有的时候关闭了服务器,但是端口还是占用,解决的方法是 kill 掉占用该端口的进程. # 查看 8009 端口的占用情况 $ lsof -i:8009 可以看到,该 ...
- GIF Brewery for Mac(录制 Gif 动图工具)安装
1.软件简介 GIF Brewery 一款用于录制 Gif 动图等的工具. 2.资源列表 链接 提取密码 系统要求 软件语言 GIF Brewery for Mac v3.9.5 ltmf ma ...
- 批量 1 insert into select 2 sqldataadapter.update 3 SELECT INTO FROM (要求目标表不存在) 4 AddRange(ef) 5 MySqlBulkLoader.Load() 6 BatchInsert 7 insert bulk
insert into a(col1,col2) select top 1 '1','2' from a union select top 1 '3','4' from a SELECT IN ...
- PHP中数字检测is_numeric与ctype_digit的区别介绍
PHP中的两个函数is_numeric和ctype_digit都是检测字符串是否是数字,但也存在一点区别 is_numeric:检测是否为数字字符串,可为负数和小数 ctype_digit:检测字符串 ...
- xtrabackup 源码安装
安装依赖包:这些依赖包必须要先安装好 # yum install cmake libaio-devel ncurses-devel bzip2-devel libxml2-devel libgcryp ...
- State Threads 回调终结者
上回写了篇<一个“蝇量级”C语言协程库>,推荐了一下Protothreads,通过coroutine模拟了用户级别的multi-threading模型,虽然本身足够“轻”,杜绝了系统开销, ...
- 解决虚拟机vmware虚拟机安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题
前言 虚拟机使用的是VMware Workstation,并且首次在虚拟机体验64 位系统.在新建好虚拟机,运行时候就出现了VMware Workstation 的提醒:此主机支持 Intel VT- ...
- java 执行mysql 8.0.11存储过程报错The user specified as a definer ('root'@'10.%.%.%') does not exist解决办法
执行存储过程,报错 java.sql.SQLException: The user specified as a definer ('root'@'10.%.%.%') does not exist ...