Write a program that will print "C" if compiled as an (ANSI) C program, and "C++" if compiled as a C++ program
Asked Answered
S

10

18

Taken from http://www.ocf.berkeley.edu/~wwu/riddles/cs.shtml

It looks very compiler specific to me. Don't know where to look for?

Subsidence answered 10/1, 2010 at 19:14 Comment(0)
T
31

Simple enough.

#include <stdio.h>
int main(int argc, char ** argv) {
#ifdef __cplusplus
printf("C++\n");
#else
printf("C\n");
#endif
return 0;
}

Or is there a requirement to do this without the official standard?

Twink answered 10/1, 2010 at 19:17 Comment(8)
Just made the output agree with the question.Cyrille
That's correct. The C++ 1998 standard says: "The name __cplusplus is defined to the value 199711L when compiling a C++ translation unit". (Section 16.8: [cpp.predefined])Pullover
Of course, a really nasty C implementation could define __cplusplus.Cyrille
Nit-pick - that isn't a program; it is just the operational core of a program. :DShaveling
__cplusplus is defined to be 199711L only when the compiler is completely conforming. gcc, for example, still defines __cplusplus to be 1.Zymo
@stakx, KennyTM: Be careful. That number will change in C++0x.Johathan
@Neil, in the C99 standard, section 6.10.8.5 explicitly forbid the implementation from defining __cplusplus. But nothing of the sort in C89.Unreflective
@anon: I know at least one that does - MSVC in C (C89) mode (IIRC)Rapt
G
49

1. Abuse C++ automatic typedefs

(Note that the struct needs to be declared in an inner scope so that it takes precedence over the outer name in C++.)

#include <stdio.h>

int main(void)
{
    char x;

    {
        struct x { char dummy[2]; };
        printf("%s\n", sizeof (x) == 1 ? "C" : "C++");
    }
}

A similar version that doesn't rely on the ambiguity between sizeof (type) and sizeof (variable), using only types:

#include <stdio.h>

int main(void)
{
    typedef char t;

    {
        struct t { char dummy[2]; };
        printf("%s\n", sizeof (t) == 1 ? "C" : "C++");
    }
}

2. Abuse C++ struct/class equivalence, automatic typedefs, and automatically-generated default constructors

#include <stdio.h>

int isC = 0;
void Foo() { isC = 1; }

int main(void)
{
    struct Foo { int dummy; };
    Foo();
    printf("%s\n", isC ? "C" : "C++");
}

3. Abuse nested struct declarations in C

Also see Symbol clashing of inner and outer structs, C++ vs C

#include <stdio.h>

int main(void)
{
    typedef struct inner { int dummy; } t;

    {
        struct outer { struct inner { t dummy[2]; } dummy; };
        printf("%s\n",
               sizeof (struct inner) == sizeof (t)
               ? "C++"
               : "C");
    }
}

4. Abuse // comments

This won't work with C99 or with C89 compilers that support // as an extension.

#include <stdio.h>

int main(void)
{
    printf("%s\n",
           0 //* */
           +1
           ? "C++"
           : "C");
}

or alternatively:

    printf("%s\n",
           1 //* */ 2
           ? "C++"
           : "C");

5. sizeof differences with char literals

Note that this isn't guaranteed to be portable since it's possible that some hypothetical platform could use bytes with more than 8 bits, in which case sizeof(char) could be the same as sizeof(int). (Also see Can sizeof(int) ever be 1 on a hosted implementation?)

#include <stdio.h>

int main(void)
{
    printf("%s\n", sizeof 'a' == 1 ? "C++" : "C");
}

6. Abuse differences in when lvalue⇒rvalue conversions are performed

This is based off of the 5.16, 5.17, 5.18 example in the ISO C++03 standard, and it works in gcc but not in MSVC (possibly due to a compiler bug?).

#include <stdio.h>

int main(void)
{
    void* array[2];
    printf("%s\n",
           (sizeof (((void) 0), array) / sizeof (void*) == 1)
           ? "C"
           : "C++");
}

7. Abuse differences in the way C and C++'s grammars parse the ternary operator

