再谈定义指针变量时星号应该靠左还是靠右

「已注销」 2013-04-28 11:03:21
虽然
(1) int* a;
(2) int *a;
都能编译通过,不过我想这是编译器的纠错优化,给许多编码潦草的人一个很大的容错度。
但是,这并不代表原本的含义就是可以随意跟随。
我的观点是正确的应该是(2)。
我看过一些帖子,有人认为(1)是正确的,理由是:int*代表了一个类型,这种说法面临的最大质疑是,倘若真如此,那么如下写法应该表示定义了三个指针变量
int* a,b,c;
但实际上我们知道不是。
另外,若int*代表了一个类型,那我们很容易想到,int*&是否又代表回了int类型呢?但实际上这样的写法编译器是不认的。
再有,若int*代表了一个类型,那转定义的时候为什么不能写成
typedef (int*) PINT;
呢?但却可以写成
typedef int (*PINT);
这实际上更强烈的暗示,我们并不是将int*定义成了PINT,而是将*PINT定义成了int。
还有
typedef char ARRAY[10];
是合法的,但
typedef char[10] ARRAY;
是非法的。这也提示,我们是定义了一个数组大小为10的ARRAY,而这ARRAY中的一个,是定义为字符类型的。而不是将ARRAY定义为了大小为10的字符数组类型。
当然,在java里,这两种写法都是ok的,这是不是后来为了更好理解的原因,想强制纠正成(1)的理解,这是另外的话题。
那支持(1)的规则到底是什么呢?我认为很混乱了。首先多引入了许多代表指针、双重指针、多重指针的类型;其次,又规定跟随&是不合法的;又规定只能适用于定义变量,而不适用于转定义类型,不适用于定义数组等等。
我认为,一个好的规则,是要尽量的简单,如果用已有的规则已经能解释清楚了,就没必要再引入新的规则。
这在哲学上叫做奥卡姆剃刀原理,即:如无必要,勿增实体。因此,(1)的规则,是不好的规则,至少在编译器所能支持的语法规范里是如此。
现在我来说说我理解的(2)的规则:
在(2)这种写法里,类型还是int,并没有增加。*a代表a取内容,这也是既有的规则。因此,它说的是,变量a的值所在的地址里,存储的是类型为int的值。因此,a被通俗地称为指向int型的指针。
在这里,没有引入额外的概念(即int*)就让人明白a到底是什么东西了。
同样,在typedef int (*PINT);
里,我们定义的还是基本类型int,没有引入额外的类型。数组的定义也是如此。
那我们再来看看,用规则(2)是怎么解释,为什么形如
int &*a;
的写法是错误的。此时,我们定义了一个变量a,它的值所在的地址里的值,再取地址……等等,这里我们就碰到问题了,无法继续理解下去。
譬如a=0x1234,在内存地址为0x1234的地方,假设值为0x5678,0x5678取地址?但,0x5678只是一个数值,不是变量,怎么取地址呢,所以是错误的声明。
但是按照(1)的规则,我们完全应该可以这样写
int*& a;
这无非是定义了一个变量a,它的类型是int*&,至于int*&是什么东西,管我什么事。再说编译器也没有提示我它不认识int*&啊。
同样的,在转定义函数指针的时候,也可以按照(2)的规则,很简单的理解定义的内容,比如
typedef int (*func)(void);
这表示函数返回值所在的内存地址里,表征的是int类型的值。我们无法写成
typedef (int*) func(void);
综上所述,个人认为,基于当前C编译器支持的语法而言,引用奥卡姆剃刀原理的话,只有(2)规则才是正确的理解。
我个人在看一些复杂定义的时候,都是通过(2)这样的规则去理解的,从来没有出现过错误。所以我也一直沿用(2)的写法。
最后,我想说的是,从谁更加容易被理解的角度,实际上(2)的规则并没有(1)来的好,我个人更希望是这样的规则
int* a,b,c; 定义了3个指针变量
char[10] array; 定义数组
typedef (int*) PINT; 是转定义类型
这显得更直观,但无奈目前编译器不是这么想的。
以上为一家之言,若有理解不对的地方,请大家指正。
...全文
2362 79 打赏 收藏 转发到动态 举报
写回复
用AI写文章
79 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaohuh421 2014-01-22
  • 打赏
  • 举报
