C语言中的名字层次

来源:互联网 发布:船舶ais数据 编辑:程序博客网 时间:2024/05/05 15:15

P.J Plauger的"The Standard C Library"一书的Chapter0的章后练习中有这样的一道题:编写一个包含如下一行语句的正确的程序: x:      ((struct x*)x)->x=x(5);

并描述这行语句中x的5种截然不同的use,这里其实涉及到这么一个知识或者说概念:C语言的命名空间(namespace),在"C语言参考手册"中还被称作: overloading class。 这 里namespace,并非C++中的那个keyword "namespace",这里的namespace更多是编译器为了识别不同范围下的标识符而进行的划分,而不是提供给应用程序员的类似c++中的那个 namespace facility。再次注意:C的namespace不是一个关键字。

简单分析一下这行语句:x:      ((struct x*)x)->x=x(5); 这里有5个x,第一印象:这样的语句能编译过去么?那既然P.J Plauger提出了这样的问题,那么自然有solution。

从左到右顺序:

第一个x -- 毋庸置疑,这是一个标号(label) ;

第二个x -- 这里的x显然是一个struct tag(结构体标志);

第三个x -- 这里的x 无法确定其具体身份,可能是一指针类型,也可能就是一个整型;

第四个x -- x前面有->,显然这个x是某结构体的一个成员变量;

第五个x -- x(5)让人"浮想联翩",第一印象是函数调用,细致一想还可能是一个宏哦(你肯定会说不可能,呵呵,别着急,慢慢来) 到底如何增加一些语法元素能让这一行能顺利通过编译,并执行后得到合理结果呢?

我们不妨先来温习一下C标准中对C的"命名空间"的诠释。 在"C语言参考手册"中有如此说明,标准C将其Namespace分成了五种,分别是:

1) 预处理器宏名

2) 语句标号

3) 结构、枚举、联合结构的标志

4) 成员名

5) 其他名称 包括变量名、函数名、typedef名称和枚举常量

 有了以上的说明,我们有了第一种方案:上面说了,语句x:      ((struct x*)x)->x=x(5)中有三个x都是可以确定的,不确定的是第三个x和最后一个x。我们先考虑让最后一个x为一个函数。 考虑到最后一个名称空间的说明,一旦最后一个x为函数的话,第三个x就不能为变量名、typedef名称和枚举常量了。如果x是对象宏(不带参数的宏),显然也不合理;那么我们先将x实现为函数看看:

struct x { //for the 2nd x

        int x;  //for the 4th x };

int x(int a) { //for the 3rd and 5th x   

      return a; }

int main()

 {

x:      ((struct x*)x)->x=x(5);

}

这个在gcc(sunos or mingw on windows下)下编译能顺利通过。但是执行一下编译出的程序,会出现致命错误。初略分析一下也不奇怪。函数x的地址是在代码段,那块内存区域是只读且受保护的,尝试强制赋值显然os是不允许的。 第一种方案虽然能通过编译,但是执行结果不合理。

我们来做第二种尝试:试着将最后一个x实现为一个函数宏(带参数的宏)。

struct x { //for the 2nd x

         int x;  //for the 4th x };

struct x ax;

#define x(a)  (a);

int main() {

         int x = (int)(&ax);

x:      ((struct x*)x)->x=x(5);

          printf("%d/n", ((struct x*)x)->x); //output: 5

}

这回,我们得到了正确的且合理的solution了。在P.J Plauger的"The Standard C Library"一书中还有一张关于C语言命名空间的图,记起来更形象。

原创粉丝点击