为什么解除引用运算符(*)也用于声明指针?

dig*_*ire 53 c++ syntax pointers notation dereference

我不确定这是否是一个正确的编程问题,但它总是困扰我,我想知道我是否是唯一的.

在最初学习C++时,我理解了引用的概念,但指针让我感到困惑.你为什么问?因为你如何声明一个指针.

考虑以下:

void foo(int* bar)
{
}


int main()
{
    int x = 5;
    int* y = NULL;

    y = &x;
    *y = 15;     
    foo(y);

}
Run Code Online (Sandbox Code Playgroud)

该函数foo(int*)int指针作为参数.由于我已经声明yint指针,我可以传递yfoo,但是当我第一次学习C++时,我将*符号与解除引用相关联,因此我认为int需要传递一个解引用.我会试图通过*y进入foo,这显然是行不通的.

用一个单独的运算符来声明指针会不会更容易?(或用于解除引用).例如:

void test(int@ x)
{
}
Run Code Online (Sandbox Code Playgroud)

Cra*_*rks 84

C语言的发展中,Dennis Ritchie解释了他的推理:

第二个最明显区别C与其前辈的创新是这种更全面的类型结构,特别是它在声明语法中的表达......给定一个任何类型的对象,应该可以描述一个新的对象,它将几个聚集到一个数组中,从函数中产生它,或者是指向它的指针.... [This] 导致了一个声明语法,用于反映名称通常出现的表达式语法的名称.从而,

int i, *pi, **ppi;声明一个整数,一个指向整数的指针,一个指向整数指针的指针.这些声明的语法反映了在表达式中使用时i, *pi, and **ppi都会产生int类型的观察结果.

类似地,int f(), *f(), (*f)();声明一个返回整数的函数,一个返回指向整数的指针的函数,一个返回整数的函数的指针.int *api[10], (*pai)[10];声明一个指向整数的指针数组,以及一个指向整数数组的指针.

在所有这些情况下,变量的声明类似于在表达式中的用法,该表达式的类型是在声明的头部命名的类型.

语法意外导致了语言的复杂性.在C语言中拼写为*的间接运算符在语法上是一元前缀运算符,就像在BCPL和B中一样.这在简单表达式中很有效,但在更复杂的情况下,需要使用括号来指导解析.例如,为了区分间接通过函数返回的值与调用指针指定的函数,*fp() and (*pf)() 分别写入.表达式中使用的样式贯穿于声明,因此可以声明名称

int *fp(); int (*pf)();

事情变得更加华丽但仍然很现实,事情变得更糟:int *(*pfp)();是指向函数的指针,返回指向整数的指针.

发生了两种效应.最重要的是,C有一套相对丰富的描述类型的方法(比如用Pascal比较).例如,像C-Algol 68那样富有表现力的语言中的声明描述同样难以理解的对象,仅仅因为对象本身很复杂.第二个效果归功于语法的细节.C中的声明必须以"由内而外"的方式阅读,许多人都难以理解.Sethi [Sethi 81]观察到,如果将间接运算符作为后缀运算符而不是前缀,许多嵌套的声明和表达式会变得更简单,但到那时为时已经太晚了.

  • @diggingforfire:我希望你在那里使用句号而不是逗号:( (4认同)
  • 这是一个很好的解释,我希望我们实际上在课堂上被告知这些事情,它会帮助我更快地理解指针. (3认同)
  • @Tomalak Geret'kal:糟透了,太糟糕了我现在无法编辑它.嗯,这些事情确实发生在用外语写作时. (2认同)
  • 这句话的哪一部分解释了为什么选择前缀 * 而不是替代品? (2认同)

Stu*_*etz 14

如果你这样写的原因更清楚:

int x, *y;
Run Code Online (Sandbox Code Playgroud)

也就是说,x和*y都是整数.因此y是int*.

  • 可能不是世界上最清晰的答案,但在iPad上打字因此比平常更短.我试图提出的一点是,如果你用变量名对*进行分组,那么这个语法来自哪里变得更加清晰 - 即通过说*y是一个int,你暗示y本身就是一个int*.由于这个答案已经在实践中通过给出的参考证实了(并且它不是猜测),我不确定敌意来自何处.无论如何,让我们同意不同意. (3认同)

Dav*_*eas 10

这是一个早于C++的语言决策,因为C++从C继承了它.我曾经听说过,动机是声明和使用是等价的,也就是说,给定一个声明int *p;,表达式*p的类型intint i;表达式i是类型int.


Lig*_*ica 6

因为委员会以及那些在标准化之前几十年内开发C++的委员会决定*应该保留其原有的三个含义:

  • 指针类型
  • 解引用运算符
  • 乘法

你是正确的,可以认为*(和,类似地&)的多重含义令人困惑.多年来我一直认为它是理解语言新手的重要障碍.


为什么不为C++选择另一个符号?

向后兼容性是根本原因......最好在新环境中重用现有符号,而不是通过将先前非运算符转换为新意义来破坏C程序.


为什么不为C选择另一个符号?

这是不可能确定的,但有几个论点可以 - 并且已经 - 制作.最重要的是:

当[an]标识符出现在与声明符相同的表达式中时,它会生成指定类型的对象.{K&R,p216}

这也是为什么C程序员倾向于[引用需要]更喜欢将他们的星号与右边而不是左边对齐,即:

int *ptr1; // roughly C-style
int* ptr2; // roughly C++-style
Run Code Online (Sandbox Code Playgroud)

虽然两种语言的程序都有不同的变化.

  • 在有一个委员会之前,Ritchie发明了这种语法.Stroustrup不会通过不使用它来破坏与C的兼容性.所有这些决定都是在C和C++的开发被置于标准组织之前做出的. (8认同)
  • 我认为委员会没有做出任何决定; 可能是Dennis Ritchie在创建C. (3认同)
  • @BrianNeal:很好,但我们所知道的语言由这些委员会管理,而这些委员会 - 目前 - 负责该决定.除非你想归功于原始汤中的第一种蛋白质. (3认同)

sar*_*old 5

专家C编程的第65页:Deep C Secrets包括以下内容:然后,C哲学认为对象的声明应该看起来像它的用法.

C编程语言第216页(又名K&R)包括:声明符被读作断言,当它的标识符出现在与声明符相同的表达式中时,它产生一个指定类型的对象.

我更喜欢范德林登所说的方式.


Bin*_*ian 5

哈哈,我感觉到你的痛苦,我遇到了完全相同的问题。

我认为应该声明一个指针,&int因为指针是某物的地址是有道理的。

一段时间后,我自己思考,C 中的每个类型都必须向后阅读,例如

int * const a

是给我的

a constant something, when dereferenced equals an int. 必须取消引用的东西,必须是一个指针。