2011-08-02 34 views
2

Hello Developers!我正在學習Skiena的算法設計手冊。在那裏,我有以下代碼:C編碼在C++編碼時不起作用

#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" (in directory: /home/akacoder/Desktop/Algorithm_Design_Manual/chapter2) test.c: In function ‘insert_list’: test.c:15: warning: assignment from incompatible pointer type Compilation finished successfully.

但是,當我重寫這段代碼爲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" (in directory: /home/akacoder/Desktop/Algorithm_Design_Manual/chapter2) chapter2.cpp:15: error: conflicting declaration ‘typedef struct list list’ chapter2.cpp:14: error: ‘struct list’ has a previous declaration as ‘struct list’ chapter2.cpp: In function ‘void insert_list(list**, item_type)’: chapter2.cpp: In function ‘void insert_list(list**, item_type)’: chapter2.cpp:19: error: invalid conversion from ‘void*’ to ‘list*’

任何人都可以明白爲什麼是這樣?我怎樣才能用C++重寫它?

回答

8

你在這兩種情況下的問題是在結構體定義中:struct list *next沒有引用你正在聲明的結構體。試試這個:

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

此外,在C++中,你必須強制返回void *通過malloc到相應的指針類型(list *),C++是這些東西嚴格。另外,在C++中,如果你願意的話,你可以完全忽略typedef。

不同的錯誤消息的原因是語言的差異。

在C中,編譯器知道struct list *是一個指向結構的指針,因此它不需要抱怨它實際上不知道「結構列表」是什麼。然而,後來,當你嘗試從類型爲「list *」的指針(它的類型是「指向匿名結構的指針」)指定這個「struct list *」時,它會抱怨不匹配。

在C++中,「struct」聲明或多或少等同於「類」聲明(主要區別在於成員的默認可見性)。除此之外,這意味着C++中的結構或多或少都會自動進行typedefed。因此,當編譯器看到「struct list * next」時,它將其作爲名爲「list」的類的前向聲明;那麼當它完成語句並處理typedef時,會拋出一個錯誤,因爲您試圖將某個標識符鍵入某個已經(前向)聲明爲其他內容的標識符。然後它會發出更多的錯誤,因爲它實際上並不知道「list」可能是什麼,這是由於之前的錯誤。

+0

它不必具有前面的聲明,它將'struct list * next'解釋爲前向聲明。 –

+0

@GeneBushuyev:好點,我調整了措辭。 – Anomie

9

這是因爲在類型轉換方面,C++比c更嚴格。

代碼中有其他錯誤。請注意,只需放置c源代碼,將文件重命名爲.cpp &使用g++編譯不會將c源代碼編寫爲C++。

如果在c++編寫一個程序,請使用new &不malloc,這樣你就不需要明確的類型轉換爲在malloc情況。

+0

而我可以歸結爲什麼downvote? –

+1

這個答案需要更具體一些:「代碼中有其他錯誤」並不能幫助任何人。 –

+2

@Chris Johnson:當我寫完主要錯誤部分的時候,其他答案已經提出了錯誤的詳細列表,我認爲它不值得在這裏重複相同的答案,回答了基本的缺陷&我想說的一點。 –

5

C++不允許任意指針轉換,而C不允許。但是,由於這不是很好的風格,編譯器會發出警告。

只需添加一個投,它會解決這兩個消息:

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

或者,如果你想成爲C++只:

p = new list; 

但是,你應該聲明構造函數和這樣的,也。

+0

將'malloc()'的結果投射在C中被認爲是* poor * style。它可以掩蓋錯誤;例如,如果你忘記'#include ',一些編譯器會認爲'malloc()'返回一個int。避免類型不匹配的習語是:'p = malloc(sizeof * p);'。 –

+0

如果你正在編寫可以編譯爲C或C++的代碼,那麼你可能做錯了。 C/C++互操作性足夠好,因此很少有很好的理由這樣做。 –

0

什麼C只警告,C++可能會考慮一個錯誤。

這是一個編程文化的東西。 C非常寬容,沒有執行它的打字系統。 C++仍然相當寬容,但是你正在用C做一些事情,即使C++也不會原諒。

當你malloc那塊內存時,把它轉換成一個指向列表的指針。這將把地址(指針)轉換爲正確類型的指針。

如果沒有這種轉換,你可能有malloc的大小任何東西,並沒有告訴它是否意味着被列表指針或其他指針引用。

+0

在C中,投射'malloc()'的結果更有可能創建或隱藏錯誤,而不是解決它們。只需將結果分配給指針對象;從void *到任何指向對象類型的隱式轉換都會照顧剩下的部分。使用'p = malloc(sizeof * p);'以確保所有的大小和類型是一致的和正確的。 (這是特定於C;對於C++,您可能不應該使用'malloc()'。) –

3

您需要更改這個類:

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

這樣:

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

說明:在第一個例子,你有匿名結構,在其內部struct list向前聲明。因此,當編譯器在下一行看到typedef時,它會發現名稱衝突,因爲typedef與C++中的struct declaration不同。

+0

不完全;在'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;'。 –

+0

@Keith - 'list * next;'被聲明(否則編譯器會失敗);在課堂內部,所有的名字(不僅僅是課程名稱)都是可見的。你可能想說的是'list'在這一點上是不完整的,這很好,它不會阻止編譯器在不完整類型上使用指針。 –

+0

在C++中,'struct list'的聲明,即使是不完整的類型,也可以讓您將類型稱爲'list'。在C中,它不會 - 這就是爲什麼C代碼通常對結構類型使用typedef的原因。 (我自己的首選是省略typedef,只需將其類型稱爲'struct list'。) –

4

這在this link中有解釋。

報價:

Gotcha for a C++ programmer using C

Structs and Enums

You have to include the struct keyword before the name of the struct type to declare a struct: In C++, you could do this

struct a_struct { int x; };

a_struct struct_instance;

and have a new instance of a_struct called struct_instance . In C, however, we have to include the struct keyword when declaring struct_instance :

struct a_struct struct_instance;

In fact, a similar situation also holds for declaring enums: in C, you must include the keyword enum ; in C++, you don't have to. As a side note, most C programmers get around this issue by using typedefs:

typedef struct struct_name { /* variables */ } struct_name_t;

Now you can declare a struct with

struct_name_t struct_name_t_instance;

But there is another gotcha for C++ programmers: you must still use the " struct struct_name " syntax to declare a struct member that is a pointer to the struct .

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

既然你在做什麼是真正定義struct,然後創建一個與typedef我認爲這是更具可讀性做到這一點在C情況下的一個別名:

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

爲什麼您對標記(list_)和typedef名稱(列表)使用不同的標識符?由於結構標籤位於不同的名稱空間中(在C的意義上說),所以編寫'typedef struct list {...} list;';那麼「struct list」和「list」是同一類型的兩個不同名稱。 –

+0

@Keith你是對的,我想我只是喜歡這種風格,因爲它給人的印象是用戶不應該使用'struct list_',而是應該通過'typedef',主要是爲了一致性。 – mgalgs

1

使用下面的代碼

#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; 
} 
+0

爲什麼'malloc'?這應該是C++ NOT C! –