【问题标题】:C code does not work when coded like C++像 C++ 一样编码时,C 代码不起作用
【发布时间】:2011-08-02 16:53:13
【问题描述】:

开发人员您好!我正在从 Skiena 的算法设计手册中学习算法。我有以下代码:

    #include <stdio.h>
#include <stdlib.h>

typedef int item_type;

typedef struct{
    item_type item;
    struct list* next;
    }list;

void insert_list(list **l, item_type x){
    list *p;
    p = malloc(sizeof(list));
    p->item = x;
    p->next = *l;
    *l = p;
    }

int main(){
    return 0;
    }

编译时给我警告:

gcc -Wall -o "test" "test.c"(在目录中: /home/akacoder/Desktop/Algorithm_Design_Manual/chapter2) test.c:在 函数‘insert_list’:test.c:15:警告:赋值来自 指针类型不兼容 编译成功。

但是当我将这段代码重写为 C++ 时:

 #include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

typedef int item_type;

typedef struct{
    item_type item;
    struct list* next;
    }list;

void insert_list(list **l, item_type x){
    list *p;
    p = malloc(sizeof(list));
    p->item = x;
    p->next = *l;
    *l = p;
    }

int main(){
    return 0;
    }

它给出了以下内容:

g++ -Wall -o "chapter2" "chapter2.cpp" (在目录中: /home/akacoder/Desktop/Algorithm_Design_Manual/chapter2) Chapter2.cpp:15:错误:声明冲突'typedef struct list list' chapter2.cpp:14: 错误:'struct list' 有一个先前的声明 作为‘结构列表’chapter2.cpp:在函数‘void insert_list(list**, item_type)': chapter2.cpp: 在函数'void insert_list(list**, item_type)’:chapter2.cpp:19:错误:来自‘void*’的无效转换 到“列表*”

谁能解释为什么会这样?以及如何用 C++ 重写它?

