C code does not work when coded like C++
Hello Developers! I am learning algorithms from Algorithms Design Manual Book by Skiena. There I have the following code:
#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;
}
It gives me Warning when compiled:
开发者_运维问答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.
But when I rewrite this code as 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;
}
It gives the following:
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*’
Can anyone explain why it is so? And How can I rewrite it in C++?
This is because c++ is stricter than c with respect to type conversions.
There are host of other errors in your code. Please note that just putting a c source code, renaming the file as .cpp
& compiling using g++
does not make a c source code as c++.
If you are writing a program in c++
please use new
& not malloc
, doing so you do not need to explicitly type cast as in case of malloc
.
Your problem in both cases is in the struct definition: struct list *next
doesn't refer to the struct you are in the process of declaring. Try this instead:
typedef struct list {
item_type item;
struct list* next;
} list;
In addition, in C++ you must cast the void *
returned by malloc
to the appropriate pointer type (list *
), C++ is stricter about these things. Also, BTW, in C++ you can leave off the typedef completely if you want.
The reason for the differing error messages is a difference in the languages.
In C, the compiler knows that struct list *
is a pointer to a struct, so it doesn't need to complain that it doesn't actually know what a "struct list" is yet. Later, though, when you try to assign this "struct list *" from a pointer of type "list *" (the type of which is "pointer to an anonymous struct"), it complains about the mismatch.
In C++, a "struct" declaration is more or less equivalent to a "class" declaration (the major difference is in the default visibility of members). Among other things, this means that structs in C++ are more or less automatically typedefed. So when the compiler sees "struct list *next", it takes it as a forward declaration of a class named "list"; then when it finishes the statement and processes the typedef, throws an error because you're trying to typedef something to an identifier that is already (forward-)declared as something else. Then it issues further errors because it doesn't actually know what "list" might be, due to the earlier error.
C++ does not allow arbitrary pointer conversions, while C does. But since this is not considered good style, the compiler emits a warning.
Just add a cast and it will solve both messages:
p = (list*)malloc(sizeof(list));
Or if you want to be C++ only:
p = new list;
But then, you should declare constructors and such, also.
This is explained in this link.
Quote:
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
calledstruct_instance
. In C, however, we have to include thestruct
keyword when declaringstruct_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 astruct
member that is a pointer to thestruct
.typedef struct struct_name { struct struct_name instance; struct_name_t instance2; /* invalid! The typedef isn't defined yet */ } struct_name_t;
You need to change this class:
typedef struct{
item_type item;
struct list* next;
}list;
to this:
struct list {
item_type item;
list* next;
};
Explanation: in the first example, you have anonymous structure, inside which struct list
is forward declared. So when compiler sees typedef on the next line it finds a name collision, because typedef is not the same as struct declaration in C++.
Since what you're doing is really defining a struct
and then creating an alias with the typedef
I think it's more readable to do this in the C case:
typedef struct list_ {
item_type item;
struct list_* next;
} list;
Use the following code
#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;
}
What C only warns against, C++ is likely to consider an error.
It's a programming cultural thing. C was very forgiving in not enforcing it's typing system. C++ is still quite forgiving, but you're doing something in C that even C++ won't forgive.
When you malloc that block of memory, cast it to a pointer to a list. That will covert the address (pointer) to a pointer of the right type.
Without that cast, you could have malloc'd the size of anything, and there's no telling if it was meant to be referenced by a list pointer or some other pointer.
精彩评论