3

I am learning C, and after starting out learning C++ as my first compiled language, I decided to "go back to basics" and learn C.

There are two questions that I have concerning the ways each language deals with functions.

Firstly, why does C "not care" about the scope that functions are defined in, whereas C++ does?

For example,

int main()
{
    donothing();
    return 0;
}

void donothing() { }

the above will not compile in a C++ compiler, whereas it will compile in a C compiler. Why is this? Isn't C++ mostly just an extension on C, and should be mostly "backward compatible"?

Secondly, the book that I found (Link to pdf) does not seem to state a return type for the main function. I check around and found other books and websites and these also commonly do not specify return types for the main function. If I try to compile a program that does not specify a return type for main, it compiles fine (although with some warnings) in a C compiler, but it doesn't compile in a C++ compiler. Again, why is that? Is it better style to always specify the return type as an integer rather than leaving it out?

Thanks for any help, and just as a side note, if anyone can suggest a better book that I should buy that would be great!

james
  • 477
  • 2
  • 5
  • 10
  • [The Definitive C Book Guide and List](http://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) – potrzebie Oct 05 '12 at 10:32
  • "Isn't C++ mostly just an extension on C" -- you won't do yourself a favor if you treat C++ as "C with extras". It's completely different how you *should* program in it, really. – Xeo Oct 05 '12 at 14:06

6 Answers6

6

Firstly, why does C "not care" about the scope that functions are defined in, whereas C++ does?

Actually, C does care. It’s just that C89 allows implicitly declared functions and infers its return type as int and its parameters from usage. C99 no longer allows this.

So in your example it’s as if you had declared a prototype as

int dosomething();

The same goes for implicit return types: missing return types are inferred as int in C89 but not C99. Compiling your code with gcc -std=c99 -pedantic-errors yields something similar to the following:

main.c: In function 'main':
main.c:2:5: error: implicit declaration of function 'donothing' [-Wimplicit-function-declaration]
main.c: At top level:
main.c:5:6: error: conflicting types for 'donothing'
main.c:2:5: note: previous implicit declaration of 'donothing' was her

For the record, here’s the code I’ve used:

int main() {
    donothing();
    return 0;
}
void donothing() { }
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 2
    I'm not sure his code should compile in C. As you said, the implicit declaration of the function has a return type of `int`. When the compiler then encounters `void dosomething()`, it should complain about incompatible return types. – James Kanze Oct 05 '12 at 09:58
  • @James Ah, my fault, I’ve corrected that. But yes, it still compiles, even with conflicting types. – Konrad Rudolph Oct 05 '12 at 10:11
  • 1
    C99 does _not_ allow this, `example.c:3:5: error: implicit declaration of function ‘donothing’`, `example.c:7:6: error: conflicting types for ‘donothing’`. I don't know how other implementations handle that, but gcc allows a lot of forbidden stuff unless you compile with `-pedantic-errors`. – Daniel Fischer Oct 05 '12 at 10:26
  • @Daniel Which compiler are you using? I’m using GCC 4.7 and even with `-pedantic` it still compiles (actually, the above warning messages were generated by GCC 4.2 but GCC 4.7 yields equivalent output, only the phrasing differs). – Konrad Rudolph Oct 05 '12 at 10:27
  • 1
    I have 4.6, but you need `-pedantic-errors`, gcc's flag names are often blatant lies (`-Wall`). – Daniel Fischer Oct 05 '12 at 10:28
  • @Daniel Interesting. But does this mean that C99 actually forbids it? Unfortunately I have to profess ignorance here (I never use C myself). – Konrad Rudolph Oct 05 '12 at 10:33
  • Yes, it does mean C99 actually forbids it. I'd have to search for the relevant paragraphs in the standard, don't know them off the top of my head, but the list of major changes in the Foreword contains `remove implicit int` and `remove implicit function declaration`. – Daniel Fischer Oct 05 '12 at 10:36
  • @DanielFischer Thanks for this. I’ve updated my answer. I had found the foreword in the C standard before but I was unsure whether the removal was merely about a suggestion to the compiler or mandatory. – Konrad Rudolph Oct 05 '12 at 10:46
  • 4
    It's because C is a drunk college girl in a bar, while C++ is a prim Sunday school teacher in sensible shoes. – Bob Jarvis - Слава Україні Oct 05 '12 at 11:01
  • Sensible shoes sound just a little bit too javaêsque. In fact it goes nicely with _'javaêsque'_, don't you think? (Buy them at a `SensibleFactoryBoutiqueProxy`) – sehe Oct 05 '12 at 11:43
  • The code compiles with c89, but not with c99. gcc by default compiles against the c89 standard, so it accepts the code. gcc is not fully c99 compliant; even if you specify `--std c99`, it still accepts the code. Clang is c99 compliant; it rejects the change from the implied `int` return value to the specified `void` return value. – David Hammen Oct 05 '12 at 16:14
3

It's because C++ supports optional parameters. When C++ sees donothing(); it can't tell if donothing is:

void donothing(void);

or

void donothing(int j = 0);

It has to pass different parameters in these two cases. It's also because C++ is more strongly typed than C.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • The implicit declaration of a function is `int function_name()`. There's a huge difference between that and `int function_name(void)`. The `function_name()` is ancient K&R C style. You can plug any number of arguments in and the compiler will not complain. It can't complain. That `()` denotes an unspecified number of arguments. – David Hammen Oct 05 '12 at 16:18
2

he above will not compile in a C++ compiler, whereas it will compile in a C compiler. Why is this?

Because C++ requires a declaration (or definition) of the function to be in scope at the point of the call.

Isn't C++ mostly just an extension on C

Not exactly. It was originally based on a set of C extensions, and it refers to the C standard (with a few modifications) for the definitions of the contents of standard headers from C. The C++ "language itself" is similar to C but is not an extension of it.

and should be mostly "backward compatible"?

Emphasis on "mostly". Most C features are available in C++, and a lot of the ones removed were to make C++ a more strictly typed language than C. But there's no particular expectation that C code will compile as C++. Even when it does, it doesn't always have the same meaning.

I check around and found other books and websites and these also commonly do not specify return types for the main function

The C and C++ standards have always said that main returns int.

In C89, if you omit the return type of a function it is assumed to be int. C++ and C99 both lack this implicit int return type, but a lot of C tutorial books and tutorials (and compilers and code) still use the C89 standard.

C has some allowances for implementations to accept other return types, but not for portable programs to demand them. Both languages have a concept of a "freestanding implementation", which can define program entry and exit any way it likes -- again, because this is specific to an implementation it's not suitable for general teaching of C.

IMO, even if you're going to use a C89 compiler it's worth writing your code to also be valid C99 (especially if you have a C99 compiler available to check it). The features removed in C99 were considered harmful in some way. It's not worth even trying to write code that's both C and C++, except in header files intended for inter-operation between the languages.

I decided to "go back to basics" and learn C.

You shouldn't think of C as a prerequisite or "basic form" of C++, because it isn't. It is a simpler language, though, with fewer features for higher-level programming. This is often cited as an advantage of C by users of C. And an advantage of C++ by users of C++. Sometimes those users are the same people using the languages for different purposes.

Typical coding style in C is different from typical coding style in C++, and so you might well learn certain basics more readily in C than in C++. It is possible to learn low-level programming using C++, and the code you write when you do so may or may not end up looking a lot like C code.

So, what you learn while learning C may or may not inform the way you write C++. If it does, that may or may not be for the better.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • "C++ requires that a function prototype must be in scope to call a function" - it's not quite that strong. The requirement is that there must have been a prior declaration or definition. If you write the function before the call to it, there's no need for a prototype (i.e., declaration). – Pete Becker Oct 05 '12 at 13:53
  • @Pete: OK, my impression was that in C++ a function definition provides a prototype, but if a prototype implies an non-definition declaration then fair enough. – Steve Jessop Oct 05 '12 at 13:58
  • Formally, "prototype" isn't used in the C++ standard except as part of "function prototype scope", which refers to names in a "function declaration except the declarator of a function definition" [basic.scope.proto]/1. That's standardese for "declaration that is not a definition". – Pete Becker Oct 05 '12 at 14:39
2
int main() {
    donothing();
    return 0;
}
void donothing() { } 

Nice minimum working example.

With gcc 4.2.1, the above code gets a warning regarding the conflicting types for void donothing() with default compiler settings. That's what the C89 standard says to do with this kind of problem. With clang, the above code fails on void donothing(). The C99 standard is a bit stricter.

It's a good idea to compile your C++ code with warnings enabled and set to a high threshold. This becomes even more important in C. Compile with warnings enabled and treat implicit function declarations as an error.

Another difference between C and C++: In C++ there is no difference between the declarations void donothing(void); and void donothing(); There is a huge difference between these two in C. The first is a function that takes no parameters. The latter is a function with an unspecified calling sequence.

Never use donothing() to specify a function that takes no arguments. The compiler has no choice but to accept donothing(1,2,3) with this form. It knows to reject donothing(1,2,3) when the function is declared as void donothing(void).

David Hammen
  • 32,454
  • 9
  • 60
  • 108
1

C++ has changed these rules on purpose, to make C++ a more typesafe language.

C.1.4 Clause 5: expressions [diff.expr]
5.2.2
Change: Implicit declaration of functions is not allowed
Rationale: The type-safe nature of C++.
Effect on original feature: Deletion of semantically well-defined feature. Note: the original feature was labeled as “obsolescent” in ISO C.
Difficulty of converting: Syntactic transformation. Facilities for producing explicit function declarations are fairly widespread commercially.
How widely used: Common.

You can find other similar changes in appendix C of this Draft C++ standard

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
0

Isn't C++ mostly just an extension on C

No. If you think of C++ as "C with Classes", you're doing it very, very wrong. Whilst strictly, most valid C is valid C++, there's virtually no good C that's good C++. The reality is that good C++ code is vastly different to what you'd see as good C code.

Firstly, why does C "not care" about the scope that functions are defined in, whereas C++ does?

Essentially, because not enforcing the same rules as C++ makes doing this in C hideously unsafe and in fact, nobody sane should ever do that. C99 tightened this up, along with implicit-int and other defects in the C language.

Puppy
  • 144,682
  • 38
  • 256
  • 465