回复
引用 82 楼 truelance 的回复:
[quote=引用 79 楼 xiaohuh421 的回复:] 还有就是我觉得语法与编译器的主从关系被颠倒了. 应该是先有语法再有编译器, 而不是由编译器决定语法. 所以应该是: "没有学习过语法标准,就去说编译器应该怎样怎么样就是扯淡"
其实语言的定义过程是这样的(我现在在做一个软件系统上支持二次开发, 也算一种简单的语言): 1. 这种语言是在那种场合使用的, 它需要表达哪种语义. 2. 用什么样的语法表达这种语义,使用起来最方便 3. 这种语法表达方式编译器/解释器实现起来是否方便. 2和3的过程需要反复迭代, 在语言不成熟期经常会在实现编译器的时候提出调整语法需求. 后续语言的演化也是结合这两方面的要求: 1. 新的语义表达,怎样方便 2. 提高编译速度,提高编译优化能力. 所以, 一个定义语法的人应该先掌握编译原理. 而不是先定义出语法, 再去学编译原理. 你想强调的主从关系颠倒, 其实是语义要放在第一位, 而不是语法。[/quote] 不说多的, 就看C++标准与VS的关系. 从来都是VS去支持C++的新标准, 从来没听说过C++标准要去适应VS的哪个版本的什么特性.
赵4老师 2014-01-22
  • 打赏
  • 举报
回复
不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 并请结合“盲人摸太阳”和“驾船出海时一定只带一个指南针。”加以理解。 任何理论、权威、传说、真理、标准、解释、想象、知识……都比不上摆在眼前的事实! 说是一物即不中!
「已注销」 2014-01-22
  • 打赏
  • 举报
回复
指针定义代码,应该从右往左读。
熊熊大叔 2014-01-22
  • 打赏
  • 举报
回复
引用 83 楼 xiaohuh421 的回复:
不说多的, 就看C++标准与VS的关系. 从来都是VS去支持C++的新标准, 从来没听说过C++标准要去适应VS的哪个版本的什么特性.
那C++的标准是怎样制定出来的? C++的标准委员会都有哪些人? 象MS这样的公司会向标准组织提大量的议案. VS的一些特性他们也会提议要求标准接纳,但是这种提案他们会和其他组织和专家进行PK, 最后可能是VS的方案被接纳了, 也可能认为这个方案不是最佳的,因此不予接纳. 他们去支持标准是因为这个标准之前他们就已经评审和接纳了. 至于一些MS强烈反对的特性, C++标准接纳了, MS也完全可以不做, VS并不是100%支持标准的.
lm_whales 2014-01-21
  • 打赏
  • 举报
回复
C 有运算符,分隔符,两种符号, 以及 特殊符号下划线_,续行符\。 运算符同时具有分隔符的作用. 下划线,可以和字母一样,作为标识符的,任何一个位置的字符。 数字,可以用在字母下划线后面,和他们一起组成一个标识符。 数字和他们的组合可以表示 10 进制整型常数。 其他一些数字开头的数字,字母,符号组合,可以表示其他整型常数,和浮点型常数。 单引号括住的是字符常数,双引号括住的是字符串字面值(常量)。 这样,标识符 和常量,分隔符(运算符) 就构成了C语言的语法的成分。 识别标识符,常量,依赖的是,空白符和分隔符(运算符) * 是一种运算符,可以用来分隔标识符 所以,可以和变量或者类型 连在一起写 也可以全部分开写。

//1)靠左写
 int* p;
//2) 靠右写
int *p;
//3) 两边都不沾,这个,对于标识符识别,最有利,一律用空白分开。
int * p;
由于,分开写多了一个空格, 又不便,表达程序员的额外意图(强调定义指针,还是强调定义的指针,指向的类型) 所以一般就不写两个空格。 靠左写 int* p; 强调定义的p,是个 int 类型的指针 int* 缺点是 int* p,q;这是定义两个指针,还是一个指针,一个整型变量??? 这么看来int* 连写,的意义就失去了, 因为, 这不是定义两个指针,而是一个指针p,一个整型变量q; 靠右写int *p; 强调,对于指针p; *p 的类型,是个整型int;(指针是个整型指针); 同时,对于同时定义多个指针,不至于漏写 *号; int *p,*q; 定义两个指针;如果从这里来看,*靠右边更加靠谱一点。 这么看来,三种写法,各有道理,怎么写都很好,看不出那种更合适。
xiaohuh421 2014-01-21
  • 打赏
  • 举报
