Why is Xcode allowing me to declare C variables anywhere? - c

I created a basic C project in Xcode and modified the starter code in main.c slightly. I also went into the build settings and told it to use ANSI-C. Here's the code I have:
int main(int argc, const char * argv[])
{
// a statement!
printf("Hello, World!\n");
// shouldn't this cause a compiler error?
// the variable isn't declared at the top of the scope.
int x;
x += 10;
return 0;
}
Obviously, it doesn't do much, but I expected the variable declaration to produce a compiler error (since older versions of C require variable declarations at the beginning of the scope, before other statements). However, Xcode happily compiles it and runs it with neither an error or warning.
I might be making a dumb mistake somewhere, but I'm trying to understand why this code compiles. I've read that C99 and C11 allow you to declare variables anywhere, so this would work, but I explicitly set the project to use ANSI-C. Is this just the way Apple's LLVM compiler works? Or am I missing something elsewhere?

TL;DR You need to add -pedantic (or -Wdeclaration-after-statement) to -ansi to get the warning you want.
Somewhat to my surprise, both clang (from Apple XCode 7.2) and gcc (from GCC 5.3.0, which I built), accept the code when compiled with either -std=c90 or -ansi even though it is not strictly compliant with C90.
However, both complain when told to be -pedantic.
$ clang -ansi -c xyz.c
$ clang -std=c90 -c xyz.c
$ gcc -std=c90 -c xyz.c
$ which gcc
/opt/gcc/v5.3.0/bin/gcc
$ gcc -std=c90 -pedantic -c xyz.c
xyz.c: In function ‘main’:
xyz.c:7:5: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
int x;
^
$ clang -pedantic -std=c90 -c xyz.c
xyz.c:7:9: warning: ISO C90 forbids mixing declarations and code [-Wdeclaration-after-statement]
int x;
^
1 warning generated.
$ clang -pedantic -ansi -c xyz.c
xyz.c:7:9: warning: ISO C90 forbids mixing declarations and code [-Wdeclaration-after-statement]
int x;
^
1 warning generated.
$
The file xyz.c is your source code with the comments stripped, #include <stdio.h> added at the top, and int main(void) in place of int main(int argc, char **argv) since the code doesn't use the arguments.
Note that your code has undefined behaviour; incrementing an uninitialized variable is a bad idea.

Related

Why is there no compilation error with those array declarations (C89-C90)?

I want to show that arrays can't be defined with a variable for the length in C89 or C90.
I use GCC on Windows from TDM GCC:
C:\TDM-GCC-64\bin> .\gcc.exe --version
gcc.exe (tdm64-1) 10.3.0
My compilation options are:
gcc.exe -Wall -g -ansi -save-temps -c
I tried:
int main()
{
int i;
int tab[i];
tab[0] = 10;
return 0;
}
But it compiles fine:
gcc.exe -Wall -g -ansi -save-temps -c main.c -o main.o
gcc.exe -o Sandbox.exe main.o
main.c:6:9: warning: variable 'tab' set but not used [-Wunused-but-set-variable]
6 | int tab[i];
| ^~~
main.c:6:5: warning: 'i' is used uninitialized in this function [-Wuninitialized]
6 | int tab[i];
| ^~~
Output file is bin\Debug\Sandbox.exe with size 196.89 KB
Then:
int test(int i)
{
int tab[i];
tab[0] = 10;
return 0;
}
Compiles too:
main.c: In function 'test':
main.c:5:9: warning: variable 'tab' set but not used [-Wunused-but-set-variable]
5 | int tab[i];
| ^~~
Output file is bin\Debug\Sandbox.exe with size 196.90 KB
Or:
int main()
{
volatile int i;
int tab[i];
tab[0] = 10;
return 0;
}
Only this is not compiling:
int main()
{
// Comment
return 0;
}
error: C++ style comments are not allowed in ISO C90
What am I missing?
Thanks!
Variable length array is a GCC extension in older standard versions. Extensions are "compatible" with the standard. If you want to exactly adhere to the standard, i.e. you want to get a message when using an extension, add -pedantic option (and -pedantic-errors for errors).
Gcc docs: https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Variable-Length.html#Variable-Length , https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Warning-Options.html#Warning-Options .
gcc has sloppy standard compliance per default. It defaults to lax C conformance + GNU extensions, equivalent to -std=gnu89 (current version defaults to -std=gnu17).
When you type -ansi it does not mean conforming mode, but rather just the very same thing as -std=c89, "lax C89 mode". This compiler option may disable some GNU extensions... maybe... while keeping others. The difference between -std=c89 and -std=gnu89 is as poorly documented as the rest of gcc. We can read the unfriendly manual and it says:
For example, -std=c90 turns off certain features of GCC that are incompatible with ISO C90, such as the asm and typeof keywords, but not other GNU extensions that do not have a meaning in ISO C90
gcc supported variable-length arrays as an extensions even before C99, so switching out the GNU option ought to disable them, but not so... As far as I know, there is no official documentation over what features -std=c89 shuts off or at least I can't find it.
What's important to realize is that -std=c89/-ansi alone does not push gcc into conforming mode! To do that, you need to do -std=c89 -pedantic or if you will -std=c89 -pedantic-errors. After which you'll get a diagnostic like this:
error: ISO C90 forbids variable length array 'tab'
And when compiling with these two options in combination, gcc probably has the best standard compliance of any compiler out there.

Enforcing ANSI C89 with clang

