What Issues How to Expect Compiling C Code with a C++ Compiler

What issues can I expect compiling C code with a C++ compiler?

I've done something like this once. The main source of problems was that C++ is more strict about types, as you suspected. You'll have to add casts where void* are mixed with pointers of other types. Like allocating memory:

Foo *foo;
foo = malloc(sizeof(*foo));

The above is typical C code, but it'll need a cast in C++:

Foo *foo;
foo = (Foo*)malloc(sizeof(*foo));

There are new reserved words in C++, such as "class", "and", "bool", "catch", "delete", "explicit", "mutable", "namespace", "new", "operator", "or", "private", "protected", "friend", etc. These cannot be used as variable names, for example.

The above are probably the most common problems when you compile old C code with a C++ compiler. For a complete list of incompatibilities, see Incompatibilities Between ISO C and ISO C++.

You also ask about name mangling. In absence of extern "C" wrappers, the C++ compiler will mangle the symbols. It's not a problem as long as you use only a C++ compiler, and don't rely on dlsym() or something like that to pull symbols from libraries.

How common is it to mix C with C++?

It is fairly common. You can either

  • compile the C stuff with C++ compiler without any issues OK, others noted that this is definitely not true: What issues can I expect compiling C code with a C++ compiler?

or

  • use both object files, static and dynamic libraries compiled with C compiler but then you should be aware of name mangling that C++ compiler does and the C compiler does not (due to the fact that C does not allow name overloading but C++ does). In such case there is extern "C" linker directive to use C linkage, i.e. does not mangle names that has been compiled with C compiler.

For good explanation how to use C linkage in C++, refer e.g. to this answer here on SO: https://stackoverflow.com/a/1041880/12118546

This is the reason you often see code like this

#ifdef __cplusplus
extern "C" {
#endif

// all of your legacy C code here

#ifdef __cplusplus
}
#endif

Sample from: https://stackoverflow.com/a/12994075/12118546

Cryptic linker errors like these would result if you would forget about this:

