Why Does 'Int ;' Compile Fine in C, But Not in C++

Why does `int ;` compile fine in C, but not in C++?

The C standard says

A declaration other than a static_assert declaration shall declare at least a declarator (other than the parameters of a function or the members of a structure or union), a tag, or the members of an enumeration.

C++ says

In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 9) or enumeration.

A violation of this in either language requires a diagnostic. The standards do not talk about compiler errors or warnings. A warning is a diagnostic.

Why do programs in C compile even when the return statement is missing?

C is an old language and at the time it was introduced, returning integers was common enough to be the default return type of a function. People later started realizing that with more complicated return types, it was best to specify int to be sure you are not forgetting the return type, but in order to maintain backwards compatibility with old code, C could not remove this default behavior. Instead most compilers issue warnings.

If the function reaches the end without a return statement an undefined value is returned except in the main function, 0 is returned. This has the same reason as above.

/* implicit declaration of printf as: int printf(int); */

/* implicit int type */
main()
{
printf("hello, world\n");
} /* implicit return 0; */

Why does an invalid use of C function compile fine without any warnings?

The compiler should issue a message for this program

int foo() {
return 3;
}

int main() {
int x;
foo(&x);
return x;
}

because the function foo called with an argument though its identifier list is empty. So the program has undefined behavior.

According to the C Standard *6.7.6.3 Function declarators (including prototypes)
)

14 An identifier list declares only the identifiers of the parameters
of the function. An empty list in a function declarator that is part
of a definition of that function specifies that the function has no
parameters.
The empty list in a function declarator that is not part
of a definition of that function specifies that no information about
the number or types of the parameters is supplied.

So the program is invalid.

You could make it a valid program the following way

int foo();

int main() {
int x;
foo(&x);
return x;
}

int foo( int *p ) {
return 3;
}

Though the compiler can issue a warning that the parameter p is not used.

In this case the function declaration that is not its definition means that there is no information about the number and types of parameters.

Opposite to C in C++ this declaration

int foo();

is equivalent to

int foo( void );

Why can I call a function in C without declaring it but not in C++?

The fact that the code "compiles" as a c program doesn't mean you can do it. The compiler should warn about implicit declaration of the function foo().

In this particular case implicit declaration would declare an identical foo() and nothing bad will happen.

But suppose the following, say this is

main.c

/* Don't include any header, why would you include it if you don't
need prototypes? */

int main(void)
{
printf("%d\n", foo()); // Use "%d" because the compiler will
// implicitly declare `foo()` as
//
// int foo()
//
// Using the "correct" specifier, would
// invoke undefined behavior "too".
return 0;
}

Now suppose foo() was defined in a different compilation unit1 foo.c as

foo.c

double foo()
{
return 3.5;
}

does it work as expected?

You could imagine what would happen if you use malloc() without including stdio.h, which is pretty much the same situation I try to explain above.

So doing this will invoke undefined behavior2, thus the term "Works" is not applicable in the understandable sense in this situation.

The reason this could compile is because in the very old days it was allowed by the c standard, namely the c89 standard.

The c++ standard has never allowed this so you can't compile a c++ program if you call a function that has no prototype ("declaration") in the code before it's called.

Modern c compilers warn about this because of the potential for undefined behavior that can easily occur, and since it's not that hard to forget to add a prototype or to include the appropriate header it's better for the programmer if the compiler can warn about this instead of suddenly having a very unexplicable bug.


1It can't be compiled in the same file because it would be defined with a different return type, since it was already implicitly declared

2Starting with the fact that double and int are different types, there will be undefined behavior because of this.

Why does this program compile fine in C++14 but not in a C++11 compiler?

It works on C++14 and also works on C++11. You're very likely using an out-of-date compiler.

There's a fixed bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50025) for your exact issue (cfr. DR 1288)

C++0x initialization syntax doesn't work for class members of reference type

Quoting from Jonathan Wakely

The original C++11 rules required a temporary to be created there and the reference member binds to that temporary.

Sources: Defect Report 1288

Program without semicolon compiles fine in C not in C++ why?

The semicolon is required by both languages. Specifically, C specifies the declaration of one or more structure members as