回复
我是更倾向于跟变量写在一起. 比如有: int* p=NULL,val=0; 这时容易误认为val也是指针.
Mr. Code 2014-01-21
  • 打赏
  • 举报
回复
我喜欢,也习惯了int * p;这样的写法,中庸之道!我把*看成是一个辅助的符号,类似static或者const之类一样的辅助描述。
熊熊大叔 2014-01-21
  • 打赏
  • 举报
回复
引用 79 楼 xiaohuh421 的回复:
还有就是我觉得语法与编译器的主从关系被颠倒了. 应该是先有语法再有编译器, 而不是由编译器决定语法. 所以应该是: "没有学习过语法标准,就去说编译器应该怎样怎么样就是扯淡"
其实语言的定义过程是这样的(我现在在做一个软件系统上支持二次开发, 也算一种简单的语言): 1. 这种语言是在那种场合使用的, 它需要表达哪种语义. 2. 用什么样的语法表达这种语义,使用起来最方便 3. 这种语法表达方式编译器/解释器实现起来是否方便. 2和3的过程需要反复迭代, 在语言不成熟期经常会在实现编译器的时候提出调整语法需求. 后续语言的演化也是结合这两方面的要求: 1. 新的语义表达,怎样方便 2. 提高编译速度,提高编译优化能力. 所以, 一个定义语法的人应该先掌握编译原理. 而不是先定义出语法, 再去学编译原理. 你想强调的主从关系颠倒, 其实是语义要放在第一位, 而不是语法。
熊熊大叔 2014-01-21
  • 打赏
  • 举报
回复
引用 79 楼 xiaohuh421 的回复:
[quote=引用 78 楼 truelance 的回复:] 所以一般的编程规范都要求一行只定义一个变量. btw: 没有学习过编译原理就讨论语法应该怎么怎么样就是扯淡.
我也习惯是一行只定义一个变量, 偶而会定义多个. 但这不能保证其它人这么写, 别人这么写了, 就容易误会. 我只是从习惯与代码风格的角度去说的. 要从编译原理的角度去讨论的话, 那肯定更能理解前因后果, 希望有高手能解答为什么靠哪边都可以, 靠哪边更符合编译器规定. 还有就是我觉得语法与编译器的主从关系被颠倒了. 应该是先有语法再有编译器, 而不是由编译器决定语法. 所以应该是: "没有学习过语法标准,就去说编译器应该怎样怎么样就是扯淡" [/quote] 一个实用的语言不是纸上谈兵. 任何一种流行的语言在语法定义时都会考虑编译器是如何实现的. 否则语言定义出来, 没有好的编译器支持, 或者编译器运行速度很慢, 都会导致这种语言胎死腹中.
初見的畫面 2014-01-21
  • 打赏
  • 举报
回复
其实跟变量写一起就好了。 说那么多干什么 养成良好习惯就行了。
xiaohuh421 2014-01-21
  • 打赏
  • 举报
回复
引用 78 楼 truelance 的回复:
所以一般的编程规范都要求一行只定义一个变量. btw: 没有学习过编译原理就讨论语法应该怎么怎么样就是扯淡.
我也习惯是一行只定义一个变量, 偶而会定义多个. 但这不能保证其它人这么写, 别人这么写了, 就容易误会. 我只是从习惯与代码风格的角度去说的. 要从编译原理的角度去讨论的话, 那肯定更能理解前因后果, 希望有高手能解答为什么靠哪边都可以, 靠哪边更符合编译器规定. 还有就是我觉得语法与编译器的主从关系被颠倒了. 应该是先有语法再有编译器, 而不是由编译器决定语法. 所以应该是: "没有学习过语法标准,就去说编译器应该怎样怎么样就是扯淡"
熊熊大叔 2014-01-21
  • 打赏
  • 举报
