你好,我有 3 个问题/3 行代码我不明白。 如果有人能帮助我,那就太好了。 我的理解仍然不是鸡蛋中的黄色。 这些问题被注释为//QUESTION:
#include <stdio.h>
#include <stdlib.h>
struct trace {
char *sign;
int *values;
struct trace *pN;
};
int main() {
int decimal[] = {4,2,1};
char text[]="Word-2!";
struct trace *pV;
pV = (struct trace*) calloc(2, sizeof(struct trace));
pV->pN = pV;
pV->values = decimal + decimal[2]; // = 2
// QUESTION: explanation why decimal + decimal[2] is 2 / what is decimal (not *decimal).
// My guess is: decimal[0 + decimal[2]] = decimal[0 + 1] = decimal[1] = 2
(*pV).sign = text + *decimal; // text + 4 //"-2!";
*(pV + 1) = pV[0]; // pV[1] = pV[0] = *pV
++pV[1].values; // QUESTION: what does this do? the ++ in front of pV instead of pV[++1].values
++*pV[1].values; // QUESTION: what does this do?
printf("%d %sn", *pV->values, pV->sign);
printf("%d %sn",*pV->pN[1].values, pV->pN[1].sign);
return 0;
}
编辑:这样做的目的是找出这 2 个 printf 中显示的内容,它们都是: "2-2!" "2-2!">
你难以理解该代码是有充分理由的;这很令人讨厌。 它试图说明指针和数组的一些怪异行为,但它以一种过于"棘手"且难以理解的方式完成。 它混合和匹配数组和指针表示法,并且在访问成员的方式上不一致。 这也不安全。 这是如何不编写 C 代码的一个很好的例子。
在我们开始之前,先介绍一下语法备忘单:
a[i] == *(a + i), therefore
a[0] == *(a + 0) == *a
p->m == (*p).m == (*(p + 0)).m == p[0].m
所以:
pV->values = decimal + decimal[2];
TL/DR- 这是将pV->values
(与(*pV).values
相同,与pV[0].values
相同)设置为指向decimals
数组的第二个元素;图形上,它看起来像这样:
+---+ +---+ +---+
pV:| | -----> | | pV[0].sign decimal: | 4 | decimal[0]
+---+ +---+ +---+
| | pV[0].values ----------> | 2 | decimal[1]
+---+ +---+
| | pV[0].pN | 1 | decimal[2]
+---+ +---+
| | pV[1].sign
+---+
| | pV[1].values
+---+
| | pV[1].pN
+---+
相当于写作
pV->values = &decimal[1];
在这种情况下,表达式decimal
从类型"int
的 3 元素数组"衰减"到"指向int
的指针"类型,表达式的值是数组第一个元素的地址(我们将在后面讨论为什么会这样)。 对于这个指针值,我们将存储在decimal[2]
中的值相加,即1
:
pV->values = decimal + 1;
将 1 添加到指针将生成指向指向类型的下一个对象的指针,该对象不一定是下一个字节;如果decimal[0]
的地址是0x8000
的,并且int
宽度为 4 个字节,则上面相加的结果是0x8004
,而不是0x8001
。
(*pV).sign = text + *decimal;
TL/DR - 这会将pV->sign
设置为指向text
字符串的"-"
字符;*decimal
与包含值4
的decimal[0]
相同,所以上面相当于
(*pV).sign = &text[4];
到程序的这一点上,我们遇到以下情况:
+---+ +---+ +---+
pV:| | --+--> | | pV[0].sign ---+ decimal: | 4 | decimal[0]
+---+ | +---+ | +---+
| | | pV[0].values ------------> | 2 | decimal[1]
| +---+ | +---+
+--- | | pV[0].pN | | 1 | decimal[2]
+---+ | +---+
| | pV[1].sign |
+---+ | +---+
| | pV[1].values | text: |'W'| text[0]
+---+ | +---+
| | pV[1].pN | |'o'| text[1]
+---+ | +---+
| |'r'| text[2]
| +---+
| |'d'| text[3]
| +---+
+----------> |'-'| text[4]
+---+
|'2'| text[5]
+---+
|'!'| text[6]
+---+
| 0 | text[7]
+---+
++pV[1].values;
TL/DR - 这会将pV[1].values
设置为指向decimal[2]
。
表达式++pV[1].values
解析为++(pV[1].values)
- 我们将 1 添加到pV[1].values
。 在程序的前面,我们将pV[0]
的内容复制到pV[1]
,并且我们设置了pV[0].values
指向decimal[1]
。 就像我上面说的,向指针添加 1 会生成指向指向类型的下一个对象的指针;因此,pV[1].values
现在指向decimal[3]
.
所以现在我们的图片是这样的:
+---+ +---+ +---+
pV:| | --+--> | | pV[0].sign ---+ decimal: | 4 | decimal[0]
+---+ | +---+ | +---+
| | | pV[0].values ------------> | 2 | decimal[1]
| +---+ | +---+
+--- | | pV[0].pN | +------> | 1 | decimal[2]
| +---+ | | +---+
| | | pV[1].sign ---+ |
| +---+ | | +---+
| | | pV[1].values -----+ text: |'W'| text[0]
| +---+ | +---+
+--- | | pV[1].pN | |'o'| text[1]
+---+ | +---+
| |'r'| text[2]
| +---+
| |'d'| text[3]
| +---+
+----------> |'-'| text[4]
+---+
|'2'| text[5]
+---+
|'!'| text[6]
+---+
| 0 | text[7]
+---+
++*pV[1].values;
TL/DR- 我们正在增加decimal[3]
的价值。
与前面的表达式类似,++*pV[1].values
解析为++(*pV[1].values)
。 我们不是在pV[1].values
上加 1,而是在pV[1].values
指向的事物上加 1,即decimal[3]
。 所以最后,在一切都说完了之后,我们的图片是这样的:
+---+ +---+ +---+
pV:| | --+--> | | pV[0].sign ---+ decimal: | 4 | decimal[0]
+---+ | +---+ | +---+
| | | pV[0].values ------------> | 2 | decimal[1]
| +---+ | +---+
+--- | | pV[0].pN | +------> | 2 | decimal[2]
| +---+ | | +---+
| | | pV[1].sign ---+ |
| +---+ | | +---+
| | | pV[1].values -----+ text: |'W'| text[0]
| +---+ | +---+
+--- | | pV[1].pN | |'o'| text[1]
+---+ | +---+
| |'r'| text[2]
| +---+
| |'d'| text[3]
| +---+
+----------> |'-'| text[4]
+---+
|'2'| text[5]
+---+
|'!'| text[6]
+---+
| 0 | text[7]
+---+
那么为什么数组表达式会"衰减"成指针表达式呢?
C 派生自早期名为 B 的编程语言 - 在 B 中,当您声明一个数组时,编译器将留出一个单独的字来存储数组第一个元素的偏移量。 鉴于声明
auto a[5];
你会在内存中有这样的东西:
+---+
a: | | ----------+
+---+ |
... |
+---+ |
| | a[0] <----+
+---+
| | a[1]
+---+
| | a[2]
+---+
| | a[3]
+---+
| | a[4]
+---+
数组下标操作a[i]
被定义为*(a + i)
- 给定一个存储在a
中的地址,从该地址偏移i
单词并取消引用结果。
Ritchie 想将 B 的数组行为保留在 C(a[i] == *(a + i)
中),但他不想存储行为所需的单独指针。 相反,我们有这个规则:
6.3.2.1 左值、数组和函数指示符C2011 在线草稿
...
3 除非它是sizeof
运算符、_Alignof
运算符或 一元&
运算符,或者是用于初始化数组的字符串文本,该表达式具有 类型"类型数组"转换为类型为"指向类型的指针"的表达式,该表达式指向 到数组对象的初始元素,并且不是左值。如果数组对象具有 注册存储类,行为未定义。
当您用 C 声明一个数组时,例如
int a[5];
你在内存中得到这个:
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
| | a[3]
+---+
| | a[4]
+---+
数组下标操作a[i]
仍然定义为*(a + i)
,但不是将指针值存储在名为a
的单独对象中,而是根据需要计算指针值。 因此,表达式decimal
和text
最终计算为指针值。
问题:解释为什么十进制 + 十进制[2] 是 2/什么是 十进制(不是*十进制).
//我的猜测是:十进制[0 + 十进制[2]] = 十进制[0 + 1] = 十进制1 = 2
严格来说,decimal + decimal[2]
并不2
.它是指针,指向包含2
的内存(因为简单decimal
指向decimal[0]
,并且添加到这个指针decimal[2]
,即1
,得到decimal[1]
,即2
)。
对于接下来的两个问题,查看运算符优先级表很有用。
++pV[1].values; // it's basically ++(pv[1].values), i.e. incrementing of the pointer 'values'
++*pV[1].values; // it's basically ++(*(pv[1].values)), i.e. incrementing of the integer value, pointed by the pointer 'values'