This one isn't strictly legal, but some compilers are lax.

#include <stdio.h>

int main(void)
{
    int isCPP = 1;
    printf("%s\n", (1 ? isCPP : isCPP = 0) ? "C++" : "C");
}

(You also could check for the __cplusplus preprocessor macro (or various other macros), but I think that doesn't follow the spirit of the question.)

I have implementations for all of these at: http://www.taenarum.com/csua/fun-with-c/c-or-cpp.c

Godin answered 10/1, 2010 at 19:46 Comment(7)
I'm pretty sure the standard requires a char to be 1 byte, and a byte to be 8 bits.Candidate
No, char by definition is 1 byte, and a byte must be at least 8 bits, but it can be more. That's why CHAR_BIT exists.Godin
It requires char to be 1 byte. The number of bits in a byte is implementation-defined.Amitie
+1. Gotta love an answer where one option is non-portable and the other six are described as abuse :-)Mcneil
Could someone explain why the solution 1 works the way it does in C? I didn’t think that struct x { ... } was a legal Syntax in C. What does it do?Spense
@Spense struct x { ... } is the typical way to declare a struct in C with type struct x. If you wanted a typedef so you don't need to type struct all the time when using it, you'd do typedef struct x { ... } simpler_name. Note that the struct tag (x) and the typedef (simpler_name) live in separate C namespaces, so you also can do typedef struct x { ... } x. C++ creates such typedefs implicitly for struct and class, hence x in approach #1 resolves differently in C++ than in C.Godin
Thanks @Godin ! It's been so many years since I did something with C, so I guess I got confused... Just remembered that I always used typedef struct ..., but not why. So, in C++, I can access the struct as 'x', whereas in C it's 'struct x', so sizeof(x) is the size of the char x...Spense
A
44

We had to do a similar assignment at school. We were not allowed to use preprocessor (except for #include of course). The following code uses the fact that in C, type names and structure names form separate namespaces whereas in C++ they don't.

#include <stdio.h>
typedef int X;
int main()
{
    struct X { int ch[2]; };
    if (sizeof(X) != sizeof(struct X))
        printf("C\n");
    else
        printf("C++\n");
}
Amitie answered 10/1, 2010 at 19:25 Comment(1)
It is not true that in C++ they don't form separate namespaces, they are kept separate by the compiler, but the compiler will lookup symbols in both namespaces (first in the non-user defined types, then on the user defined types). There are a few ways of testing it, the simplest of which is typedef struct X {} Y; void X(); compiles, but if you add void Y(); you will get a compiler error: the symbol Y, in the non-user-defined namespaces is already used by the typedef.Grimsley
T
31

Simple enough.

#include <stdio.h>
int main(int argc, char ** argv) {
#ifdef __cplusplus
printf("C++\n");
#else
printf("C\n");
#endif
return 0;
}

Or is there a requirement to do this without the official standard?

Twink answered 10/1, 2010 at 19:17 Comment(8)
Just made the output agree with the question.Cyrille
That's correct. The C++ 1998 standard says: "The name __cplusplus is defined to the value 199711L when compiling a C++ translation unit". (Section 16.8: [cpp.predefined])Pullover
Of course, a really nasty C implementation could define __cplusplus.Cyrille
Nit-pick - that isn't a program; it is just the operational core of a program. :DShaveling
__cplusplus is defined to be 199711L only when the compiler is completely conforming. gcc, for example, still defines __cplusplus to be 1.Zymo
@stakx, KennyTM: Be careful. That number will change in C++0x.Johathan
@Neil, in the C99 standard, section 6.10.8.5 explicitly forbid the implementation from defining __cplusplus. But nothing of the sort in C89.Unreflective
@anon: I know at least one that does - MSVC in C (C89) mode (IIRC)Rapt
R
16
puts(sizeof('a') == sizeof(int) ? "C" : "C++");
Revet answered 10/1, 2010 at 19:25 Comment(2)
This is actually not guaranteed to work. sizeof(int) can be 1 if CHAR_BIT is at least 16.Amitie
Oak, in C, character literals are of type int, in C++ they're of type char.Amitie
S
3

Here's the program:

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("This is %s\n", sizeof 'a' == sizeof(char) ? "C++" : "C");
    return 0;
}

