正确的阅读方式 'int * ptr=&x'

Proper way to read 'int * ptr=&x'

下面的代码很容易理解为

int x=4;
int *ptr;  // ptr is a pointer to an int
ptr=&x; // the address of x  is assigned to the pointer ptr

但是下面的等价代码,可能读起来像

int *ptr=&x; //  the address of x is assigned to  *ptr, which is an integer

也就是说它似乎被解释为将x的地址分配给指向的值*ptr (int *ptr )= &x 正确的解释是声明和初始化分开时应该写成类似 int *(ptr = &x) 的形式,以表明赋值是指向指针而不是指向的位置,但这会产生错误,这是为什么呢?阅读和思考 int *ptr=&x 的最佳方式是什么?

int * 是类型,所以它很有意义。只是 C 中的指针表示法需要一些努力才能习惯。但是看看这段代码

typedef int* int_ptr;
int x;
int_ptr ptr = &x;

同样的事情。但是,通常建议不要使用 typedef 指针。

混淆来自于 * 有两个角色。它既可以命名类型,也可以取消引用指针。第一个例子是 sizeof(int*)

如果你一次声明多个指针,它看起来更乱,但它仍然是一样的。通常,建议一次不要声明多个指针。因为如果我们想用两个指针做上面的事情,它看起来像这样:

int *pa = &x, *pb = &x;

这与

相同
int *pa = &x;
int *pb = &x;

这会做一些完全不同的事情,并且会生成警告,因为您将变量的地址分配给类型为 int

pb 变量
int *pa = &x, pb = &x;

但是,使用上面的 typedef,您可以(但可能不应该)这样做:

int_ptr pa=&x, pb = &x;

但一种思考方式是,在任何情况下取消引用未初始化的指针都毫无意义。

And what is the best way to read and think of int *ptr=&x?

假设取消引用未初始化的指针是没有意义的。您正在对指针进行初始化,因此您应该使用(有效)地址对其进行初始化。

Ok, I see that, another thing. If the type is int* why is it almost always written like with the * next to the pointer variable instead, like int *ptr?, it would make more sense, even if it is the same to write it like int* ptr.

因为,如果一次声明多个指针,那么忘记星号会容易得多。这会给人一种印象,即 int* p,q; 声明了两个指针。

C 指针语法笨拙。这是一种非常古老的语言。习惯就好了。它永远不会改变。只是为了好玩,这是一个可以告诉声明是什么的页面 https://cdecl.org/ 所以试试这些:

int (*p)[3]
int *p[3]
const int *p[3]
int *const p[3]
const int (*p)[3]
int (*const p)[3]

你可以写

int ( *ptr ) = &x;

甚至喜欢

int ( * ( ptr ) ) = &x;

虽然这个记录

int *ptr = &x;

对于每个C程序员来说都很清楚,因为它是声明而不是赋值语句。此外,在 C 中,与 C++ 声明相反,声明不是语句。

即 1) 您不能像您写的那样将声明括在括号中 (int *ptr )= &x 和 2) 您可以将声明符括在括号中。

如果按照你的逻辑,那么你应该写这样的声明

int *p = ( p = &x );

这使得声明更加混乱。:)

这是一个演示程序,展示了指针声明和初始化的一些示例。

#include <stdio.h>

int main(void) 
{
    int x = 10;
    
    int *p1 = &x;
    int ( *p2 ) = &x;
    int ( *( p3 ) ) = &x;
    
    int typedef *T;
    
    T ( p4 ) = &x;
    
    printf( "*p1 = %d\n", *p1 );
    printf( "*p2 = %d\n", *p2 );
    printf( "*p3 = %d\n", *p3 );
    printf( "*p4 = %d\n", *p4 );

    return 0;
}

程序输出为

*p1 = 10
*p2 = 10
*p3 = 10
*p4 = 10

请注意,在 C 中,与在许多其他语言中一样,一些符号被重载,它们的含义取决于上下文。例如,符号 & 可以表示运算符的地址和按位与运算符,顺便说一句,在 C++ 中,这个符号也可以表示引用。

比较以下。

int n = 5;     // (a) defines 'int' variable 'n' and initializes it to '5'

int *p;        // (b) defines 'int*' pointer variable 'p`
p = &n;        //     initializes 'p' to '&n'

int *p1 = p;   // (c) defines 'int*' pointer variable 'p1' and initializes it to 'p' 
               //     syntactically, it looks just like (a), but for a pointer type

int *p2 = &n;  // (d) same as (b) and (c) combined into a one-liner

int n3 = 7, *p3 = &n3;  // (e) same as (a), (b) and (c) combined into a one-liner

诚然,这是 C 的一个奇怪部分,主要是由于 C 的演变。

在 C 的语法中,声明旨在镜像使用,因此在 int *x; 之后,*x 解析为 int 并且 *x=42 分配给 int.但是在声明的 specifiers declarator 部分之后以 = 开始的初始化在句法和语义上与赋值不同。

(初始化可以“赋值”给 static/filescope 变量,这样的赋值不会生成代码:它只是有助于生成二进制文件的构成。赋值总是生成代码,除非优化可以删除它)

在史前 C 语言中,初始化和赋值看起来非常不同,在 C 语言中,您可以在没有 = 符号的情况下进行初始化,就像在 int x 42; 中一样。 (即使在 = 被添加到初始化语法之后,很长时间以来都不可能初始化局部的、非静态的变量,这意味着像 int *p = &x; 这样的情况并不经常出现。)

旧语法有问题(int x (42); 也会声明和初始化 x 还是函数声明?)这就是它被替换的原因,但我喜欢它强调初始化的方式与作业不同。

不幸的是,在新语法中(如 int x, *p = &x;),这种区别并不那么明显,您只需要记住,当您在左端有类型说明符 (int) 时,那么 = 并不表示您只能查看 *p = &x 的赋值,而是表示您必须查看整个声明的 初始化 和查看声明的内容(x 作为 intp 作为指向 int 的指针)。该上下文中的 = 然后初始化声明的标识符。

当声明一个指针时,int p 等于 int p; int* 是一种类型。