如何在 C 中声明指向结构的指针?

How can I declare a Pointer to a struct in C?

我了解到可以用 3 种不同的方式声明指针:

int* a;
int *b;
int * c;

我更喜欢:

int* a;

在声明一个指向结构体的指针时,这样写是否正确:

struct Card {
   int a;
};
struct Card my_card = { 3, 7 };
struct Card* p = &my_card; 
(*p).a = 8; 

我很困惑,因为我发现它到处都是这样声明的:

struct Card *p = &my_card;

提前致谢。

如果 T 是某种类型说明符,则指向类型 T 的对象的指针可以通过以下任何方式声明

T*p;
T* p;
T *p;
T * p;

例如,如果 Tint * 那么指针声明可以像

int **p;
int ** p;
int * *p;
int * * p;

与声明结构指针的方式相同

struct Card*p = &my_card;
struct Card* p = &my_card;
struct Card *p = &my_card;
struct Card * p = &my_card;

注意你可以写

T ( *p );

但你可能不会写

( T* ) p;

还有一个微妙之处。如果你会写例如

int* p1, p2

那么变量 p1 的类型为 int * 而变量 p2 的类型为 int 而不是 int *.

但是如果你会写

typedef int * T;

在此声明中

T p1, p2;

这两个变量的类型都是 int *

将空格放在哪里并不重要。 struct Card* pstruct Card *p 同样有效;喜欢哪一种只是风格的问题。

我同意第二种形式更常见,但最重要的是在整个代码中始终使用一种形式。您的老板/老师/其他开发人员也可能有指定使用哪种形式的编码风格标准。

IMO int *aint* a 更有意义。在C中,*a读作'content of a',所以int *a读作'content of a is an integer',同理int a读作'a是一个整数'。正如其他答案清除的那样,空格在这里并不重要 - 它们对编译器来说都是一样的。

这种写法在声明多个指针变量时也更有意义,如int *a, *b。编写 int* a, b 并期望 ab 都是 int 指针很容易出错。

就编译器而言,

T *a;
T* a;
T*a;
T      *      a;

都表示完全相同的意思 - 都被解释为 T (*a)a 的类型为“指向 T 的指针”)。在这种情况下,空格并不重要。使用您的 struct 类型,struct Card *pstruct Card* p 做完全相同的事情,并且都被解释为 struct Card (*p).

在句法上,* 总是绑定到 声明符 (更多内容见下文)。这意味着像

这样的声明
T* a, b;

被解释为

T (*a), b;

并且仅将 a 声明为指向 T 的指针 - b 是常规 T。这是我建议 反对 使用 T* p 样式的众多原因之一(我将在下面给出更多原因)。

在 C 中,声明有两个主要部分 - 一系列 声明说明符(类型说明符、structunionenum 说明符、存储 class 说明符、类型限定符等)后跟 声明符 的逗号分隔列表。在像

这样的声明中
static unsigned long int a[10], *p, f(void);

声明说明符是 static unsigned long int,声明符是 a[10]*pf(void)

声明符引入被声明事物的名称(apf)以及关于该事物的数组特性、指针特性和功能性。每个项目的类型完全由声明说明符和声明符的组合指定。

声明符的结构与代码中表达式的结构相匹配 - 如果您有一个指向 int 的指针数组并且您想要访问特定的 int 值,则需要使用一元 * 运算符 解引用 结果:

printf( "%d\n", *ap[i] );

表达式*ap[i]的类型是int,所以ap数组的声明是

int *ap[N];

声明符 *ap[N] 匹配表达式 *ap[i].

的结构

在声明中,*[] 以及 () 运算符仅用于指示类型 - 您实际上并没有取消引用或索引或调用任何东西。但是,它们确实遵循与表达式中相同的优先级规则。后缀运算符的优先级高于一元运算符,因此 []()*:

之前“绑定”
T *a[N];    // parsed as *(a[N]) -- a is an array of pointers
T *f(void); // parsed as *(f(void)) - f is function returning a pointer

要声明指向数组或函数的指针,您必须明确地将 * 运算符与数组或函数表达式组合在一起:

T (*a)[N];    // a is a pointer to an array
T (*f)(void); // f is a pointer to a function

现在是这个答案的编辑部分(您可以随意忽略)...

随着时间的推移,我越来越反对 T* p 风格的指针声明。它是 C++ 程序员的首选风格,但你想得越多,它就越没有意义,根据我的经验,它只会导致 问题 .

其声明的目的 - 强调变量的“指针性” - 是虚假的。您不能通过将变量声明为

来强调变量的“数组特性”
T[N] a; // syntax error

的“功能性”
T(void) f; // syntax error

因为后缀[]()运算符的操作数分别是af,不是T。同样,在

这样的声明中
T* p; // parsed as T (*p);

一元*运算符的操作数p不是T。而 * 运算符是 unary(前缀),而不是后缀,所以 T* 看起来是错误的 anyway。因为它是一元的,而且因为空格无关紧要 T* p 将按预期工作,但是当你这样做时你有点忽略了语言规则。

如果不出意外,它与声明指向数组的指针或指向函数的指针不一致:

T (*ap)[N];     // ap is a pointer to an N-element array of T
T (*fp)(void);  // fp is a pointer to a function returning T

并声明一个指向指针数组的指针,如

T* (*ap)[N];    // ap is a pointer to an array of pointers to T

很丑陋,只是表示思维混乱,就像我之前说的,你不能遵循数组或函数声明的约定:

T[N] a;    // syntax error
T(void) f; // syntax error

如果你使用它,你将不可避免地搞砸并写

T* a, b;

当你打算写的时候

T *a, *b;

现在,不可避免 对反对意见的回应是在不同的行上放置单独的声明:

T* a;
T* b;

是的,每行一个声明有很多充分的理由,但解决不良做法并不是其中之一。将这些声明写为

T *a;
T *b;

相反。