我在尝试理解C编程中的指针时遇到了一些问题。
struct student {
int age;
char *name;
};
int main()
{
struct student b[3] = { 18, "Peter", 19, "Mary", 20, "John" };
struct student *p = b;
printf("%d\n", ++p->age);
printf("%s\n", (*p).name);
printf("%c\n", *p->name - 1);
printf("%c\n", *++p->name);
printf("%c\n", *p++->name);
printf("%c\n", *(++p)->name);
return 0;
}
我从visual studio获得的输出是:
19
Peter
0
e
e
J
对于前三行我理解它是如何工作的。但是,在*++p->name
它是怎么来的?'我认为指针已经指向玛丽,它应该是' a'以下应该是' a'以及它的后期修复。然后就*(++p)->name
而言,让我们说上一行让我回过头来,它是如何跳过玛丽并打印出来的。'。
我的任务是在没有运行程序的情况下将输出写在纸上,所以我想知道原因。提前谢谢。
答案 0 :(得分:1)
对于寻址操作"从后缀开始,继续前缀,并从里到外读取两组。"除非parens覆盖优先级,否则它们与其他运算符的工作方式相同。
因此*x->y
被解释为将x
视为指向struct的->
指针,查找其y
成员,然后将其视为指针{{1}取消引用它以获得最终值。
相反,*
被解释为将(*x)->y
视为指向pointef结构的指针,x
取消引用它以获取指向struct的指针,然后*
检索->
成员。
请注意,[]也是后缀运算符。所以y
检索第三个指向结构的指针,从中获取y,然后解除引用。
__有用提示:_ C声明的语法和优先级与C寻址完全相同。所以*x[3]->y
是一个指向四个整数数组的指针,而int (*x)[4];
是一个由4个指针组成的数组。
(多年前我为C声明编写了一个简单的递归下降解析器,它会将它们转换成这种类型的短语。正如任何老派黑客所说的那样,我称之为C EXplainer,或CEX,明显的发音。)
答案 1 :(得分:0)
*++p->name
相当于*(++(p->name))
。
此时,p
仍然指向b
的第一个元素,因此其内容为{ 18, "Peter" }
。
它接受name
指针,对其应用前缀增量,表达式求值为指向"Peter"
的第二个字符的指针。最后,解除引用运算符给出了字符'e'
。
在*(++p)->name
,p
指向b
的最后一个元素(在增量之后),因此其内容为{ 20, "John" }
。
它会使用name
指针并取消引用它,以便为"John"
提供'J'
的第一个字符。
修改:在每个printf
之后打印指针值可能会对您有所帮助,以帮助您了解输出。
答案 2 :(得分:0)
struct student b[3] = { 18, "Peter", 19, "Mary", 20, "John" };
创建一个struct student
数组,其中包含三个元素,这些元素取决于编译器和填充,将具有sizeof
12-16个字符(x86_64)或8-16个字符(x86),由{组成{1}}(int)和4-bytes
(char *)(x86上的4个字节)并且将具有类似的内存布局(例如用途):
8-bytes
在内存中创建单个字符串文字( int char*
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^
p
),并将指向每个字符串文字的指针存储为结构的各个元素中的"Peter", "Mary", "John"
。
使用name
运算符时,它是结构的解引用。使用->
上的预增量运算符,可以获得数组p->age
中的下一个age元素,但指针地址b
保持不变:
p
取消引用printf("%d\n", ++p->age);
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^ ^
p ++p->age
19
并使用p
(点)运算符,打印与'.'
中当前元素关联的名称:
b
当您从printf("%s\n", (*p).name);
Peter
中减去1
时,您正在按*p->name
递减字符指针,这会让您在 no-mans-land 中的内存中的地址处在字符串文字“彼得”之前的某个地方。
1
当你取消引用然后预增量时,(你正在递增指针printf("%c\n", *p->name - 1);
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^ \
p Peter (string literal in memory)
^
p->name - 1
0
)你在字符串文字“Peter”中向前移动一个字符:
p->name
当您下次取消引用,然后进行后递增时,您将打印相同的字符,然后将指针前进到printf("%c\n", *++p->name);
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^ \
p Peter
^
++p->name
e
中的第二个元素:
b
最后通过运算符优先级,您预先递增指针(将指针前进到printf("%c\n", *p++->name);
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^ \
p Peter
^
p->name
e
+----+--------+----+--------+----+--------+
|age |name* |age |name* |age |name* |
+----+--------+----+--------+----+--------+
^
p
中的第三个元素),然后将指针b
指向p->name
:
J