ANSI-C之前使用结构选择器

时间:2013-04-19 16:42:43

标签: c pointers gcc struct ansi

几年前,在C标准化之前,允许在地址上使用结构选择器。例如,允许并经常使用以下代码。

#define PTR 0xAA000
struct {  int integ; };

func() {
   int i;
   i = PTR->integ;    /* here, c is set to the first int at PTR */
   return c;
}

也许它不是很整洁,但我喜欢它。在我看来,这种语言的力量和多样性也依赖于它缺乏约束。如今,编译器只是转储错误。我想知道是否可以在GNU C编译器中删除此约束。

PS:C的发明者在UNIX内核上使用了类似的代码(在V6中,在param.h中声明了一些虚拟结构)

3 个答案:

答案 0 :(得分:6)

几年前'实际上'很久很久以前'。 AFAICR,第7版UNIX™中的C(1979年,定义了C89标准之前的十年)不再支持这种表示法(但见下文)。

问题中显示的代码仅在所有结构的所有结构成员共享相同名称空间时才起作用。这意味着structure.integpointer->integ始终在结构的开头引用int,因为整个程序中只有一个可能的结构成员integ

请注意,在“现代”C(1978年以后)中,您无法参考结构类型;它既没有结构标签也没有typedef - 这种类型没用。原始代码还引用了未定义的变量c

要使其有效,您需要以下内容:

#define PTR 0xAA000
struct integ {  int integ; };

int func(void)
{
   struct integ *ptr = (struct integ *)PTR;
   return ptr->integ;
}

C for 7th Edition UNIX

我建议使用第7版UNIX的C支持单独的结构类型的单独命名空间。但是,与UNIX程序员手册第2卷一起发布的C参考手册在§8.5结构中提到:

  

结构成员和结构标签的名称可以与普通变量相同,因为区别可以   由上下文制作。但是,标签和成员的名称必须是不同的。可以显示相同的成员名称   只有当两个成员属于同一类型并且它们的结构相对于它们的结构时才有不同的结构   相同;因此,单独的结构可以共享一个共同的初始段。

但是,同一本手册也提到了符号(另见What does =+ mean in C):

  

§7.14.2左值= +表达
  §7.14.3左值= - 表达

  §7.14.4lvalue= *表达
  §7.14.5左值= /表达
  §7.14.6左值=%表达

  §7.14.7lvalue=>>表达
  §7.14.8lvalue=<<表达
  §7.14.9左值=&表达
  §7.14.10左值= ^表达

  §7.14.11lvalue= |表达

     

“E1 = op E2”形式的表达式的行为可以通过将其视为等效来推断   ''E1 = E1 op E2'';但是,E1仅评估一次。此外,表达式如'i = + p'',其中指针是   添加到整数,是禁止的。

AFAICR,我使用的第一批C编译器不支持(1983年 - 我很古老,但不是很古老);只允许使用现代+=符号。换句话说,我不认为该产品发布时该参考手册中描述的C是完全最新的。 (我没有查看我的第一版K& R - 有没有人可以检查?)你可以在http://cm.bell-labs.com/7thEdMan/在线找到UNIX第7版手册。

答案 1 :(得分:3)

通过为结构提供类型名称并稍微调整宏,您可以在代码中实现相同的效果:

typedef struct { int integ; } PTR_t;
#define PTR ((PTR_t*)0xAA000)

答案 2 :(得分:1)

  

我想知道是否可以在GNU C编译器中删除此约束。

我有理由肯定答案是否定的 - 也就是说,除非你重写gcc以支持旧版本的语言。

gcc manual记录-traditional命令行选项:

  

' - 传统''-traditional-cpp'

     

以前,这些选项导致GCC试图模仿a      预标准C编译器。他们现在只支持      `-E'开关。预处理器继续支持预标准      模式。有关详细信息,请参阅GNU CPP手册。

这意味着现代gcc(引用来自4.8.0手册)不再支持ANSI C之前。

您所指的特定功能不仅仅是ANSI之前的版本,而是非常之前的ANSI。 ANSI标准于1989年出版。第一版K& R于1978年出版,我记得它所描述的语言并不支持您正在寻找的功能。 gcc的最初版本发布于1987年,因此gcc的 no 版本很可能曾支持该功能。

此外,启用此类功能会破坏现有代码,这可能取决于在不同结构中使用相同成员名称的能力。 (旧规则的痕迹在标准C库中存活,例如,类型struct tm的成员都具有以tm_开头的名称;在现代C中,这些名称不是必需的。)

您可能能够找到以您想要的方式工作的古老C编译器的源代码。已故丹尼斯·里奇的home page将是一个很好的起点。在没有大量工作的情况下,你能够在任何现代系统上运行这样的编译器并不是很明显。结果将是一个不支持C可能有用的新功能的编译器,例如longsignedunsigned关键字,能力通过值,函数原型和诊断来传递结构,以尝试混合指针和整数。

C 现在比以前更好。有一些危险的东西比它们稍微困难一点,但我不知道任何实际的表现力都已经丢失了。