Head First C:勘误表?指针语法

Head First C: Erratum? Pointer Syntax

我正在使用 O'Reilly 出版的 "Head First C" 自学 C 编程。 (此外还有一些其他文本。)

在本书介绍指针的过程中,第 58 页的示例程序让我非常困惑。令我震惊的是文本中的一点标注,指向该行:

int *choice = contestants;

标注内容如下:

“choice” is now the address of the “contestants” array.

据我所知,这是错误的。该行将 *choice 分配给存储为 contestants 数组的 ,不是吗?

免责声明:我现在无法访问本书/章节。

考虑到contestants是数组类型,引用C11,章节§6.3.2.1(强调我的

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [...]

在您的例子中,数组变量用作赋值运算符的 RHS,因此在这种情况下 contestants&(contestants[0]) 相同。

这与 int * 的类型完全相同,与要分配的变量 (LHS) 相同。

您可能不知道的是,数组的名称是指向该数组第一个元素的指针。示例

int contestants[] = {1, 2, 3};

表示&contestants[0]contestants

的值相同

这在本章前面的 要点 列表中进行了解释。

  • An array variable can be used as a pointer.
  • The array variable points to the first element in the array.

并且在标有 POINTER DECAY:

的框中更进一步

Because array variables are slightly different from pointer variables, you need to be careful when you assign arrays to pointers. If you assign an array to a pointer variable, then the pointer variable will only contain the address of the array.

这本书是正确的。指针与变量的工作方式不同,* 在不同情况下的工作方式也不同。

首先,您可能知道指针只是一个变量,其中包含指向另一个项目在内存中的位置的地址

当我们使用 int *choice 我们创建一个指向整数的指针,它存储整数的地址

在这种情况下,我假设参赛者是一个数组。我们假设它是一个整数数组

int contestants[5] = [0,1,2,3,4];

集合(例如数组)始终存储为指向第一个元素的指针。因此第二个元素是 addressof_first_item + sizeof(element_stored).

假设地址是0x12345,就是这样int contestants = 0x12345

现在我们选择存储该地址: int *choice = contestants

换句话说,它是一个指向整数的指针,它恰好是参赛者数组中的第一个元素。

作为旁注,我们也可以将其写为:

int* choice = &contestants[0];

关于指针的 * 运算符的另一个用例是取消引用。取消引用涉及获取指针指向的地址处的值。

int value = *choice;

会给我们一个值为0的整数,因为存储的地址指向第一个元素

所以你已经有了四个答案,但是 none 其中提到了 assignmentinitialization 语法之间的区别。

int *choice = contestants;  // initialization

以上等同于:

int *choice;                // declaration
choice = contestants;       // assignment to pointer

但与此完全不同(无法在初始化中完成):

int *choice;                // declaration
*choice = contestants;      // assignment to target (not valid unless choice is initialized)

第二个版本写的不正确,因为它试图将 contestants 的值存储在 choice 指向的地址——但 choice 不包含地址,除非你初始化它。取消引用可能未初始化的指针总是错误的。

严格来说,这本书不正确,但你的推理也不正确。

假设contestants是一个int的数组,初始化

 int *choice = contestants;

等同于

 int *choice = &contestants[0];

也就是说choice的值是contestants的第一个元素的地址。这种从 contestants(数组名称)到 &contestants[0](指针)的转换通常称为 "array to pointer" 转换。

然而,这与contestants数组的地址不同,因为类型是错误的。 contestants 数组的地址是 &contestants,其类型为 int (*)[4](即四个 int 数组的地址)。

如果您尝试初始化

 int *choice = &contestants;

结果将是编译错误(因为 int(*)[4] 不能隐式转换为 int *)。

你的推理

And, as far as I can tell, that's wrong. That line assigns *choice to the value stored as the contestants array, is that not so?

有缺陷,因为 contestants 没有存储值。这是一组值。

确实&contestants[0]&contestants的值相同,但它们的类型也不同。所以 &contestant[0] == &contestants 的比较不会编译。但是,如果将值转换为通用类型,例如 (void *)(&contestants[0]) == (void *)(&contestants),代码将编译,并且转换后的值比较相等。