struct-declaration:
specifier-qualifier-list struct-declarator-list ;

and C++ specifies the declaration of one or more class member variables as

member-declaration:
attribute-specifier-seq<opt> decl-specifier-seq<opt> member-declarator-list<opt> ;

both of which require a semicolon at the end.

You'll have to ask the compiler writers why their C++ compiler is more strict than their C compiler. Note that the language specifications only require a "diagnostic" if a program is ill-formed, so it's legitimate either to issue a warning and continue compiling as if the semicolon were present, or to issue an error and stop.

It looks like your IDE is using GCC as its compiler; in which case you could use -Werror to convert warnings into errors, if you'd prefer stricter diagnostics.

Why does auto a=1; compile in C?

auto is an old C keyword that means "local scope". auto a is the same as auto int a, and because local scope is the default for a variable declared inside a function, it's also the same as int a in this example.

This keyword is actually a leftover from C's predecessor B, where there were no base types: everything was int, pointer to int, array of int.(*) Declarations would be either auto or extrn [sic]. C inherited the "everything is int" as a default rule, so you could declare integers with

auto a;
extern b;
static c;

ISO C got rid of this, but many compilers still accept it for backward compatibility. If it seems unfamiliar, then you should realise that a related rule is at work in

unsigned d;  // actually unsigned int

which is still common in modern code.

C++11 reused the keyword, which few if any C++ programmers were using with the original meaning, for its type inference. This is mostly safe because the "everything is int" rule from C had already been dropped in C++98; the only thing that breaks is auto T a, which no-one was using anyway. (Somewhere in his papers on the history of the language, Stroustrup comments on this, but I can't find the exact reference right now.)

(*) String handling in B was interesting: you'd use arrays of int and pack multiple characters in each member. B was actually BCPL with different syntax.

Why does flowing off the end of a non-void function without returning a value not produce a compiler error?

C99 and C++ standards require non-void functions to return a value, except main. The missing return statement in main will be defined (to return 0). In C++ it's undefined behaviour if execution actually reaches the end of a non-void function other than main, while in C it's only UB if the caller uses the return value.

This means functions can look like they might reach the end without returning a value, but actually can't reach the closing }. John Kugelman's answer shows some examples, like a noreturn function called from one side of an if. It's only undefined behaviour if execution actually does get to the end without reaching a return earlier. The rationale includes that checking if every real code path returns a value is quite difficult (without knowing which functions never return), so it's not illegal to compile a function like your example, only to actually call it like your main does.

As an extension, at least one compiler (MSVC) allows a return value to be set with inline assembly, but most others still require a return statement in functions that use inline asm.

From C++11 draft:

§ 6.6.3/2

Flowing off the end of a function [...] results in undefined behavior in a value-returning function.

§ 3.6.1/5

If control reaches the end of main without encountering a return statement, the effect is that of executing

return 0;

Note that the behaviour described in C++ 6.6.3/2 is not the same in C.


gcc will give you a warning if you call it with -Wreturn-type option.

-Wreturn-type Warn whenever a function is defined with a return-type that
defaults to int. Also warn about any
return statement with no return-value
in a function whose return-type is not
void (falling off the end of the
function body is considered returning
without a value), and about a return
statement with an expression in a
function whose return-type is void.

This warning is enabled by -Wall.


Just as a curiosity, look what this code does:

#include <iostream>

int foo() {
int a = 5;
int b = a + 1;
}

int main() { std::cout << foo() << std::endl; } // may print 6

This code has formally undefined behaviour, and in practice it's calling convention and architecture dependent. On one particular system, with one particular compiler, the return value is the result of last expression evaluation, stored in the eax register of that system's processor, if you disable optimization.

This seems to be a consequence of GCC internals with optimization disabled, because in that case it picks the return-value register if it needs any to implement a statement. With optimization enabled in C++ mode, GCC and clang assume this path of execution is unreachable because it contains undefined behaviour. They don't even emit a ret instruction, so execution falls into the next function in the .text section. Of course undefined behaviour means that anything could happen.



Related Topics



Leave a reply



Submit