C 指针和内存表示



我在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->cchar *

表达式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,并且istruct i2c中的第一个成员,因此p->i位于地址 0x10。在那里我们找到78 0c 00 00,或0xC78。

最后,我们需要应用 ++ 运算符,并将该值递增到 0xC79。

.....

1 - 指针算术意味着您将指针视为数组。所以p + 3并不意味着"p加3个字节",而是意味着&p[3],或"p加(3 * sizeof(*p))字节"。

最新更新