And here is some nice reading on C and C++ differences.

Schade answered 10/1, 2010 at 19:56 Comment(0)
H
2

Just look to see if the __STDC__ and __cplusplus compiler macros are defined.

Hemi answered 10/1, 2010 at 19:16 Comment(0)
Z
1

One word, __cplusplus.

Zymo answered 10/1, 2010 at 19:16 Comment(0)
G
1

I'm guessing the intent is to write something that depends on differences between the languages themselves, not just predefined macros. Though it's technically not absolutely guaranteed to work, something like this is probably closer to what's desired:

int main() { 
    char *names[] = { "C", "C++"};

    printf("%s\n", names[sizeof(char)==sizeof('C')]);
    return 0;
}
Gauge answered 10/1, 2010 at 19:27 Comment(9)
Same rant as for Matthew Slattery, this may not work on all platforms.Amitie
@avakar:Theoretically sort of true -- that's why I said It's not absolutely guaranteed to work. At the same time, the basic design of the C I/O library depends on EOF being different from any value of unsigned char. That implies that sizeof(int)>sizeof(unsigned char). Even though there was an idea that sizeof(char)==sizeof(int) should be allowed, it's really impossible to make C work that way, so the code above really does work dependably.Gauge
Don't confuse the result of sizeof with the type's allowed range of values.Godin
Jerry, I don't think you're correct. EOF is required to be negative, so it's true that it must be different from any value of unsigned char. However I don't see how it implies sizeof(int)>sizeof(unsigned char). int is not required to hold all values of unsigned char, only those values that can be read through getchar (which need not span the entire range of unsigned char). On PDP-10, where CHAR_BIT == 36, sizeof(char) indeed equals sizeof(int).Amitie
@Jamesdlin:For unsigned char, all bits are required to participate in the value representation (i.e. no padding bits, signaling bits, etc., are allowed. This means for unsigned char, the range must be defined directly by the number of bits. For int that's not required. In theory, that would mean it could use the same number of bits, but have a smaller range, except that having a smaller range is directly prohibited.Gauge
@avakar:Sorry, but no. The standard specifically requires that you can any stream of unsigned chars, write them to a file, and get the same values when you read them back. Thus, you must be able to read any value of unsigned char with getchar(). As an aside, I never heard of an implementation of C for the PDP-10 that used 36-bit chars. Most used 7-bit chars, stored 5 per word. That's enough for ASCII, but not the standard -- but I doubt there was ever a conforming implementation of C for the PDP-10.Gauge
@Amitie is correct in that this is not guaranteed to work: it is possible to have sizeof(int) == 1 and have all int values be valid character returns from getchar(), in which case EOF can also be a valid character, and testing whether EOF indicates end-of-file needs to be performed with feof() (and testing for error with ferror()). And Crays are an example of sizeof(int) == 1: they used to have all the integer types be 64 bits wide.Nudity
@jlk:Like I said, in theory, it's sort of correct. In reality, an implementation chars the same size as ints would break so much code nobody would ever use it. As far as Crays go, the idea of all integer types having the same size is an urban legend -- a rumor that remains widespread despite repeated testing showing that if it was ever true, it was only with a compiler that was never released to the outside world (and no evidence that it ever existed internally either).Gauge
@Jerry: See also my question #3861443Desiccator
D
0

For what it's worth, here's another answer:

char x[sizeof(char *)+2], y[1];
printf("%.*s\n", sizeof(1?x:y)-sizeof(char *)+1, "C++");
Desiccator answered 25/6, 2011 at 15:35 Comment(2)
I tried compiling using gcc and g++ following code pastebin.com/zsxX1NNN The files were saves as foo.c and foo.cpp respectively. It printed C both times. I don't think that the way you did it should work, because size of pointers depends on the architecture.Subsidence
Perhaps my idea was wrong. I thought the array would not decay to a pointer in C++ in this context...Desiccator
W
-1

You could try preprocessor directives, but that might not be what they are looking for.

Willful answered 10/1, 2010 at 19:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.