C Programming Language Quiz
C Programming Language Quiz
This quiz is about quirks of the programming language C and intended for fun and educational purpose. The one or the other question is more academic, i.e., it should make you think ;-)
Unless otherwise stated, the questions and corresponding answers are independent of a specific version of the C standard.<br>Thus the answers are the same considering C89 up to and including C17 and probably future releases.
If two pointers p and q of the same type point to the same address, then p == q must evaluate to true.
Yes
No
Short answer: Comparing two pointers which are derived from two different objects which are not part of the same aggregate or union object invokes undefined behavior.
Have a look at this post for a detailed discussion.
Consider the following code snippet where an object of type int is accessed through lvalues of type short and unsigned char:
int i = 42;
short *s = (short *) &i;<br>unsigned char *c = (unsigned char *) &i;
*s; // (A)<br>*c; // (B)
(A) and (B) invoke undefined behavior
(A) invokes undefined behavior but (B) is legal
(B) invokes undefined behavior but (A) is legal
(A) and (B) are legal
The rule of thumb is: accessing an object of type T through an lvalue of type U where T and U are not compatible (modulo few exceptions) invokes undefined behavior—according to the strict aliasing rules.<br>That means, in the example we accessed an object of type int through an lvalue of type short which leads to undefined behavior.<br>One exception to the rule is that a character pointer may alias any other pointer, i.e., any object may be accessed through a character pointer.
Note, only type unsigned char is guaranteed to have no padding bits and therefore has no trap representation which could invoke undefined behavior (since C11 also signed char is guaranteed to have no padding bits).
Thus (A) invokes undefined behavior whereas (B) is legal.
Have a look at this post for a detailed discussion.
It may make a difference whether an integer constant is given in decimal or hexadecimal format. In other words the meaning may be different.
Yes
No
In a nutshell the type of an unsuffixed decimal constant is always signed whereas of a hexadecimal constant the type may be signed or unsigned.
C17 § 6.4.4.1 Integer constants ¶ 5
The type of an integer constant is the first of the corresponding list in which its value can be represented.
Suffix<br>Decimal Constant<br>Octal or Hexadecimal Constant
none<br>int<br>long int<br>long long int
int<br>unsigned int<br>long int<br>unsigned long int<br>long long int<br>unsigned long long int
Thus for constants between INT_MAX+1 and UINT_MAX the type differs depending on whether the constant is given in decimal or hexadecimal format. In certain cases this might lead to unexpected effects. One example are functions with a variable number of arguments.
void foo(int n, ...);
void bar(void) {<br>foo(42, 0x80000000);<br>foo(42, 2147483648);
Depending on the ABI and width of integer types of a platform, the function calls may differ. For example, according to the ABI for the Arm 32-bit architecture an int and long are 32-bit wide and passed in one register whereas a long long is 64-bit wide and passed in two registers. Thus the call sides differ.
bar:<br>push {r3, lr}
// first call side<br>// second argument is in r1 only<br>mov r1, #-2147483648<br>movs r0, #42<br>bl foo
// second call side<br>// second argument is in r2 and r3<br>mov r2, #-2147483648<br>movs r3, #0<br>movs r0, #42<br>bl foo
pop {r3, pc}
Of course, there are plenty examples about arithmetic expressions which suffer from this nuance as e.g.
uint64_t x = 0x80000000
where x equals zero and y equals 232 assuming that int is 32-bit wide.
We may not only run into different behavior on the same platform but also across which results in portability problems. Arithmetic expressions may lead to different results on different platforms. For example, expression -1 evaluates to true on a platform where int is 32-bit wide and to false where int is 16-bit wide.
A further example where the type of a constant makes a difference are generic selections:
#define print_type_of(x) _Generic((x), \<br>unsigned int: puts("unsigned int"), \<br>long: puts("long"))
print_type_of(0x80000000);<br>print_type_of(2147483648);
While speaking of generic selections the whole story may also lead to subtle situations in C++ where we have overloaded functions:
void foo(unsigned int x);<br>void foo(long x);
void bar() {<br>foo(0x80000000);<br>foo(2147483648);
Another, probably more contrived example, is sizeof(0x80000000) == sizeof(2147483648) which evaluates to false on a platform where int is 32-bit wide.
The following two function declarations can be used interchangeably, i.e., they mean exactly the same:
int foo();<br>int foo(void);
Yes
No
The short answer is that the former declares a function with an unknown number and types of arguments while the latter declares a function without any argument, i.e., it is a nullary...