在C/C++中编写"指向某事物的指针"的好方法

Fra*_*ger 3 c c++ pointers typography

在C/C++中编写"指向某事物的指针"是否有"好"的方法?
我用来写void foo( char *str );但有时我发现它非常不合逻辑,因为类型str是"指向char的指针",那么它应该更合乎逻辑地附加*到类型名称.
写指针有规则吗?

char*str;
char* str;
char *str;
char * str;
Run Code Online (Sandbox Code Playgroud)

DrA*_*rAl 22

没有严格的规则,但请记住*附加到变量,所以:

char *str1, *str2; // str1 and str2 are pointers
char* str1, str2;  // str1 is a pointer, str2 is a char
Run Code Online (Sandbox Code Playgroud)

有些人喜欢这样做char * str1,但这取决于您或您公司的编码标准.

  • 这确实是为什么许多指南建议将`*`附加到变量名称的原因.但是,我认为更好的做法是避免在同一行中声明几个变量:我发现它更清晰,更易于维护并且更不容易出错.在这种情况下,`*`的位置并不重要(人物,我在前后放置一个空格,它更容易发现指针(或引用),但这主要是品味问题). (5认同)
  • 这是在声明变量时不应使用","的众多原因之一.解决方案是*不*以适应某种指针声明语法,而是在自己的一行上声明每个变量.然后你不会有这个混淆的逗号声明语法引起的荒谬的新手错误. (2认同)

Joh*_*ode 9

常见的C约定是写入T *p,而常见的C++约定是写入T* p.两者都解析为T (*p); 它*是声明符的一部分,而不是类型说明符.这纯粹是指针声明语法的一个意外,你可以用它来编写它.

C(以及扩展,C++)声明语法是以表达为中心的 ; IOW,声明的形式应该与代码中相同类型的表达式的形式相匹配.

例如,假设我们有一个指针int,我们想访问该整数值.为此,我们使用间接运算符取消引用指针*,如下所示:

x = *p; 
Run Code Online (Sandbox Code Playgroud)

表达式 的类型*pint; 因此,p应该声明

int *p  
Run Code Online (Sandbox Code Playgroud)

int-ness p是由类型说明符提供的int,但指针式p是由声明符提供的*p.

作为一个稍微复杂的例子,假设我们有一个指向数组的指针float,并希望i通过指针访问数组的第th个元素的浮点值.我们取消引用数组指针并下标结果:

f = (*ap)[i];
Run Code Online (Sandbox Code Playgroud)

表达式 的类型(*ap)[i]float,所以它遵循数组指针的声明

float (*ap)[N];
Run Code Online (Sandbox Code Playgroud)

float-ness ap是由类型说明符提供的float,但指针-ness和array-ness由声明符提供(*ap)[N].请注意,在这种情况下,*必须明确地绑定到标识符; []具有比一元的优先级高*的表达和声明的语法,所以float* ap[N]将被解析为float *(ap[N]),或"指针数组float",而不是"指针的数组float".我想你可以把它写成

float(* ap)[N];
Run Code Online (Sandbox Code Playgroud)

但我不确定这是什么意思; 它没有ap任何更清晰的类型.

更好的是,指向函数的指针如何返回指向指针数组的指针int:

int *(*(*f)())[N];
Run Code Online (Sandbox Code Playgroud)

同样,至少有两个*运算符必须在声明符中明确绑定; 将最后一个绑定*到类型说明符,如

int* (*(*f)())[N];
Run Code Online (Sandbox Code Playgroud)

只是表明IMO思维混乱.

即使我在自己的C++代码中使用它,即使我理解为什么它变得流行,我对T* p公约背后的推理的问题是它只是不适用于最简单的指针声明之外,它强化了C和C++声明语法的简单到错误的观点.是的,类型p是"指向T的指针",但这并没有改变这样一个事实,即语言语法*与声明符绑定,而不是类型说明符.

对于另一种情况,如果类型a是"N元素数组T",我们

T[N] a;
Run Code Online (Sandbox Code Playgroud)

显然,语法不允许它.同样,这个论点在这种情况下并不适用.

编辑

正如Steve在评论中指出的那样,你可以使用typedef隐藏一些复杂性.例如,您可以重写

int *(*(*f)())[N];
Run Code Online (Sandbox Code Playgroud)

就像这样

typedef int *iptrarr[N];           // iptrarr is an array of pointer to int
typedef iptrarr *arrptrfunc();     // arrptrfunc is a function returning
                                   // a pointer to iptrarr

arrptrfunc *f;                     // f is a pointer to arrptrfunc
Run Code Online (Sandbox Code Playgroud)

现在你可以干净地应用T* p约定,声明farrptrfunc* f.我个人并不喜欢用这种方式做事,因为从typedef中不一定清楚如何f在表达式中使用它,或者如何使用类型的对象arrptrfunc.非typedef'd版本可能很丑陋且难以阅读,但至少它告诉你需要事先了解的所有内容; 你不必去挖掘所有的typedef.

  • @Steve:啊,但是typedef引入了自己的问题.`int*(*(*f)())[N];`可能与不可读性有关,但至少你知道如何在表达式中使用`f`.但是,如果所有这些都隐藏在像`Fptr f`这样的typedef后面,你必须去挖掘定义以了解如何正确使用它.我知道我在这方面有点异常,但出于这个原因,我更喜欢非typedef版本.隐藏在typedef后面的指针似乎总是让我感到胃灼热. (3认同)