1998年的C代码现在无法在gcc下编译

6
我有大约16k行1998年的C代码(约50个主程序),当时可以在gcc下完美构建,但现在在第一个例程“stutter.c”中出现了许多“需要左值作为赋值操作数”的错误。我不是足够熟练的C程序员,找不到问题,并且似乎无法搜索到与我的问题相关的答案(具有这种相当通用的错误消息)。
以下是具体信息:
(年代久远的)Makefile中的编译行:
gcc -O3 -Wall -D__dest_os=unix -I/usr/X11/include -DPLOTX11 -c -o stutter.o ../src/stutter.c
失败语句示例:
    cell_car(cell) = free_list;
    cell_type(cell) = type;
    cell_name(atom) = strdup(name);
    cell_cdr(list) = binding_list;
    cell_cdr(cell_car(current)) = b;

"

...(以及许多类似的)

前面是:

"
    typedef enum CELL_ENUM {
      CELL_LAMBDA, CELL_SFUNC, CELL_VFUNC, CELL_LIST, CELL_ATOM
    } CELL_TYPE;

    typedef struct CELL_STRUCT {
    void *car, *cdr;
    unsigned type : 7;
    unsigned mark : 1;
    char empty[3];
    } CELL;

并且:

    #define cell_car(c)      ((CELL *)(c)->car)
    #define cell_cdr(c)      ((CELL *)(c)->cdr)        
    #define cell_name(c)     ((char *)(c)->car)
    #define cell_func(c)     ((CELL *(*)())(c)->car)
    #define cell_type(c)     ((CELL_TYPE)(c)->type)
    #define cell_mark(c)     ((c)->mark)

如果需要更多的代码细节,请告诉我。这里是否有一些明显的过时特性可以解释这个错误??
我作为一名科学Fortran程序员有多年的经验,但在退休之前没有学到足够的C语言知识来完全转换。在http://gcc.gnu.org/bugs/上没有找到任何关于遗留代码的有用信息。我很感激任何能帮助我在Linux下为我的当前项目使这些例程工作的帮助。非常感谢!

@dyp,不知道free_list的类型,很难说... @sambledsoe,你可以尝试使用clang编译而不是gcc,以便获取更多有用的错误信息吗? - Maxime Chéramy
在过去的C语言中,(some_variable) = 123是合法的吗?因为这就是你在这里做的事情。 - Eric Lippert
3
@EricLippert 这仍然是合法的... - Maxime Chéramy
@Maxime:我每天都会学到新东西。 :-) - Eric Lippert
1
@Maxime 在 C 语言中,强制类型转换永远不会产生一个左值。因此,无论 free_list 的类型是什么,该宏都不能出现在赋值语句的左侧。 - dyp
car(地址寄存器的内容)和cdr(数据寄存器的内容)。嗯 - 我闻到了Lisp的味道。 - chux - Reinstate Monica
2个回答

13

gcc不再允许您向强制转换赋值。

((CELL *)(cell)->car) = free_list;

不再合法。与其将lhs转换以匹配rhs,倒不如将rhs转换以匹配lhs。解决此问题的一种方法是获取lvalue的地址,将其强制转换为指针,然后对该指针进行解引用,这样赋值就是指针解引用而不是类型转换。

例如:

*((CELL **)&(cell)->car) = free_list;

这可以通过更新宏来处理,因此应该是相当简单的...

#define cell_car(c)      (*((CELL **)&(c)->car))

这个宏可以作为左值或右值使用。


我认为它是这个扩展名。虽然文档是从2001年的,但我没有找到更早的版本。 - dyp
Lvalue转换似乎是GCC的扩展,现在已经被弃用。 - pat
是的,我认为它们在3.3.6(2004年)左右被弃用了,在4.X文档中也没有出现。 - dyp
3
C语言的"cast-as-lvalue"扩展(自3.3.4版开始被废弃)已在2005年的GCC 4.0中被移除:http://gcc.gnu.org/gcc-4.0/changes.html - Michael Burr
1
那种指针转换的风格在 C99 及以后的版本中很不幸地容易导致未定义行为,除非能够禁用严格别名规则。 - supercat
显示剩余3条评论

0
除了更改代码以采用地址、强制转换指针和取消引用之外,很可能还需要使用“no_strict_aliasing”选项。C99添加了一些规则,旨在促进优化,这些规则否定了C作为系统状态的模型,即一堆unsigned char[]。其中最臭名昭著的是严格别名规则。虽然将指针强制转换为表示“将此指针指向的东西解释为X”的方法的代码可能会编译,但使用一种类型写入指针并使用另一种类型读取将通常产生未定义行为,并且某些编译器将将其解释为许可证,以相当奇怪和奇异的方式行事,不受时间和因果律的约束。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接