在C语言中,声明指针的正确语法是什么?

20

我模糊地记得之前在另一个问题的回答中见过这个,但搜索没有得到答案。

我不记得声明指针变量的正确方法是什么。是这样吗:

Type* instance;

或者:

Type *instance;

虽然我知道在大多数情况下这两种方式都可以编译,但我认为有一些例子是很重要的,可能与在同一行声明多个相同类型的变量有关,因此其中一种比另一种更合理。


8个回答

25

这仅仅取决于你更喜欢怎样阅读。

有些人之所以会这样表达:

Type *instance;

是因为代码说只有实例(instance)才是指针(pointer)。如果你有一组变量:

int* a, b, c;

只有a是指针,所以这样更容易

int *a, b, c, *d;

其中a和d都是指针,实际上没有区别,只是为了可读性。

其他人喜欢将*放在类型旁边,因为(除其他原因外)他们认为它是“指向整数的指针”,并认为*应该与类型一起出现,而不是变量。

个人来说,我总是这样做

Type *instance;

但这确实取决于你以及你公司或学校的代码风格指南。


我想这就是问题所在。它涉及到连续声明多个变量,而我无法确定编译器是否将 "Type*" 视为一种类型,还是 * 更像是一个修饰符,表示它是指向 "Type" 类型实例的指针变量。对于我来说,int* a,b,c 会让人感到困惑,因此如果没有其他指导方针,我会使用 Type *instance - Edd
我在C中使用int *,在C++中使用int*。在C++中,一般最好逐个声明标识符,因此参数“int* a, b, c是蓬松的”意义不大。类型系统的一致性在C++中更是一个重要问题。 - Alexandre C.

12

这两者是相同的。然而,如果您进行多次声明,前者可能会使您陷入困境。

int* pi, j;

声明了一个int指针 (pi) 和一个int (j)。


9

在C语言中,你可以以两种方式编写它,但是它总是被解析为

Type (*pointer);

也就是说,* 符号总是绑定到声明符上,而非类型说明符。如果你写成

Type* pointer1, pointer2;

如果需要解析,它会被解析为

Type (*pointer1), pointer2;

因此,只有pointer1被声明为指针类型。

C遵循“声明模仿使用”的范例;声明的结构应该尽可能地模仿代码中使用的等效表达式。例如,如果你有一个名为arr的指向int类型的指针数组,并且你想要检索数组中第i个元素指向的整数值,你会对数组进行下标运算并解引用结果,写作*arr[i](解析为*(arr[i]))。表达式*arr[i]的类型是int,因此arr的声明被写成:

int *arr[N];

这离右边近还是左边近?这要看问谁了。像我这样的老旧C工程师喜欢使用T *p,因为它反映出语法实际发生的情况。许多C++程序员更喜欢T* p,因为它强调了p的类型,在许多情况下感觉更自然(我自己写了容器类型后,也能明白这一点,尽管仍然感觉不对)。

如果您想声明多个指针,可以全部明确声明,例如:

T *p1, *p2, *p3;

或者您可以创建一个 typedef:

typedef T *tptr;
tptr p1, p2, p3;

虽然我个人不推荐这样做;但是,如果你不小心隐藏了某个东西的指针性质,那么使用typedef可能会给你带来麻烦。


使用typedef隐藏指针的特性在使用智能指针时非常方便。例如,编写MyClassPtr比编写boost::shared_ptr<MyClass>要快得多。 - Pedro d'Aquino
当你不得不在几百行的模板诊断中搜索时,这也是一件非常痛苦的事情,因为迭代器没有被正确地解引用;也就是说,你应该写 (*it)->foo() 而不是 it->foo(),但是由于迭代器被声明为 vector<a_typedef_name_that_doesn't_indicate_pointerness_AT_ALL>::iterator 而不是 vector<T*>::iterator,你没有意识到需要额外的解引用。那个下午并不是最有效率的。 - John Bode

3

这两个完全相同。

然而,在声明多个变量的情况下,如下所示:

int * p1, p2;

p1是指向整型的指针类型,而p2是整型类型。如果您希望将p2声明为指向整型的指针,您需要编写:

int *p1, *p2;

这是正确的,在 OP 所拥有的单变量声明情况下,但不适用于一般情况。 - Puppy

2
我更喜欢以下风格:
Type *pointer;

为什么?因为这符合语言创建者试图建立的思维方式:
“变量声明的语法模仿了变量可能出现的表达式的语法。”(《C程序设计语言》Brian W. Kernighan和Dennis M. Ritchie,ansi c版,第94页)
在任何语言中都可以使用FORTRAN编写代码,但您总是应该使用[编程语言X]来编写[编程语言X]。

1
所有这些都可以编译,都是合法的,并且等效:
Type* instance;
Type * instance;
Type *instance;

选择一种样式并保持一致。

1

有趣的是,在C++0x中如果类型可以被推断,则不适用此规则。

int a;
decltype(&a) i, j;

变量 i 和 j 都是 int* 类型。


2
我认为你不需要C++0x来实现这样的行为。只需定义一个 typedef int *IntPtr;,然后 IntPtr i, j; 就可以展示相同的行为。 - Philipp

0

顺便说一下,我认为理解C语言声明语法背后的动机很有帮助,这是为了模拟变量的使用方式。以下是一些示例:

  • char *x 表示如果你执行 *x,你会得到一个 char
  • int f(int x) 表示如果你执行例如 f(0),你会得到一个 int
  • const char *foo[3] 表示如果你执行例如 *foo[0](与 *(foo[0]) 相同),你会得到一个 const char。这意味着 foo 必须是一个指向 const char 的指针数组(在这种情况下大小为3)。
  • unsigned int (*foo)[3] 表示如果你执行例如 (*foo)[0],你会得到一个 unsigned int。这意味着 foo 必须是一个指向大小为3的 unsigned int 数组的指针。

大致上,一般的语法是这样的:[你得到什么] [你如何得到它]。诀窍在于,这可以扩展为[你得到什么] [你如何得到它],[你如何得到它],...以一次声明多个事物。因此,以下三个声明--

int *p;
int f(int x);
int a[3];

-- 可以合并为一行,如下所示(是的,这甚至适用于函数声明。在这方面它们并不特殊):

int *p, f(int x), a[3];

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