I'm trying to make the C compiler clang go into ANSI C89 mode but without success.
Here is an example session:
$ cat t.c
#include <stdio.h>
int main(void)
{
puts(__FUNCTION__);
return 0;
}
$ gcc -pedantic -std=c89 -Wall t.c
t.c: In function ‘main’:
t.c:5:7: warning: ISO C does not support ‘__FUNCTION__’ predefined identifier [-Wpedantic]
puts(__FUNCTION__);
^~~~~~~~~~~~
$ clang -pedantic -std=c89 -Wall t.c
$ clang --version
clang version 3.8.1-24 (tags/RELEASE_381/final)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
As you can see, the clang command completes with no warning. Is there a command option that I'm missing here?
It seems as if this specific warning is not emitted by clang, but apparently -std=c89 does toggle ANSI C89 syntax checking.
For example:
inline int foo(int* restrict p)
{
return *p;
}
Will refuse to compile with -std=c89 -pedantic -Wall:
t.c:1:1: error: unknown type name 'inline'
t.c:1:23: error: expected ')'
int foo(int* restrict p)
But will compile without errors using -std=c99.
The non-standard predefined identifiers warning was introduced with GCC 5 (https://gcc.gnu.org/gcc-5/porting_to.html), and apparently clang did not adapt with it.

compiler isn't issue error/warning in mismatch function parameter

I have the next code :
test.c
#include "a1.h"
int main() {
int a = 8;
foo(a);
return a;
}
a1.h
void foo (int a);
a1.c
int f = 0;
void foo (int a, int b){
f=5+a+b;
return;
}
Pay attention that in a1.c foo has 1 more parameter than the prototype defined in a1.h.
The compiler isn't issue a warning or an error and so as coverity :
make all
Building file: ../src/a1.c
Invoking: GCC C Compiler
gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/a1.d" -MT"src/a1.d" -o "src/a1.o" "../src/a1.c"
Finished building: ../src/a1.c
Building file: ../src/test.c
Invoking: GCC C++ Compiler
gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/test.d" -MT"src/test.d" -o "src/test.o" "../src/test.c"
Finished building: ../src/test.c
Building target: test
Invoking: GCC C++ Linker
gcc -o "test" ./src/a1.o ./src/test.o
Finished building target: test
How can I defend myself in those cases ? I know that if I will add #include "a1.h" in the a1.c file I will get an error but is there a way to get an error without the "include " ?
Compiler isn't issuing a warning because it does not know that foo(int) from a1.h header and foo(int,int) from a1.c file is the same function. C++ allows functions to be overloaded, so both functions could potentially coexist. That is why C++ compiler cannot detect this problem, so you need to wait until the linking stage.
If you were compiling using C, not C++, you could have the compiler detect this condition simply by including a1.h at the top of a1.c file.
You're overloading foo. The version with only one parameter is never defined, hence you should get a linker error when using it.
How can I defend myself in those cases ?
You can't defend yourself from function overloading. Just make sure that you've got the same signature in both the header as the source file.

Variable declaration in C mingw

I was reading C programming from a book that says all variables have to be declared in the beginning of the function. I tried following code but didn't issue any error. I am using mingw and codeblocks. The code is as follows:
#include <stdio.h>
int main()
{
int i=10;
printf("%d\n",i);
int j=20;
printf("%d\n",j);
return 0;
}
Do I have to change any compiler setting or something to make it compatible with the standard given in the book?
I am using -std=c89 compiler option. See the compilation messages below:
-------------- Clean: Debug in HelloWorld (compiler: GNU GCC Compiler)---------------
Cleaned "HelloWorld - Debug"
-------------- Build: Debug in HelloWorld (compiler: GNU GCC Compiler)---------------
mingw32-gcc.exe -Wall -std=c89 -g -c D:\MyCodeBlocksProjects\HelloWorld\main.c -o obj\Debug\main.o
mingw32-g++.exe -o bin\Debug\HelloWorld.exe obj\Debug\main.o
Output size is 68.53 KB
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 0 warnings (0 minutes, 0 seconds)
all variables have to be declared in the beginning of the function.
To be precise, they have to be declared in the beginning of a block. And this is only true in C89. C99 has removed this limit. So you can change your compiler to strict C89 mode. For example, for GCC, it's -std=c89 option. To obtain all the diagnostics required by the standard, you should also specify the option -pedantic.
To demonstrate what I mean by in the beginning of a block, this is legal C89 syntax:
void foo()
{
int x = 1;
x = x + 1;
{
int y = 42; /**OK: declaration in the beginning of a block*/
}
}

Getting compilation warnings for non-prototype declarations

In C, function declarations can be prototype or non-prototype declarations. For example, consider the following minimal program:
int foo (); /* non-prototype declaration */
int bar (void); /* prototype declaration */
int main (int argc, char **argv)
{
return 0;
}
Although in C99 non-prototype declarations are obsolete, I cannot get GCC to complain about them. For example, compiling the above program with GCC and all errors enabled just succeeds:
$ gcc -std=c99 -pedantic -Werror -Wall test.c
$
Is there any way to persuade GCC to emit warnings for function declarations that aren't prototypes?
(Question inspired by an answer by Keith Thompson.)
I think the option you are looking for is -Wstrict-prototypes
Note that gcc with -std=c99 -pedantic -Wall options doesn't issue any warning for the old style function declarations but C doesn't require the implementation to issue a diagnostic in presence of the old style function declarations.
C characterizes the use of old style function declaration as obsolescent since C89 but it it still valid C code (in C89/C99/C11).

Resources