c指针和内存表示

时间:2015-01-05 06:39:12

标签: c pointers

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

1 个答案:

答案 0 :(得分:3)

您可能想要参考:Operators in C and C++ - Operator precedence

问题没有说明,但我们假设指针是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字节)的值。在little-endian中解释的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处,是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'


3

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。


4

++p->i = ??

对于这个,你真的需要知道运营商的优先级。应该清楚的是,这可以通过两种不同的方式解释:

  • a)增加指针p 1 ,然后取消引用p以获取其成员i
  • b)取消引用p以获取其成员i,然后递增整数值

precedence chart,我们发现->的优先级为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))字节”。< /子>