【问题讨论】:

    标签: c++ c pointers


    【解决方案1】:

    这是因为 c++ 在类型转换方面比 c 更严格。

    您的代码中还有许多其他错误。请注意,仅放置 c 源代码,将文件重命名为 .cpp 并使用 g++ 编译不会使 c 源代码成为 c++。

    如果您在c++ 中编写程序,请使用new 而不是malloc,这样做您不需要像malloc 那样显式键入强制转换。

    【讨论】:

    • 这个答案需要更具体:“您的代码中有许多其他错误”对任何人都没有帮助。
    • @Chris Johnson:当我写完主要的错误部分时,其他答案已经提出了elaborated list 的错误,我认为在这里重复同样的内容是不值得的,回答,回答了基本的缺陷和我想表达的观点。
    【解决方案2】:

    在这两种情况下,您的问题都出在结构定义中:struct list *next 不是指您正在声明的结构。试试这个:

    typedef struct list {
        item_type item;
        struct list* next;
    } list;
    

    此外,在 C++ 中,您必须将 malloc 返回的 void * 转换为适当的指针类型 (list *),C++ 对这些事情更严格。另外,顺便说一句,在 C++ 中,您可以根据需要完全取消 typedef。

    错误消息不同的原因是语言不同。

    在 C 中,编译器知道struct list * 是一个指向结构的指针,因此它不需要抱怨它实际上还不知道“结构列表”是什么。但是,稍后,当您尝试从“list *”类型的指针(其类型为“指向匿名结构的指针”)分配此“struct list *”时,它会抱怨不匹配。

    在 C++ 中,“结构”声明或多或少等同于“类”声明(主要区别在于成员的默认可见性)。除此之外,这意味着 C++ 中的结构或多或少是自动类型定义的。所以当编译器看到“struct list *next”时,它会把它当作一个名为“list”的类的前向声明;然后当它完成语句并处理 typedef 时,会引发错误,因为您试图将某些内容输入到已经(前向)声明为其他内容的标识符。然后它会发出进一步的错误,因为它实际上并不知道“列表”可能是什么,因为之前的错误。

    【讨论】:

    • 它不必有先前的声明,它将struct list *next解释为前向声明。
    【解决方案3】:

    C++ 不允许任意指针转换,而 C 允许。但由于这不是好的样式,编译器会发出警告。

    只需添加一个演员,它就会解决这两条消息:

    p = (list*)malloc(sizeof(list));
    

    或者如果你只想成为 C++:

    p = new list;
    

    但是,你也应该声明构造函数等等。

    【讨论】:

    • 铸造malloc()的结果在C中被认为是风格。它可以掩盖错误;例如,如果你忘记了#include &lt;stdlib.h&gt;,一些编译器会假设malloc() 返回一个int。避免类型不匹配的习惯用法是:p = malloc(sizeof *p);
    • 如果您尝试编写可以编译为 C 或 C++ 的代码,那么您可能做错了。 C/C++ 互操作性足够好,很少有充分的理由这样做。
    【解决方案4】:

    this link 对此进行了解释。

    引用:

    C++ 程序员使用 C 的陷阱

    结构和枚举

    你必须 在结构类型名称之前包含结构关键字 声明一个结构:在 C++ 中,你可以这样做

    struct a_struct { int x; };

    a_struct struct_instance;

    并有一个名为struct_instancea_struct 的新实例。在 C 中, 但是,我们必须在声明时包含 struct 关键字 struct_instance:

    struct a_struct struct_instance;

    事实上,类似的情况也适用于声明枚举:在 C 中,你 必须包含关键字enum;在 C++ 中,您不必这样做。作为一方 注意,大多数 C 程序员通过使用 typedefs 来解决这个问题:

    typedef struct struct_name { /* 变量 */ } struct_name_t;

    现在你可以用

    声明struct

    struct_name_t struct_name_t_instance;

    但是 C++ 程序员还有另一个问题:你仍然必须使用 "struct struct_name" 语法来声明一个 struct 成员,它是 指向struct 的指针。

    typedef struct struct_name {
        struct struct_name instance;
        struct_name_t instance2; /* invalid!  The typedef isn't defined
    yet */ } struct_name_t;
    

    【讨论】:

      【解决方案5】:

      你需要改变这个类:

      typedef struct{
          item_type item;
          struct list* next;
          }list;
      

      到这里:

      struct list {
          item_type item;
          list* next;
          };
      

      说明:在第一个示例中,您有匿名结构,其中struct list 被前向声明。因此,当编译器在下一行看到 typedef 时,它会发现名称冲突,因为 typedef 与 C++ 中的 struct 声明不同。

      【讨论】:

      • 不完全;在list* next; "list" 中未声明。 (C++ 允许您将“struct list”称为“list”;C 不允许。)您需要 struct list { item_type item; struct list *next; };(然后将类型称为“struct list”)或 typedef struct list { item_type item; struct list *next; } list;
      • @Keith -- list* next; 被声明(否则编译器会失败);在类体内所有名称(不仅是类名)都是可见的。您可能想说的是 list 在这一点上是不完整的,这很好,它不会阻止编译器获取不完整类型的指针。
      • 在 C++ 中,struct list 的声明,即使是一个不完整的类型,也允许您将类型称为 list。在 C 中,它不会——这就是为什么 C 代码通常将 typedef 用于结构类型。 (我自己的偏好是省略 typedef,只将类型称为 struct list。)
      【解决方案6】:

      由于您实际上是在定义 struct,然后使用 typedef 创建别名,因此我认为在 C 案例中这样做更具可读性:

      typedef struct list_ {
          item_type item;
          struct list_* next;
      } list;
      

      【讨论】:

      • 为什么标签(list_)和typedef名称(list)使用不同的标识符?由于 struct 标签位于不同的命名空间中(在 C 术语的意义上),因此编写 typedef struct list { ... } list; 是完全合法的;那么“struct list”和“list”是同一类型的两个不同名称。
      • @Keith 你说得对,我想我只是更喜欢这种风格,因为它给人的印象是用户不应该使用struct list_,而应该通过typedef,主要是为了一致性。
      【解决方案7】:

      使用以下代码

      #include <iostream>
      #include <cstdio>
      #include <cstdlib>
      using namespace std;
      
      typedef int item_type;
      
      struct list{
          item_type item;
          list* next;
      };
      
      void insert_list(list **l, item_type x){ 
          list *p; 
          p = (list*)malloc(sizeof(list));
          p->item = x;
          p->next = *l; 
          *l = p;
      }
      
      int main(){
          return 0;
      }
      

      【讨论】:

      • 为什么是malloc?这应该是 C++ 而不是 C!
      【解决方案8】:

      C 只警告,C++ 可能会考虑错误。

      这是一种编程文化。 C 在不强制执行它的打字系统方面非常宽容。 C++ 仍然很宽容,但你在 C 中做的事情连 C++ 都不会原谅。

      当你 malloc 内存块时,将其转换为指向列表的指针。这会将地址(指针)转换为正确类型的指针。

      如果没有这种转换,您可以对任何东西的大小进行 malloc,并且不知道它是要被列表指针还是其他指针引用。

      【讨论】:

      • 在 C 中,转换malloc() 的结果比解决错误更有可能产生或隐藏错误。只需将结果分配给您的指针对象;从 void* 到任何指向对象类型的隐式转换将负责其余的工作。使用p = malloc(sizeof *p); 确保所有大小和类型一致且正确。 (这是特定于 C 的;对于 C++,您可能无论如何都不应该使用 malloc()。)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多