...
/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x19): undefined reference to `foo()'
...

Sample from: https://stackoverflow.com/a/12573818/12118546

What is the cost of compiling a C program with a C++ compiler?

There's probably no 'cost', assuming that the two compilers are of equivalent quality. The traditional objection to this is that C++ is much more complex and so it's more likely that a C++ compiler will have bugs in it.

Realistically, this is much less of a problem that it used to be, and I tend to do most of my embedded stuff now as a sort of horrible C/C++ hybrid - taking advantage of stronger typing and easier variable declaration rules, without incurring RTTI or exception handling overheads. If you're taking a given compiler (GCC, etc) and switching it from C to C++ mode, then much of what you have to worry about is common to the two languages anyway.

Why doesn't C code compile properly in Visual Studio?

The problem with compiling C in Visual Studio

Visual Studio does not provide full support for ANSI C. If you want C code to be portable enough to compile with Visual Studio, you'll probably have to target C89 or have it compile as C++ code. The first option is unnecessarily restrictive, unless for some reason you really really love the '89 standard C and you hate all of the new features of later standards.

Compiling as C++

The second option, compiling as C++, can be achieved, as dialer mentions in his comment, by changing the target language type. You can do this by right-clicking the source file(s), and selecting Properties, navigate to C/C++ -> Advanced and changing the Compile As option to Compile as C++ code.

You can also specify the source file type as C++ by using the /Tp <filename> switch on the command line, or use the /TP switch to compile everything as C++.

Problems with Linking

If you're linking to a library written in C, the above fix can cause linking to fail. This is because, now that you're compiling your C files as C++, the function names will be mangled. When the compiler adds the library and tries to match the name of the function you called to one exported by the library, it will fail because the name exported by the library will not be mangled.

To combat this problem, C++ allows you to specify that specific names are exported with "C" linkage, which tells the compiler that the names are not mangled. This is usually done by prefixing the function declaration with extern "C", or placing everything in a block of

extern "C" {
/* header contents here */
}

Well-disciplined C library developers know about this problem and will use techniques, such as macros, to combat it. A common technique is to detect when the user is compiling as C++, and place macros similar to these at the beginning and end of a block of declarations in a header file:

#if defined (__cplusplus)
#define BEGIN_EXTERN_C extern "C" {
#define END_EXTERN_C }
#else
#define BEGIN_EXTERN_C
#define END_EXTERN_C
#endif

If you're using well-established and well-coded C libraries, the headers probably contain something similar to this. If not, you might need to do it yourself (and if the library is open-source, submit the changes as a patch!)

The future of C in Visual Studio

There is an MSDN blog post from July 2013, which announced that a large number of C99 features have been implemented for Visual Studio 2013. Part of the reason for this seems to be that the features are mentioned in parts of some C++ standards, so they would be required anyway. The new features include new math.h functions, new inttypes.h types and more. See the post for a full list.

An earlier post gives the following tidbits:

Additionally, some C99 Core Language features will be implemented in 2013 RTM:

  • C99 _Bool
  • C99 compound literals
  • C99 designated initializers
  • C99 variable declarations

Note that there are features missing, including:

  • The tgmath.h header is missing. C compiler support is needed for this header.

    • Note that the ctgmath header was added—this is possible because that header does not require the tgmath.h header—only the ccomplex and
      cmath headers.
  • The uchar.h header is missing. This is from the C Unicode TR.
  • Several format specifiers in the printf family are not yet supported.
  • The snprintf and snwprintf functions are missing from stdio.h and wchar.h.

Although you can expect them in the future:

We don't hate snprintf() (quite the contrary), we just missed it and ran out of time.

Note that other language features that don't have to do with the standard library are still not available.

It looks like standard C will receive more support in the future, although probably just because the implementation of more modern features is necessary to support C++11 and C++14.

<tgmath.h> and its associated compiler magic are special and I don't know our plans for them (as Pat's post explained, C++ has overloading/templates and doesn't need C compiler magic).

Can static_cast be used in C source code compiled by C compiler?

static_cast is part of the C++ language, and more importantly it is not part of the C language, so attempts to use static_cast<> should cause a C compiler to emit compile-time errors.

If you've seen it successfully used in "C source code" anyway... one likely explanation is that the "C source" code was being compiled as C++ source code by a C++ compiler. Since C++ is 99% backwards-compatible with C, most C source code can be compiled as C++ source code and will work, and in that scenario, static_cast could be part of the code and would compile.

Another possibility is that the compiler vendor got a bit sloppy and erroneously allowed a bit of their C++-specific functionality to execute even when compiling in the role of a C compiler, but hopefully that's not the case.

Can somebody explain me why is this C code not compiling my else if statement?

As Jean-François Fabre pointed out in the comments, your problem is the trailing semicolon in this #define:

#define OUT 0;

That causes your code to effectively look like this:

    if(c == '\n' || c == '\t' || c == ' ') 
state = OUT;
; // The "else" would go here, but you've already completed the "if"

else if(state == OUT) { // Now there is no "if" to associate with
state = IN;
++nw;
}

The preprocessor has the ability to cause all kinds of problems like this, which is a good reason to avoid it when possible. (This coming from a guy who has used cpp to do dirty, disgusting things).

In this case, an anonymous enum is the way to go:

enum {
OUT = 0,
IN = 1,
};

Also, this is a matter of style, but I always include curly braces, except for simple conditionals with no else:

// OK
if (ok)
foo();

// Not OK
if (ok)
foo();
else if (x == 3) {
lots();
of();
stuff();
}
else
bad();

Error while compiling C program with gcc in VSCode

Visual studio code has nothing to do with your issue, you are not compiling with it. Because it is an IDE (or source code editor), not a compiler. I guess you are using it on some Linux or POSIX system. BTW my preferred source code editor is GNU emacs. So your IDE is running some compilation commands (and you need to understand which ones and what these commands are doing). You could run these commands in a terminal (and that actually might be simpler).

As your console logs shows, you are compiling with GCC. Some gcc command has been started (by Visual studio code probably).

Read carefully about Invoking GCC. Order of arguments matters a lot!

You should compile your code with

gcc -Wall -Wextra -g q2.c -lm -o q2

Let me explain this a bit:

  • gcc is your compiler front-end (the actual compiler is cc1 but you never use it directly; you ask gcc to run it)

  • -Wall asks for almost all warnings

  • -Wextra asks for extra warnings. You'll be happy to get them

  • -g asks for debugging information in DWARF. You really want to be able to use the gdb debugger, and gdb practically needs debugging information.

  • q2.c is the source file of your sole translation unit

  • -lm is for your math library. You are using log(3) and its documentation mention that.

  • -o q2 tells gcc to put the executable in q2 (the actual work is done by the ld linker invoked by gcc)

How to configure visual studio code to use that command is your business. You could otherwise type the above command in a terminal. Then you can run your q2 program by typing ./q2 in a terminal for your shell (and you could use gdb on it).

Notice that gcc is starting other programs (like cc1, as, ld). If you want to understand which ones, insert -v after gcc in the command above.

Be sure to read the documentation of every function you are using (so read printf(3), scanf(3), log(3) at least...) and of every program you are using (e.g. of gcc and of Visual studio code).

Once you'll write bigger programs made of several translation units (e.g. foo.c, bar.c, gee.c), you would want to use some build automation tool (because compiling all of them every time with gcc -Wall -Wextra -g foo.c bar.c gee.c -lm -o qqq is possible, but inconvenient). You could learn to use GNU make (or ninja).

Read How to debug small programs. Don't expect your program to work as you want at first.

BTW, study the source code of some existing free software programs (but start with simple projects, e.g. on github, of less than a hundred thousand lines). This could teach you many useful things.



Related Topics



Leave a reply



Submit