我如何在 C 语言中声明一个结构体指针?

4

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

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;

谢谢您提前的支持。


1
即使以下方式,您也可以声明指针:struct Card *p = &my_card; 空格可以省略。 - Vlad from Moscow
4个回答

4
如果 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 * 类型。


1
就编译器而言,
T *a;
T* a;
T*a;
T      *      a;

所有的意思都完全相同-所有都被解释为T (*a)a的类型为“指向T的指针”)。在这种情况下,空格不重要。对于您的struct类型,struct Card *pstruct Card* p完全相同,并且两者都被解释为struct Card (*p)
从句法上讲,*始终与declarator(稍后会详细介绍)绑定在一起。这意味着像声明这样的声明
T* a, b;

被解释为。
T (*a), b;

仅将a声明为指向T的指针 - b是常规的T。 这是我建议不要使用T* p风格的众多原因之一(我将在下面给出更多原因)。

在C语言中,声明有两个主要部分 - 声明说明符序列(类型说明符,structunionenum说明符,存储类说明符,类型限定符等),后跟逗号分隔的声明符列表。 在像下面这样的声明中:

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[] p来强调变量的“数组性”。
T[N] a; // syntax error

或者说“功能性”作为
T(void) f; // syntax error

因为后缀运算符 []() 的操作数是 af,而不是 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;

代替。


1
无论您在哪里放置空格都没关系。struct Card* pstruct Card *p是同样有效的;只是风格问题,取决于您更喜欢哪种方式。
我同意第二种形式更为常见,但最重要的是您在整个代码中始终使用一种形式。您的老板/教师/其他开发人员可能还有编码风格标准,指定使用哪种形式。

1

在编程中,int *aint* a更有意义。在C语言中,*a可以被读作“a的内容”,因此int *a可以被读作“a的内容是一个整数”,与此类似,int a可以被读作“a是一个整数”。正如其他答案所解释的那样,这里空格并不重要-它们对编译器来说都是一样的。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接