我在SO上遇到了这个问题: (Tricky pointer question):
C程序员正在使用一个小端机器,每个字节有8位,4个字节 一句话。编译器支持未对齐访问,并使用1,2和4字节分别存储char,short和int。程序员编写以下定义(右下图)来访问主存储器中的值(左下方):
Address | Byte offset
--------|-0--1--2--3----
0x04 | 10 00 00 00
0x08 | 61 72 62 33
0x0c | 33 00 00 00
0x10 | 78 0c 00 00
0x14 | 08 00 00 00
0x18 | 01 00 4c 03
0x1c | 18 00 00 00
int **i=(int **)0x04;
short **pps=(short **)0x1c;
struct i2c {
int i;
char *c;
}*p=(struct i2c*)0x10;
(a)记下以下C表达式的值:
**i
p->c[2]
&(*pps)[1]
++p->i
这个问题只回答了第三个问题,但我想知道其余的子问题将如何解决。我是C的新手,并试图提高我对指针的理解,这尤其令人困惑。谢谢你的帮助!
答案 0 :(得分:3)
您可能想要参考:Operators in C and C++ - Operator precedence
问题没有说明,但我们假设指针是4个字节 - sizeof(void*) == 4
。
int **i=(int **)0x04;
**i = ???
i
是指向(int
的指针)的指针。当我们说**i
时,我们两次取消引用指针 - 或者读取指针指向的值。
首先请注意**i == *(*i)
。所以我们首先从地址0x04的内存中读取指针大小(4字节)的值。这是10 00 00 00
- 解释为小端值,即0x10。所以现在我们留下了*((int*)0x10)
。这意味着我们从地址0x10的内存中读取int
大小(4字节)的值。在little-endian中解释的78 0c 00 00
是值0xC78。
struct i2c {
int i;
char *c;
} *p = (struct i2c*)0x10;
p->c[2] = ???
这个有点棘手。我假设您理解结构只是变量的集合(不包括填充,这里不适用)在内存中一个接一个地布局。
我们的指针p
指向内存为0x10的struct i2c
对象。这意味着在地址0x10处是int
,名为p->i
。紧接着,在地址0x14处,是char *
,名为p->c
。
表达式p->c[2]
表示:“首先从char *c
指向的结构中获取p
。然后,从char
的数组中获取p->c
{1}}指向。“
首先,我们会得到p->c
。我已经提到这个char*
位于地址0x14。在那里我们找到指针08 00 00 00
或0x8。
现在,我们有char *
指向地址0x8,我们希望该数组中索引为2的char
。要获取数组元素的地址,我们使用以下公式:
&(x[y]) == (char*)x + (y * sizeof(x[0]))
换句话说,数组中n
元素的偏移量(从数组开始)是每个元素大小的n
倍。
由于char
是1个字节,p->c[2]
位于0x8 + 2 = 0xA
。我们找到值0x62
,即ASCII字符'b'
。
short **pps=(short **)0x1c;
&(*pps)[1] = ???
使用我们的知识运算符优先级,我们将&(*pps)[1]
读作“第一个取消引用pps
,这是一个指向短指针(或短数组)。然后,我们想要 索引1处元素的地址。“
在地址0x1C,我们有18 00 00 00
或0x18。所以现在我们有一个指向short数组的指针,这个数组从地址0x18开始。使用上面的公式,并且知道short
的大小为2个字节,我们将元素1计算为地址0x18 + (1 * 2) == 0x1A
。
地址0x1A为4c 03
或0x034C。然而,问题并不是要求我们提供元素1的价值 - 这将解决(*pps)[1]
。相反,它要求&(*pps)[1]
或地址该元素。所以,我们只需回到上一段的末尾,我们说地址是0x1A。
++p->i = ??
对于这个,你真的需要知道运营商的优先级。应该清楚的是,这可以通过两种不同的方式解释:
p
1 ,然后取消引用p
以获取其成员i
p
以获取其成员i
,然后递增整数值从precedence chart,我们发现->
的优先级为2,而++
(前缀增量)的优先级低于3那意味着我们需要首先应用->
,然后再增加。因此,选项 b)是正确的。
所以,首先让我们得到p->i
。我们已经部分地说过 2。,因为p
指向地址0x10,而i
是struct i2c
中的第一个成员,p->i
位于地址0x10。我们找到78 0c 00 00
或0xC78。
最后,我们需要应用++
运算符,并将该值增加到0xC79。
.....
1 - 指针算术意味着您将指针视为数组。因此p + 3
并不意味着“p
加上3个字节”,它意味着&p[3]
或“p
加(3 * sizeof(* p))字节”。< /子>