1

几年前,在 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 中声明了一些虚拟结构)

4

3 回答 3

6

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

问题中显示的代码仅在所有结构的所有结构成员共享相同的名称空间时才有效。这意味着structure.integ或者总是在结构的开头pointer->integ引用a,因为整个程序中只有一个可能的结构成员。intinteg

请注意,在“现代”C(1978 年以后)中,您不能引用结构类型;它既没有结构标记,也没有 typedef —— 类型是无用的。原始代码还引用了一个未定义的变量c

为了使它工作,你需要类似的东西:

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

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

C 代表第 7 版 UNIX

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

结构成员和结构标记的名称可以与普通变量相同,因为可以通过上下文进行区分。但是,标签和成员的名称必须不同。只有当两个成员的类型相同并且它们的结构起源相同时,相同的成员名称才能出现在不同的结构中;因此,不同的结构可以共享一个共同的初始段。

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

§7.14.2 左值 =+ 表达式
§7.14.3 左值 =- 表达式
§7.14.4 左值 =* 表达式
§7.14.5 左值 =/ 表达式
§7.14.6 左值 =% 表达式
§7.14.7 左值 =>> 表达式
§ 7.14.8 左值 =<< 表达式
§7.14.9 左值 =& 表达式
§7.14.10 左值 =^ 表达式
§7.14.11 左值 = | 表达

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

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

于 2013-04-19T16:55:03.617 回答
3

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

typedef struct { int integ; } PTR_t;
#define PTR ((PTR_t*)0xAA000)
于 2013-04-19T16:55:44.333 回答
1

我想知道是否可以在 GNU C 编译器中消除这种限制。

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

gcc 手册记录了命令-traditional行选项:

'-traditional' '-traditional-cpp'

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

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

您所指的特定功能不仅是 pre-ANSI,而且非常pre-ANSI。ANSI 标准于 1989 年发布。K&R 的第一版于 1978 年发布,我记得它描述的语言不支持您正在寻找的功能。gcc 的初始版本是在 1987 年,因此很可能没有任何版本的 gcc 支持该功能。

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

您也许可以找到以您想要的方式工作的古老 C 编译器的源代码。已故的 Dennis Ritchie 的主页将是一个很好的起点。无需大量工作就可以让这样的编译器在任何现代系统上运行,这一点并不明显。结果将是一个编译器不支持许多您可能会发现有用的 C 新特性,例如longsignedunsigned关键字、按值传递结构的能力、函数原型和混合尝试的诊断指针和整数。

C现在比以前好多了。有一些危险的事情比以前稍微困难一些,但我不知道任何实际的表达能力已经丧失。

于 2013-04-19T17:35:47.200 回答