回复
引用 76 楼 xiaohuh421 的回复:
我是更倾向于跟变量写在一起. 比如有: int* p=NULL,val=0; 这时容易误认为val也是指针.
所以一般的编程规范都要求一行只定义一个变量. btw: 没有学习过编译原理就讨论语法应该怎么怎么样就是扯淡.
赵4老师 2014-01-21
  • 打赏
  • 举报
回复
引用 76 楼 xiaohuh421 的回复:
我是更倾向于跟变量写在一起. 比如有: int* p=NULL,val=0; 这时容易误认为val也是指针.
严重同意!
UpCokio 2014-01-20
  • 打赏
  • 举报
回复
国内不仅落后的是技术,而且有思想。 硬件行业落后至少20年 软件你们算去吧。。。 受到什么样的教育,就会有什么样的思想。 理解语言,而不是死记硬背规则。 没人敢站出来说C哪里哪里不合适修改了,乱猜,乱掐。 脑子里永远是1+1=2,怎么可能创造出1+1=3.
lm_whales 2013-05-06
  • 打赏
  • 举报
回复
任何一种写法都没错,关键是你想,强调指针的类型,还是变量的类型。 int* p;//强调指针p,所指类型是整型。这个是整型指针。 int *p;//强调p是一个指针变量; (后面这个没有强调:这个指针,所指的类型是整型。) 各取所好而已!!! int*p;这个才是尽量要避免的!!!!写到一块了。
March0912 2013-04-30
  • 打赏
  • 举报
回复 2
个人习惯:当int* 用来定义变量的时候,*跟随变量,当int*用来做函数返回值是*跟随int; 例如:int *pNum = NULL; int* Fun();
无_名_ 2013-04-30
  • 打赏
  • 举报
回复
引用 64 楼 Athenacle_ 的回复:
引用 63 楼 u010481185 的回复:
[quote=引用 37 楼 fthislife 的回复:] 我就喜欢看相互掐的
呵呵
掐来掐去我都看晕了。。。 逻辑风暴啊。。。 你们两个去看编译原理把,然后再看C专家编程,然后再看一两本关于程序设计语言语义学,最后看看C语言标准之后,这些问题都不是问题了。。。[/quote]Athenacle_,给学C语言的新人再推荐几本好书吧
kotiyasanae 2013-04-30
  • 打赏
  • 举报
回复
csdn原来是不能编辑的,只好连贴了,如下:

(* a)=0;       //int *a
(* b)() = 0;   //int (*b)(...)

main(void){return 0;}
老的标准是可以省略某些int的,比如上面的几种情况,所以猜测是一个语句的第一个符号如果是括号,那么几乎期待下一个就是*(去掉其他直接修饰指针的扩展比如__stdcall不论), 这个观点我没有查阅ANSI C的BNF(or EBNF)规则,希望知道标准的大大能告诉我一下
sanae 2013-04-30
  • 打赏
  • 举报
回复
个人猜测(仅仅是个人猜测)'(int *)i' 中不能用普通括号的原因是,可能有解析错误,比方说,遇到[]当做数组声明(至于自动退化成指针可能不是这一阶段了当然),遇到()当做函数声明,而并没有把括号里面视为一个类型整体的解析规则,而仅仅是当做了类型的一部分,比如int add(int, int)中的(int, int)
sanae 2013-04-30
  • 打赏
  • 举报
回复
偶尔利用下这种括号结构也可以换一种表达的,别忘了不是普通的括号是__typeof()。

/*libc中signal原本的类型实际上是*/
void (*signal(int sig, void (*func)(int)))(int);

/*现在另给这类型一定义*/
typedef __typeof(
   __typeof(void (*)(int))            /*返回一个函数指针为某某类型*/
    (int, __typeof(void (*)(int)))    /*的函数,参数是int, 和一个函数指针*/
)  
*signal_id_prototype;               /*的函数指针是singal_id_prototype;*/


int main(){ 
  signal_id_prototype s = signal; /* 必须相同类型函数指针才能在最严格模式不出警告或者错误 */
  return 0;
}
加载更多回复(59)

69,759

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