就编译器而言,
T *a;
T* a;
T*a;
T * a;
all 的意思完全相同——都被解释为T (*a)(a 的类型为“指向T”的指针)。在这种情况下,空格并不重要。对于您的 struct 类型,struct Card *p 和 struct Card* p 执行完全相同的操作,并且都被解释为 struct Card (*p)。
从句法上讲,* 总是绑定到 声明符(更多内容见下文)。这意味着像
这样的声明
T* a, b;
被解释为
T (*a), b;
并且仅将a 声明为指向T 的指针-b 是常规T。这是我建议反对使用T* p 样式的众多原因之一(我将在下面给出更多理由)。
在 C 中,声明有两个主要部分 - 声明说明符序列(类型说明符、struct、union 和 enum 说明符、存储类说明符、类型限定符等.) 后跟以逗号分隔的声明符列表。在像
这样的声明中
static unsigned long int a[10], *p, f(void);
声明说明符是static unsigned long int,声明符是a[10]、*p和f(void)。
声明器引入了被声明事物的名称(a、p 和 f)以及有关该事物的数组、指针和函数的信息。每个项目的类型完全由声明说明符和声明符的组合指定。
声明符的结构与代码中表达式的结构相匹配——如果你有一个指向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
因为后缀[] 和() 运算符的操作数 是a 和f,不是T。同样,在像
这样的声明中
T* p; // parsed as T (*p);
一元* 运算符的操作数是p,不是T。而* 运算符是一元(前缀),而不是后缀,所以T* 看起来是错误的无论如何。因为它是一元的,并且因为空格无关紧要,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;
改为。