如何在 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;
例如,如果 T
是 int *
那么指针声明可以像
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* p
和 struct Card *p
同样有效;喜欢哪一种只是风格的问题。
我同意第二种形式更常见,但最重要的是在整个代码中始终使用一种形式。您的老板/老师/其他开发人员也可能有指定使用哪种形式的编码风格标准。
IMO int *a
比 int* a
更有意义。在C中,*a
读作'content of a
',所以int *a
读作'content of a
is an integer',同理int a
读作'a
是一个整数'。正如其他答案清除的那样,空格在这里并不重要 - 它们对编译器来说都是一样的。
这种写法在声明多个指针变量时也更有意义,如int *a, *b
。编写 int* a, b
并期望 a
和 b
都是 int 指针很容易出错。
就编译器而言,
T *a;
T* a;
T*a;
T * a;
都表示完全相同的意思 - 都被解释为 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
说明符、存储 class 说明符、类型限定符等)后跟 声明符 的逗号分隔列表。在像
这样的声明中
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
。而 *
运算符是 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;
相反。
我了解到可以用 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;
例如,如果 T
是 int *
那么指针声明可以像
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* p
和 struct Card *p
同样有效;喜欢哪一种只是风格的问题。
我同意第二种形式更常见,但最重要的是在整个代码中始终使用一种形式。您的老板/老师/其他开发人员也可能有指定使用哪种形式的编码风格标准。
IMO int *a
比 int* a
更有意义。在C中,*a
读作'content of a
',所以int *a
读作'content of a
is an integer',同理int a
读作'a
是一个整数'。正如其他答案清除的那样,空格在这里并不重要 - 它们对编译器来说都是一样的。
这种写法在声明多个指针变量时也更有意义,如int *a, *b
。编写 int* a, b
并期望 a
和 b
都是 int 指针很容易出错。
就编译器而言,
T *a;
T* a;
T*a;
T * a;
都表示完全相同的意思 - 都被解释为 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
说明符、存储 class 说明符、类型限定符等)后跟 声明符 的逗号分隔列表。在像
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
。而 *
运算符是 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;
相反。