Why is this not giving compile error - c

#include <stdio.h>
int main()
{
int i = 10;
int *const p = &i;
foo(&p);
printf("%d\n", *p);
}
void foo(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}
p is a constant pointer to a variable x and can't point to other variables. But why don't we get error here and the output is 11 11?

This code doesn't violate a constraint, so the only thing you could expect from a compiler is a warning, which e.g. gcc gives you:
constptr.c: In function ‘main’:
constptr.c:6:9: warning: implicit declaration of function ‘foo’ [-Wimplicit-function-declaration]
foo(&p);
^
constptr.c: At top level:
constptr.c:9:10: warning: conflicting types for ‘foo’
void foo(int **p)
^
constptr.c:6:9: note: previous implicit declaration of ‘foo’ was here
foo(&p);
^
If you have a close look at these warnings, you see it's about an implicit declaration: foo() doesn't have a prototype before it is used, this isn't allowed by newer C standards, but compilers still support it for backwards compatibility. In that case, the compiler assumes the prototype is int foo(int). That's the reason for the next warning (conflicting types for foo).
If you correctly introduce a prototype like this:
#include <stdio.h>
void foo(int **p);
int main()
{
int i = 10;
int *const p = &i;
foo(&p);
printf("%d\n", *p);
}
void foo(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}
You get the warning one would expect:
constptr.c: In function ‘main’:
constptr.c:7:13: warning: passing argument 1 of ‘foo’ discards ‘const’ qualifier from pointer target type
foo(&p);
^
constptr.c:2:10: note: expected ‘int **’ but argument is of type ‘int * const*’
void foo(int **p);
^
So now the compiler warns you about the conversion dropping the const. C doesn't force you to write correct code, but you should anyways -- the warning tells you your code is incorrect and might invoke undefined behavior (as is the case here).
Although not related to your question, your code contains an even nastier case of undefined behavior: your last line in main() (the printf() line) dereferences a pointer that now points to an object of automatic storage duration (aka: the local variable j) that has gone out of scope and therefore doesn't exist any more!. It's unlikely that your compiler can warn you about this, still it's a recipe for desaster. So, always be very careful when writing C code.
Adding a very generic piece of advice here: Questions like this are often asked by people used to "modern" programming languages that are completely defined (e.g. Java, C#, and a lot more): Your code is either correct (and defined) or wrong and if it is wrong, you get either compilation errors or runtime exceptions. That's not how C works! In C, any code that adheres to the C grammar and doesn't violate a language constraint can be compiled and a lot of errors just lead to undefined behavior (which means anything could happen when executing that code). This means C "trusts" the programmer to do the correct thing -- the advantage is the possibility to create quite efficient native machine language code from a C source file, the downside is you're responsible yourself to make sure your code is actually correct. A best practice to start with is to always enable any warnings your C compiler gives you (a good setting for gcc would be e.g. -std=c11 -Wall -Wextra -pedantic) and always fix any warnings that come up.

Apart from the "modifying something const"-thing, the code as-is should actually compile with errors or warnings as the call of foo without having "forward-declared" it lets the compiler assume a different prototype (i.e. int foo(int)) than the actual definition then provides (i.e. int foo(int**); this could lead to "conflicting types for foo" - warning/error or something similar).
After having fixed this, with the following code, you should at least get a compiler warning (if not, turn on warnings or get a better compiler):
void foo3(int **p);
int main()
{
int i = 10;
int *const p = &i;
foo3(&p); // passing int * const * to parameter of type int ** discards const qualifiers
printf("%d\n", *p);
}
void foo3(int **p)
{
int j = 11;
*p = &j;
printf("%d\n", **p);
}
Thereby you get undefined behaviour because of modifying a constant value (even if the compiler does not give you an "error").
BTW: accessing a pointer that points to a (local) object which's lifetime has ended (as you do with *p = &j and the printf in main) is undefined behaviour.

Related

Pointer arithmetic on pointers to variably-modified types

Is the following valid C code? (godbolt)
#include <stddef.h>
ptrdiff_t f(size_t n, void *x, void *y)
{
if (!n) return 0;
typedef unsigned char element[n];
element *a = x, *b = y;
return a - b;
}
With -Werror=pointer-arith clang loudly complains about
<source>:8:14: error: subtraction of pointers to type 'element' (aka 'unsigned char [n]') of zero size has undefined behavior [-Werror,-Wpointer-arith]
return a - b;
~ ^ ~
while gcc compiles the code without complaint.
What is the undefined behavior that clang thinks is occuring? The
possibility of the subtraction being zero and therefore not a valid
pointer to an element of the array or something different? There's no
array access being performed, right? So that shouldn't be the case...
If the code does exhibit undefined behavior, is there a simple way to
modify the code to be fully conforming, while still using pointers to
VM-types?
The code posted is valid C code if VLAs are supported by your compiler. Note that VLAs introduced in C99 have become optional in the latest version of the C Standard.
Both gcc and clang compile the code correctly as can be verified using Godbolt's compiler explorer.
Yet clang issues a warning regarding potential undefined behavior if the value of the n argument happens to be null, failing to identify that this case has been handled with an explicit test. The problem is not the value of the subtraction but the size of the type, which would be 0 if n == 0. This warning is not really a bug, more a quality of implementation issue.
It is also arguable that a - b is only defined if both a and b point to the same array or just past the last element of it. Hence x and y must verify this constraint and have type unsigned char (*)[n] or compatible. There is an exception to this rule for accessing any type as an array of character type, so passing pointers to the same array of int would be fine, but it would be incorrect (although probably harmless) to call f this way:
int x, y;
ptrdiff_t dist = f(sizeof(int), &x, &y);
Compilers are free to issue diagnostic messages to attract the programmer's attention on potential problems, indeed such warnings are life savers in many cases for beginners and advanced programmers alike. Compiler options such as -Wall, -Werror and -Weverything are quite useful, but in this particular case, one will need to add -Wno-pointer-arith to allow clang to compile this function if -Werror is also active.
Note also that the same result can be obtained with a C89 function:
ptrdiff_t f89(size_t n, void *x, void *y)
{
if (!n) return 0;
unsigned char *a = x, *b = y;
return (a - b) / n;
}

Returning address of a local variable error

I typed this code:
void main()
{
int *a;
a = foo();
printf("%d\n", *a);
}
int* foo()
{
int b = 10;
return &b;
}
After compiling, there were 2 problems:
1. error - Conflicting type for foo()
2. warning - function returns address of local variable
But then I write this
int* foo();
void main()
{
int *a;
a = foo();
printf("%d\n", *a);
}
int* foo()
{
int b = 10;
return &b;
}
Now, it does not give an error after compiling, which is obvious, but, why does the compiler not give a warning for returning address of local variable?
How does declaring or not declaring a function affects returning address of a local variable?
Sorry for not mentioning before, but I am using GNU GCC compiler
The C standard does not require compilers to give warnings except for syntax errors.
ISO/IEC 9899:1999, 5.1.1.3:
A conforming implementation shall produce at least one diagnostic
message (identified in an implementation-defined manner) if a
preprocessing translation unit or translation unit contains a
violation of any syntax rule or constraint, even if the behavior is
also explicitly specified as undefined or implementation-defined.
Diagnostic messages need not be produced in other circumstances.
The behavior you described is inconsistent (i.e. not nice) but valid/standard-compliant.
Compilers aren't required to give you warnings or errors except for syntax errors. Semantic errors may or may not be detected by a compiler, depending on the circumstances.
Thats extra though and not required by the standard.
In your case the inconsistency is a little unfortunate.
You must declare your function before calling it. In first case you called foo and then you defined it. Try it like this and it should work:
int* foo()
{
int b = 10;
return &b;
}
void main()
{
int *a;
a = foo();
printf("%d\n", *a);
}

What does void function in C return?

#include <stdio.h>
void main()
{
int k = m();
printf("%d", k);
}
void m()
{
printf("hello");
}
Output
hello5
What is the void function returning here?
If there is no printf() then the output is 1.
What is happening here?
A void function does not return anything. Your program invokes undefined behavior because it implicitly defines m to have return type int (in C89, if a function is called before it is declared, it's implicitly assumed to have return type int), but then defines it with return type void.
If you add a forward-declaration for m, the compiler will correctly complain that you're trying to use the return value of a void function, which is not possible.
In both cases (void m with caller expecting int, void main with caller-in-C-library expecting int), this is undefined behavior, and your compiler should have chewed you out, e.g. [GCC 4.8 with -Wall]:
test.c:3:6: warning: return type of ‘main’ is not ‘int’
void main()
^
test.c: In function ‘main’:
test.c:7:5: warning: implicit declaration of function ‘m’
int k = m();
^
test.c: At top level:
test.c:13:6: warning: conflicting types for ‘m’
void m()
^
test.c:7:13: note: previous implicit declaration of ‘m’ was here
int k = m();
^
"Implicit declarations" exist only for backward compatibility with pre-C1989 programs, which are more than 20 years out of date at this point. If you had written this code in a more modern style you would've gotten a hard error:
#include <stdio.h>
extern void m(void);
int main(void)
{
int k = m();
printf("%d", k);
return 0;
}
void m(void)
{
printf("hello");
}
⇒
test.c: In function ‘main’:
test.c:7:13: error: void value not ignored as it ought to be
int k = m();
^
The key addition is the extern void m(void); line. The compiler isn't allowed to look ahead to the definition of m when it checks main for errors, so you need a forward declaration to tell it what m's type really is. You should also know that in C (but not C++), void m() means "m takes an unspecified number of arguments". To declare a function taking no arguments, which is what you wanted, you have to write void m(void).
I believe what is happening on your specific system is that the return value of printf, which your code officially ignores, is being left in the return-value register and thus interpreted as the return value of m, but you cannot rely on anything predictable happening. That is what 'undefined behavior' means.
This is undefined behavior.
The reason the compiler lets you do the assignment is that there is no prototype of m available to it. When this happens, C standard says that the function must return int. Since in this case the actual function return type is void, the behavior is undefined. The value that you get in variable k is entirely arbitrary.
The return value of a function usually stored in the one of the CPU registers, EAX for x86. As C it just a friendly assembler, you can always read the value from the register. And you will get whatever was there. Efficiently, int k = m() tells program to store value of register EAX in the variable k.
You have not declared void m(); before calling it in main.
Some compilers assume the return type of unknown function symbols as int (unknown at the time of compilation of course) . In fact, your compiler has probably issued a compilation warning on this.
If you declare this function before main (where it should be declared), then you will probably get a compilation error.

Why this C program complies and runs

With curiosity of the definition and scope of typedef I have written below C code in 2 .c files:
main.c
#include <stdio.h>
int main()
{
int a = 5, b = 6;
printf("a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
}
swap.c
typedef T;
void swap(T* a, T* b)
{
T t = *a;
*a = *b;
*b = t;
}
To my surprise, the code files could be compiled with Visual Studio C compiler (cl.exe /Tc main.c swap.c)
And the program runs correctly! From my understanding, typedef requires 2 parameters, but why this code compiles and runs?
For further analysis, in the main function, I declared another 2 float variables and try also to swap both after swapping the 2 integers, but this time it fails to compile (with cl.exe). What amazing is the code could be compiled and run with Tiny C (tcc.exe main.c swap.c), so it works like a template method!
Typedef is actually a declaration (it creates aliases for existing types), and is in no way limited to two 'parameters'. See Typedef Declarations (C) and Fundamental typedef operand syntax.
If you write typedef T;, you are declaring T to be an unspecified type (called "no specified type" in the C89 spec). It's a little (and only a very little, but conceptually this might help you) like #define X defines X but the preprocessor will replace it with the empty string (i.e. remove X).
So, you are typedefing T to be unspecified, which makes the arguments to your swap function of unspecified type.
What you are seeing here is that in C89 (but not C99, where it results in undefined behaviour - contrast ANSI 3.5.2 with ISOC99 6.7.2), unspecified types default to int, which is why your method works for integers but not floats in Visual Studio (presumably it disallows implicit integer typing by default). GCC will compile it with floats, though, provided you aren't using -Werror, which you probably should be.
I strongly suggest turning on some warnings: -Wall in gcc will spit out the following, among other things
swap.c:1:9: warning: type defaults to ‘int’ in declaration of ‘T’ [-Wimplicit-int]
The reason it "works" on floats is because floats and ints are probably the same size (32 bits) on your machine. Try it with double. Or char. Or short.
swap is really like this:
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
with you calling
swap((int*)&a, (int*)&b);
Try it and compare the results for yourself.
Edit: I just tried it in tcc. -Wall does not alert you to the implicit int typing, sadly.
In C90 (which is what MSVC follows as a baseline when compiling C code), one of the possible type specifiers is (C90 6.5.2 "Type specifiers" - emphasis added):
int, signed, signed int, or no type specifiers
so if no type specifier is provided in a declaration (including a typedef) then the type defaults to int. this is commonly known as an "implicit int" declaration. Note that C99 removed support for implicit int (by default GCC only issues a warning for this when compiling in C99 mode).
Your typedef:
typedef T;
is equivalent to:
typedef int T;
So your swap() defintion is equivalent to:
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
As it happens, when calling a function that hasn't been declared or prototyped (as occurs when you call swap() in main.c), the compiler will apply default argument promotions to arithmetic arguments and assume the function returns an int. Your call to swap() passes two arguments of type int*, so no promotion occurs (they're pointer arguments, not arithmetic). That happens to be exactly what the definition for swap() expects, so the function call works (and is well defined behavior).
Now, the calling code expects swap() to return an int since no declaration was seen, and your swap() function doesn't return anything (void). That is undefined behavior, but in this case there's no apparent problem (though it's still a bug in your code). However, if you change the definition of swap() so that it returns int:
int swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
the undefined behavior goes away, even though swap() doesn't seem to return anything. Since nothing is done with the result at the call site, C90 permits the function to return with an expression. C90 allows this in order to support pre-standard code where there was no such thing as a void type.

Function pointers assignment

Following the question Function pointer in Visual Studio 2012 I've started to wonder about the legality of certain assignments to function pointers in C.
The code below compiles with a warning, like I would expect because the assigned function requires more parameters than the function pointer declaration describes (GCC 4.8):
#include <stdio.h>
int test(int x, int y)
{
printf("%d", x);
printf("%d", y);
return 0;
}
int main()
{
int (*test_ptr)(int);
test_ptr = test;
test_ptr(1);
return 0;
}
Same warning appears if changing the code so that assigned function requires less parameters (GCC 4.8). Again, this is expected.
However, the following code compiles without a single warning, although the assigned function needs 2 parameters instead of 0 (GCC 4.8):
#include <stdio.h>
int test(int x, int y)
{
printf("%d", x);
printf("%d", y);
return 0;
}
int main()
{
int (*test_ptr)();
test_ptr = test;
test_ptr();
return 0;
}
No castings are involved anywhere.
Can anyone explain this compiler behavior?
The following:
int (*test_ptr)();
takes an unspecified number of parameters, not zero parameters.
For the latter, write
int (*test_ptr)(void);
P.S. Calling a two-argument function with zero arguments leads to undefined behaviour.
Converting from function pointer to function pointer is legal. What is illegal is calling a function pointer with a type that is not compatible with the actual pointed function.
C99 6.3.2.3 par. 8:
A pointer to a function of one type may be converted to a pointer to a
function of another type and back again; the result shall compare
equal to the original pointer. If a converted pointer is used to call
a function whose type is not compatible with the pointed-to type, the
behavior is undefined
If your compiler warned exactly for undefined behavior, it should warn at test_ptr();. But compilers cannot be expected to warn for all undefined behaviors or to warn only for undefined behaviors.
This static analyzer (that others and I work on) does its best to warn on all undefined behaviors and only for undefined behaviors. A lot of compromises are involved, but in this particular case:
$ cat > fp.c
int test(int x, int y)
{
printf("%d", x);
printf("%d", y);
return 0;
}
int main()
{
int (*test_ptr)();
test_ptr = test;
test_ptr();
return 0;
}
$ frama-c -val fp.c
...
fp.c:13:[value] warning: Function type must match type at call site: assert(function type matches)
Line 13 is test_ptr();.

Resources