我在SO上遇到了这个问题:(棘手的指针问题):
一个 C 程序员正在使用一台字节 8 位和 4 字节的小端机器一句话。编译器支持未对齐的访问,并使用 1、2 和 4 字节分别存储字符、短整型和整型。程序员编写以下定义(右下)来访问主内存中的值(左下):
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 的新手,并试图提高我对指针的理解,这特别令人困惑。感谢您的任何帮助!
您可能希望参考以下内容: C 和 C++ 中的运算符 - 运算符优先级
问题没有指定,但我们假设指针是 4 个字节 - sizeof(void*) == 4
.
1.
int **i=(int **)0x04;
**i = ???
i
是指向 (指向 int
的指针)。当我们说 **i
时,我们正在取消引用指针两次 - 或读取指针指向的值。
首先请注意,**i == *(*i)
.因此,我们首先从地址 0x04 处的内存中读取一个指针大小(4 字节)的值。这是10 00 00 00
- 解释为小端值,即0x10。所以现在我们只剩下*((int*)0x10)
.这意味着我们在地址 0x10 处从内存中读取一个 int
大小(4 字节)的值。 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处,是名为p->c
的char *
。
表达式p->c[2]
的意思是:"首先从p
指向的结构中获取char *c
。然后,从p->c
指向的数组中获取索引 2 处的char
。
所以首先,我们会得到p->c
.我已经提到过这个char*
在地址0x14。在那里我们找到指针 08 00 00 00
,或0x8。
现在,我们有一个指向寻址0x8的char *
,我们希望char
位于该数组中的索引 2 处。 为了获取数组元素的地址,我们使用以下公式:
&(x[y]) == (char*)x + (y * sizeof(x[0]))
换句话说,数组中第 n
个元素的偏移量(从数组的开头开始)是每个元素大小的 n
倍。
由于 char
是 1 个字节,因此 p->c[2]
位于 0x8 + 2 = 0xA
。在那里我们找到值 0x62
,即 ASCII 字符'b'
。
3.
short **pps=(short **)0x1c;
&(*pps)[1] = ???
使用我们的知识运算符优先级,我们将&(*pps)[1]
读作"第一次取消引用pps
",这是一个指向短线(或短线数组)的指针。然后,我们想要索引 1 处的元素地址。
在地址0x1C我们有18 00 00 00
,或0x18。所以现在我们有一个指向短裤数组的指针,这个数组从地址 0x18 开始。 使用上面的公式,并知道short
的大小为 2 个字节,我们计算元素 1 位于地址 0x18 + (1 * 2) == 0x1A
处。
地址 0x1A 是 4c 03
或 0x034C。 然而,问题不在于要求我们提供元素 1 的值——那将解决(*pps)[1]
。相反,它要求提供该元素&(*pps)[1]
或地址。因此,我们只需回到上一段的末尾,我们说地址是0x1A。
4.
++p->i = ??
对于这一点,您确实需要知道运算符优先级。应该明确的是,这可以有两种不同的解释方式:
- a) 将指针递增
p
1,然后取消引用p
以获取其成员i
- b) 取消引用
p
以获取其成员i
,然后递增整数值
从优先级图表中,我们看到->
的优先级为 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))字节"。