Proper C, improper C++

I have recently been asked if it’s possible to write a legit C program that wouldn’t compile in C++ mode. Obviously, my answer was positive, but the question made me wonder just how many incompatibilities there are between these languages, and how many of these I can cram into a single test case? I’ve decided to try my hand at writing such test case, and after a while of research I ended up with a fairly decent attempt.

It does compile in C, but it won’t compile in C++.

Here’s the full code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void fortran_style(var1,var2,var3) float var2; {
        printf("var1 = %d,\nvar2 = %.2f,\nvar3 = %d\n",var1,var2,var3);
}
 
float multiple_arguments(){return 0.5f;}
 
const int extern_const_int;
 
typedef enum { 
        and,and_eq,alignas,alignof,bitand,bitor,bool,catch,char16_t,char32_t,class,
        compl,constexpr,const_cast,decltype,delete,dynamic_cast,explicit,export,
        false,friend,mutable,namespace,new,noexcept,not,not_eq,nullptr,operator,or,
        or_eq,private,protected,public,reinterpret_cast,static_assert,static_cast,
        template,this,thread_local,throw,true,try,typeid,typename,using,virtual,
        wchar_t,xor,xor_eq
} keywords;
 
typedef struct {
        int size;
        int data[];
} variable_sized_struct;
 
implicit_int = 4;
 
main(){
        volatile int dynamic_val = 55;
        int variable_length_array[dynamic_val];
        auto float scoped_variable = multiple_arguments(implicit_int,implicit_int,implicit_int,implicit_int);
        keywords int_to_enum = 5;
        fortran_style(implicit_int,scoped_variable,int_to_enum);
        void *void_ptr = malloc(sizeof(variable_sized_struct)+sizeof(int[dynamic_val]));
        variable_sized_struct *vssptr;
        vssptr = void_ptr;
        free(void_ptr);
        return 0;
}

Let me explain all features and archaisms that prevent proper compilation in C++ mode. First of all, I’ve used all C++ keywords (and if I missed any, please let me know). That prevents C++ compilation for obvious reasons. I’ll ignore C++ keywords in further explanations.

I’ve also used stdlib functions printf, malloc and free without including their headers. It’s a bad practice, because arguments for functions that don’t have proper declarations will default to int, which can cause problems on architectures where following expression is true: sizeof(int) != sizeof(type*). It’s also disallowed after C89 and in C++.

Now to the real interesting stuff. Below is a fine example of archaic function declaration (and definition) syntax. Please note that I only specify type for one parameter — others are int by default. For some reason compiler won’t perform type checks for arguments of functions declared this way. That’s why any use outside of legacy code is, to say the least, stupid. It is, nonetheless, correct under C89 standard. C99, C1x and any C++ standard ban it, however many C compilers allow this syntax even in C99 mode.

1
2
3
void fortran_style(var1,var2,var3) float var2; {
        printf("var1 = %d,\nvar2 = %.2f,\nvar3 = %d\n",var1,var2,var3);
}

In C const variables have external linkage by default. It is not the case with C++, where linkage is internal by default, so without explicitly declaring it as extern, it won’t compile.

7
const int extern_const_int;

C++ doesn’t allow structs or classes to contain incomplete types, but C99 makes a special case for incomplete array types (called flexible array members) at the end of the struct definition (6.7.2.1#16). I miss this feature in C++, it’s elegant and quite popular — even if C++ can do better in most of the cases. Fortunately, most compilers allow such syntax even in C++ mode by default.

18
19
20
21
typedef struct {
        int size;
        int data[];
} variable_sized_struct;

In C89 variables without type specified default to int. It’s not correct code in C++ or C99 and above.

23
implicit_int = 4;

C++ requires main to be declared as returning int; C doesn’t. That said, the operating system will interpret the return value as an integer anyway.

25
main(){

C99 introduces Variable-Length Arrays. Even though it might be compiled properly by some compilers (like gcc by default) it is not proper C++ code, under any standard.

26
27
        volatile int dynamic_val = 55;
        int variable_length_array[dynamic_val];

In C, auto informs the compiler that a variable has automatic storage duration. It may be used only within functions and function argument declarations. It is the default behaviour and that’s why it was very rarely used. ISO C++ committee decided to recycle it as automatically deduced type (and one can’t declare a variable with two types, so “auto float public” won’t compile).

28
auto float scoped_variable=multiple_arguments(implicit_int,implicit_int,implicit_int,implicit_int);

Beyond that, function multiple_arguments was called with four parameters, even though its only declaration is float private(). This is allowed in C; to obtain a function declaration that will take no arguments one needs to explicitly say so, i.e.: float private(void). C++, however, acts as if void was declared and will not allow any arguments to be passed.

On a side note, auto var = 42; has the exact same meaning both in C and C++11, but the auto identifier’s role is completely different. In C it specifies var‘s storage class (which defaults to auto anyway) and type defaults to int; in C++11 it’s responsible for type deduction and storage class defaults to auto again.

C allows implicit void* to type* conversions. In C++, for technical (and probably ideological as well) reasons, it’s impossible. To make this example work, one would have to change line 33 to vssptr = (variable_sized_struct*)void_ptr;.

31
32
33
        void *void_ptr = malloc(sizeof(variable_sized_struct)+sizeof(int[dynamic_val]));
        variable_sized_struct *vssptr;
        vssptr = void_ptr;

Besides that, I’m also using struct variable_sized_struct with flexible array member data. Accodring to C99 standard, its typical use case is exactly what I did and for most purposes it’s equivalent to such declaration:

typedef struct {
        int size;
        int data[dynamic_val];
} variable_sized_struct;

That’s all, I guess I created a quite lengthy post for something so silly, but it was very educational for me.

11.12.2011: Added variable sized struct to the example;
13.12.2011: Made the article a little more readable by using meaningful names;

5 thoughts on “Proper C, improper C++

  1. hello there and thank you for your info – I have definitely picked up something new from right here. I did however expertise a few technical issues using this web site, as I experienced to reload the site a lot of times previous to I could get it to load properly. I had been wondering if your web hosting is OK? Not that I am complaining, but slow loading instances times will sometimes affect your placement in google and could damage your quality score if advertising and marketing with Adwords. Anyway I’m adding this RSS to my e-mail and could look out for a lot more of your respective intriguing content. Make sure you update this again soon..

Leave a Reply

Your email address will not be published.