c指针和内存表示

c pointers and memory representation

我在 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 的新手,并试图提高我对指针的理解,这特别令人困惑。感谢您的帮助!

你可能想参考这个:Operators in C and C++ - Operator precedence

问题没有具体说明,但我们假设指针是 4 个字节 - sizeof(void*) == 4


1.

int **i=(int **)0x04;
**i = ???

i是一个指向a的指针(指向int的指针)。当我们说 **i 时,我们两次取消引用指针 - 或者读取指针指向的值。

首先注意**i == *(*i)。所以我们首先从地址 0x04 的内存中读取一个指针大小(4 字节)的值。这是 10 00 00 00 - 解释为小端值,即 0x10。所以现在我们剩下 *((int*)0x10)。这意味着我们从地址 0x10 的内存中读取了一个 int 大小(4 字节)的值。 78 0c 00 00 以小尾数法解释为值 0xC78。


2.

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]表示:"First get the char *c from the structure that p points to. Then, get the char at index 2 from the array that p->c points to."

所以首先,我们将得到 p->c。我已经提到这个 char* 位于地址 0x14。在那里我们找到指针 08 00 00 00,或 0x8.

现在,我们有一个指向地址 0x8 的 char *,我们需要该数组中索引 2 处的 char。要获取数组元素的地址,我们使用以下公式:

&(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] 读作 "First dereference pps, which is a pointer-to-short (or an array of shorts). Then, we want the address of the element at index 1."

在地址 0x1C 我们有 18 00 00 00,或 0x18。所以现在我们有一个指向 shorts 数组的指针,这个数组从地址 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,然后递增整数值

precedence chart 中,我们看到 -> 的优先级为 2,而 ++Prefix 增量)具有较低的优先级 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)) 个字节”。