就编译器而言,
T *a;
T* a;
T*a;
T * a;
所有的意思都完全相同-所有都被解释为
T (*a)
(
a
的类型为“指向
T
的指针”)。在这种情况下,空格不重要。对于您的
struct
类型,
struct Card *p
和
struct Card* p
完全相同,并且两者都被解释为
struct Card (*p)
。
从句法上讲,
*
始终与
declarator(稍后会详细介绍)绑定在一起。这意味着像声明这样的声明
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];
T *f(void);
为了声明一个指向数组或函数的指针,你必须明确地将
*
运算符与数组或函数表达式分组:
T (*a)[N]
T (*f)(void)
现在是回答的编辑部分(你可以忽略)……
随着时间的推移,我变得更加强硬,不支持
T* p
指针声明的风格。这是C++程序员首选的风格,但越想它就越没有意义,在我的经验中只会导致问题。
它所声明的目的 - 强调变量的“指针性” - 是站不住脚的。你不能通过将其声明为
T[] p
来强调变量的“数组性”。
T[N] a
或者说“功能性”作为
T(void) f
因为后缀运算符
[]
和
()
的操作数是
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
T(void) f
如果你使用它,你将不可避免地犯错误并写下
T* a, b;
当你想要写成
时
T *a, *b;
现在,对于这个反对意见的
不可避免回应是将单独的声明放在单独的行上:
T* a;
T* b;
是的,每行只有一个声明有很多好处,但是为了解决不良实践而这样做并不是其中之一。将这些声明写成:
T *a;
T *b;
代替。