按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
cha'2';
short
sBa'4';
}*p;
假设
p的值为
0x100000。如下表表达式的值分别为多少?
p
+0x1=
0x___?
(unsignedlong)p
+0x1
=0x___?
(unsignedint*)p+0x1
=
0x___?
我相信会有很多人一开始没看明白这个问题是什么意思。其实我们再仔细看看,这个知识点
似曾相识。一个指针变量与一个整数相加减,到底该怎么解析呢?
还记得前面我们的表达式“
a+1”与“
&a+1”之间的区别吗?其实这里也一样。指针变
量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是
byte而是元素的个数。所以:
p+0x1的值为
0x100000+sizof(Test)*0x1。至于此结构体的大小为
20byte,前面的章
节已经详细讲解过。所以
p+0x1的值为:0x100014。
(unsignedlong)p+0x1的值呢?这里涉及到强制转换,将指针变量
p保存的值强制转换
成无符号的长整型数。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就
是一个无符号的长整型数加上另一个整数。所以其值为:0x100001。
(unsignedint*)p+0x1的值呢?这里的
p被强制转换成一个指向无符号整型的指针。所
以其值为:0x100000+sizof(unsigned
int)*0x1,等于
0x100004。
上面这个问题似乎还没啥技术含量,下面就来个有技术含量的:
在
x86系统下,其值为多少?
intmain()
{
inta'4'={1;2;3;4};
int*ptr1=(int*)(&a+1);
int*ptr2=(int*)((int)a+1);
printf(〃%x;%x〃;ptr1'…1';*ptr2);
return0;
}
这是我讲课时一个学生问我的题,他在网上看到的,据说难倒了
n个人。我看题之后告诉他,
这些人肯定不懂汇编,一个懂汇编的人,这种题实在是小
case。下面就来分析分析这个问题:
根据上面的讲解,&a+1与
a+1的区别已经清楚。
ptr1:将&a+1的值强制转换成
int*类型,赋值给
int*类型的变量
ptr,ptr1肯定指到数
组
a的下一个
int类型数据了。
ptr1'…1'被解析成
*(ptr1…1),即
ptr1往后退
4个
byte。所以其
值为
0x4。
ptr2:按照上面的讲解,
(int)a+1的值是元素
a'0'的第二个字节的地址。然后把这个地址
强制转换成
int*类型的值赋给
ptr2,也就是说
*ptr2的值应该为元素
a'0'的第二个字节开始的
连续
4个
byte的内容。
其内存布局如下图:
好,问题就来了,这连续
4个
byte里到底存了什么东西呢?也就是说元素
a'0';a'1'里面
的值到底怎么存储的。这就涉及到系统的大小端模式了,如果懂汇编的话,这根本就不是问
题。既然不知道当前系统是什么模式,那就得想办法测试。大小端模式与测试的方法在第一
章讲解
union关键字时已经详细讨论过了,请翻到彼处参看,这里就不再详述。我们可以用
下面这个函数来测试当前系统的模式。
intcheckSystem(
)
{
unioncheck
{
int
i;
charch;
}c;
c。i
=1;
return(c。ch
1);
}
如果当前系统为大端模式这个函数返回
0;如果为小端模式,函数返回
1。
也就是说如果此函数的返回值为
1的话,*ptr2的值为
0x2000000。
如果此函数的返回值为
0的话,*ptr2的值为
0x100。
4。5,多维数组与多级指针
多维数组与多级指针也是初学者感觉迷糊的一个地方。超过二维的数组和超过二级的
指针其实并不多用。如果能弄明白二维数组与二级指针,那二维以上的也不是什么问题了。
所以本节重点讨论二维数组与二级指针。
4。5。1,二维数组
4。5。1。1,假想中的二维数组布局
我们前面讨论过,数组里面可以存任何数据,除了函数。下面就详细讨论讨论数组里
面存数组的情况。
Excel表,我相信大家都见过。我们平时就可以把二维数组假想成一个
excel
表,比如:
chara'3''4';
4。5。1。2,内存与尺子的对比
实际上内存不是表状的,而是线性的。见过尺子吧?尺子和我们的内存非常相似。一
般尺子上最小刻度为毫米,而内存的最小单位为
1个
byte。平时我们说
32毫米,是指以零
开始偏移
32毫米;平时我们说内存地址为
0x0000FF00也是指从内存零地址开始偏移
0x0000FF00个
byte。既然内存是线性的,那二维数组在内存里面肯定也是线性存储的。实
际上其内存布局如下图:
以数组下标的方式来访问其中的某个元素:a'i''j'。编译器总是将二维数组看成是一个
一维数组,而一维数组的每一个元素又都是一个数组。a'3'这个一维数组的三个元素分别为:
a'0';a'1';a'2'。每个元素的大小为
sizeof(a'0');即
sizof(char)*4。由此可以计算出
a'0';a'1';a'2'
三个元素的首地址分别为&a'0',&
a'0'+1*sizof(char)*4,&
a'0'+2*sizof(char)*4。亦即
a'i'
的首地址为&
a'0'+i*sizof(char)*4。这时候再考虑
a'i'里面的内容。就本例而言,a'i'内有
4
个
char类型的元素,其每个元素的首地址分别为&a'i',&a'i'+1*sizof(char),
&a'i'+2*sizof(char),&a'i'+3*sizof(char),即
a'i''j'的首地址为&a'i'+j*sizof(char)。再把
&a'i'
的值用
a表示,得到
a'i''j'元素的首地址为:a+i*sizof(char)*4+j*sizof(char)。同样,可以换
算成以指针的形式表示:*(*(a+i)+j)。
经过上面的讲解,相信你已经掌握了二维数组在内存里面的布局了。下面就看一个题:
#include
intmain(intargc;char*
argv'')
{
inta
'3''2'={(0;1);(2;3);(4;5)};
int*p;
p=a'0';
printf(〃%d〃;p'0');
}
问打印出来的结果是多少?
很多人都觉得这太简单了,很快就能把答案告诉我:0。不过很可惜,错了。答案应该
是
1。如果你也认为是
0,那你实在应该好好看看这个题。花括号里面嵌套的是小括号,而
不是花括号!这里是花括号里面嵌套了逗号表达式!其实这个赋值就相当于
inta
'3''2'={
1;3;
5};
所以,在初始化二维数组的时候一定要注意,别不小心把应该用的花括号写成小括号
了。
4。5。1。3,&p'4''2'
…&a'4''2'的值为多少?
上面的问题似乎还比较好理解,下面再看一个例子:
int
a'5''5';
int
(*p)'4';
p=a;
问&p'4''2'…&a'4''2'的值为多少?
这个问题似乎非常简单,但是几乎没有人答对了。我们可以先写代码测试一下其值,然后分
析一下到底是为什么。在
VisualC++6。0里,测试代码如下:
intmain()
{
inta'5''5';
int(*p)'4';
p=
a;
printf(〃a_ptr=%#p;p_ptr=%#pn〃;&a'4''2';&p'4''2');
printf(〃%p;%dn〃;&p'4''2'…&a'4''2';&p'4''2'…&a'4''2');
return0;
}
经过测试,可知&p'4''2'…&a'4''2'的值为…4。这到底是为什么呢?下面我们就来分析一下:
前面我们讲过,当数组名
a作为右值时,代表的是数组首元素的首地址。这里的
a为二
维数组,我们把数组
a看作是包含
5个
int类型元素的一维数组;里面再存储了一个一维数组。
如此,则
a在这里代表的是
a'0'的首地址。
a+1表示的是一维数组
a的第二个元素。
a'4'表
示的是一维数组
a的第
5个元素,而这个元素里又存了一个一维数组。所以&a'4''2'表示的
是&a'0''0'+4*5*sizeof(int)+2*sizeof(int)。
根据定义,p是指向一个包含
4个元素的数组的指针。也就是说
p+1表示的是指针
p向
后移动了一个“包含
4个
int类型元素的数组”。这里
1的单位是
p所指向的空间,即
4*sizeof(int)。所以,
p'4'相对于
p'0'来说是向后移动了
4个“包含
4个
int类型元素的数组”,
即&p'4'表示的是
&p'0'+4*4*sizeof(int)。由于
p被初始化为
&a'0',那么
&p'4''2'表示的是
&a'0''0'+4*4*sizeof(int)+2*
sizeof(int)。
再由上面的讲述,&p'4''2'和
&a'4''2'的值相差
4个
int类型的元素。现在,上面测试
出来的结果也可以理解了吧?其实我们最简单的办法就是画内存布局图:
这里最重要的一点就是明白数组指针
p所指向的内存到底是什么。解决这类问题的最
好办法就是画内存布局图。
4。5。2,二级指针
4。5。2。1,二级指针的内存布局
二级指针是经常用到的,尤其与二维数组在一起的时候更是令人迷糊。例如:
char
**p;
定义了一个二级指针变量
p。p是一个指针变量,毫无疑问在
32位系统下占
4个
byte。
它与一级指针不同的是,一级指针保存的是数据的地址,二级指针保存的是一级指针的地
址。下图帮助理解:
我们试着给变量
p初始化:
A),p
=
NULL;
B),char*p2;p
=
&p2;
任何指针变量都可以被初始化为
NULL(注意是
NULL,不是
NUL,更不是
null),二
级指针也不例外。也就是说把指针指向数组的零地址。联想到前面我们把尺子比作内存,
如果把内存初始化为
NULL,就相当于把指针指向尺子上
0毫米处,这时候指针没有任何内
