Is the option -Wdeclaration-after-statement stylistic only? By that I mean, if I macro'd all cases in my C code where a variable was defined and I initialized them in them in the same fashion migrating from this older style C90 to the newer C99 style, would that code be byte-for-byte the same?
Here is how the option -Wdeclaration-after-statement is documented (from man gcc):
Warn when a declaration is found after a statement in a block. This construct, known from C++, was introduced with ISO C99 and is by default allowed in GCC. It is not supported by ISO C90.
And it allows you to take code like
int a;
{
a = 42;
printf( "%d", a );
}
and turn it into
int a = 42;
printf( "%d", a );
This is a follow-up to my question here.
I may be confused here, but I think that we are missing something.
Prior to C99, all variable declarations had to occur before any statements in a block. It did not matter where you assigned it a value (except maybe in generated assembly code).
int a;
do_something();
a = 7;
do_something_else();
What you could not do in C but has always been perfectly legal in C++ is mix declarations and statements:
do_something();
int a = 7;
do_something_else();
With the advent of C99 you can now do the same thing in C as you can in C++, and declare a variable anywhere in a block, even after non-declaration statements.
Ultimately it was a design decision based on making it easier to write a compiler that leaked into the language specification. Compilers are a little more sophisticated now (and much larger).
Statement in this context either refers to a full expression ending with ; or alternatively a compound statement, { }. These terms are taken from the formal C grammar.
Declaration after statement does not apply to the code in your question. Because... you placed the declaration before a (compound) statement. Your two examples have nothing to do with this gcc setting, so it would appear that you have misunderstood what it does.
Rather, the relevant example would be:
{
int a = 42;
}
versus
{
puts("hello");
int a = 42;
}
The former example is fine in any C version. The latter is fine in standard C, but not in the deprecated C90 standard. So the only purpose of the warning nowadays would be to give a diagnostic message for standard C programs where a certain coding style is enforced.
The vast majority of programmers out there should not use this warning and stick to standard C as defined in ISO 9899:2018.
EDIT:
This is merely about whether or not you can rewrite code from a C90 to C99 conventions and obtain binary compatibility
Yes you can rewrite to C99 style without any consequences. None of these affect how the variable is treated in the "abstract machine" as C calls it. Here are some examples, 2 relevant and 1 irrelevant:
void relevant_c99_example (void) {
if ( 1 ) {
int a = 42;
printf( "%d", a );
}
else {
int a = 42;
printf( "%d", a );
}
}
void relevant_c90_example (void) {
int a = 42;
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
void irrelevant_example (void) {
int a;
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
https://godbolt.org/z/1oqvoesx7
These 3 produce 100% identical machine code when tested on both gcc and clang for x86_64. They even produce 100% identical machine code when optimizations are disabled! Which one shouldn't expect, since that isn't how you benchmark C code correctly. But it happens to be the case here.
No, it's not. For example, the above two snippets will compile byte-for-byte the same. So will the foo and bar below, but baz will not. Here is a link to GodBolt. This demonstrates that hoisting the initialization to the declaration may NOT produce the same code
void foo () {
int a;
{
if ( 1 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}
void bar () {
int a = 42;
{
if ( 1 ) {
printf( "%d", a );
}
else {
printf( "%d", a );
}
}
}
void baz () {
int a;
{
if ( rand() > 0 ) {
a = 42;
printf( "%d", a );
}
else {
a = 42;
printf( "%d", a );
}
}
}
Related
I have a question about strict-aliasing and clang optimizations for one example.
Let's consider the following example (1):
typedef void (*FTy)(void);
FTy F = 0;
(*F)();
It is an undefined behavior.
Let's consider the example (2) below:
typedef void (*FTy)(void);
static const FTy F = 0;
void g( int flag)
{
if ( flag )
{
(*F)();
}
}
int main( void)
{
g( 0);
return (0);
}
Indirect call by F is still "undefined behavior", but stands under always false condtion. So programm must be correct.
Now let's consider the main example (3):
(second edition: Thanks to #Antti Haapala simplified version)
(third edition: using of always_inline)
#include <stdio.h>
#ifndef BUGGY
#define BUGGY 1
#endif
static inline void __attribute__((always_inline)) longLongAssign( int cond, char *ptr)
{
if ( cond )
{
*((long long *)ptr) = 0;
}
}
void funcA(int s, int g)
{
int i, j = 0, k;
int grp[4] = {-1, -1};
void *ptr[2] = {(void *)&(grp[0]), 0};
for (i = 0, k = 0; i < 1; ++i) {
for (j = 0; j < 1; ++j) {
if ( grp[g] > 0 )
{
if ( g > 5 )
{
continue;
} else
{
longLongAssign( g > 3, (char *)ptr[0]);
}
}
grp[k++] = 0;
}
printf("this should be zero: %d\n", grp[0]);
}
}
int main(void) {
funcA(0, 1);
}
Compile by gcc and execute
this should be zero: 0
Compile by "clang-7.0 -O0" and execute
this should be zero: 0
Compile by "clang-7.0 -O1 -fno-strict-aliasing" and execute
this should be zero: 0
Compile by "clang-7.0 -O1" and execute
this should be zero: -1
In the main example one of stores to grp formally violates strict-aliasing
*((long long *)ptr) = 0;
But this store stands under always false condition.
The question here is: how a store operation
violating breaking strict-aliasing rule
but located in unreachable statement
may affect any way to program execution?
Is it correct by C language standart?
Is an example (4) below correct, well defined and hasn't undefined behavior?
void assign( int type, char *ptr)
{
if ( ptr )
{
if ( (type == 0) )
{
*((int *)ptr) = 1;
} else if ( (type == 1) )
{
*((float *)ptr) = 1;
} else
{
// unknown type
}
}
}
int main( void)
{
int a;
float b;
assign( 0, (char *)&a);
assign( 1, (char *)&b);
assign( 0, (char *)0);
return (0);
}
Inline and constant propagation optimizations in function main gives
...
if ( &a )
{
if ( (0 == 0) )
{
*((int *)&a) = 1;
} else if ( (0 == 1) )
{
*((float *)&a) = 1;
} else
{
// unknown type
}
}
...
In one hand store operation
*((float *)&a) = 1;
formally violates strict-aliasing, but stands in unreacheble location.
Which reasons example (4) may be incorrect?
If example (4) is correct so why example (3) gives by clang compilation different results?
The expression statement
*(long long *)grp = 0;
has undefined behavior on account of accessing an object of type int[4] via an lvalue of different, incompatible type (long long) -- a strict-aliasing violation, as you observe in the question. But that doesn't have to be limited to runtime behavior. The (potential) issue being visible at translation time, the translation-time behavior is undefined, too, and therefore so is the result of every execution.
Or at minimum, that's an interpretation of the standard to which at least some compiler developers subscribe. Some folks around here object to such interpretations, but that doesn't change the fact that you have to deal with them.
With regard to the update
Your example (4) has perfectly well-defined behavior. The major considerations here are
It is explicitly permitted to convert a value of one object-pointer type to a different object-pointer type. There are caveats about alignment of the result, but C requires it always to work for conversion to char *, and it requires the reverse conversion to reproduce the original pointer value (which has no alignment issue if it was valid to begin with).
It is permitted to access any object's representation via an lvalue of character type. In other words, a char * is permitted to alias any part of any object, so even though you don't access anything directly through the char * values passed to assign(), a conforming compiler must assume that those pointers could alias any object in the program.
A null pointer of any type can be converted to another object-pointer type, resulting in a null pointer of the target type.
By use of the type argument to function assign() in a manner consistent with that function's implementation, the program ensures that each object involved is ultimately accessed (only) via an lvalue of its correct type.
What optimizations the compiler might apply are irrelevant to this analysis. It is the code you present to the compiler by which behavior, if defined, is established. Supposing that the program has defined behavior, it is the compiler's responsibility to ensure that that behavior is exhibited by the program resulting from translation to an executable, and it may and does use its knowledge about its own implementation to provide for that.
Thank you all for your comments! You've helped me to understand the problem much better.
Just to clarify why I do this and what I really wanted:
I am porting clang on some specific platform. So my goal here was to understand if this test (from our autogen test system) contains error, or rather it's clang compilation bug. On results of this discussion, I've submitted an llvm bug
(https://bugs.llvm.org/show_bug.cgi?id=41178).
Thanks again!
While making a function atof, I splitted the entire work into two separate translational units:
m.c
#include<stdio.h>
extern fun(char[]); //return type not mentioned on purpose
int main()
{
printf("%d\n",fun("+123.980")); //%f would also display 0.000000
return 0;
}
n.c
#include<ctype.h>
float fun(char c[])
{
float val=0, pow=1;
int i;
int sign;
for(i=0;isspace(c[i]);i++); //SKIPPING WHITESPACE IF ANY
if(c[i]=='+')
sign = 1;
if(c[i]=='-')
sign =-1;
++i;
for(;c[i]!='.';i++)
val = val*10 + (c[i]-'0');
++i;
while(isdigit(c[i]))
{
val = val*10 + (c[i]-'0');
pow=pow*10;
++i;
}
return sign*val/pow;
}
The warning says that the type of function will default to int in this case since every variable/function type at global space without any return type mentioned explicitly defaults to int.
I was expecting the output to be int version of +123.980 i.e 123 only.
But instead it shows 0 (otherwise function works fine). Why?
Implicit int and implicit function declarations are removed from the C language. They are no more. The program is ill-formed. If your compiler accepts it, then you are dealing with a vendor extension — or a compiler for an outdated version of C.
Before these things were removed, the program had undefined behaviour. All editions of the standard require that all declarations of an entity in a program must be compatible, regardless of whether anything in them is implicit or not. Violations of this rule lead to undefined behaviour, with no diagnostic required.
This works
main()
{
int c;
struct books Book1;
c = getchar( );
return 0;
}
This doesn't
main()
{
int c;
c = getchar( );
struct books Book1;
return 0;
}
Expression syntax in function main (and points to the space after the word 'struct')
This doesn't either because the definition of B is below c = getchar();, error points to the space between "int" and "b"
main()
{
int c;
struct books Book1;
c = getchar( );
int b;
return 0;
}
Is the problem that i have to define every variable before calling functions, or is it something else?
Is this how C works, or is it a turbo C thing?
Edit: found duplicates after realizing that i meant to say "definition" not "declaration"
Where you can and cannot declare new variables in C?
Can't define variables after a function call
In C89, variables must be declared in the beginning of a block. The restriction was removed since C99.
It's not a surprise that Turbo C, an outdated compiler, doesn't support this C99 feature.
In a generated piece of c code I found something like this (edited):
#include <stdio.h>
int main() {
(void) (
{
int i = 1;
int y = 2;
printf("%d %d\n", i,y);
}
);
return 0;
}
I believe I have never seen the construct (void) ( { CODE } ) before, nor am I able to figure out what the purpose might be.
So, what does this construct do?
({ }) is a gcc extension called a statement expression.
http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
A statement expression yields a value and the (void) cast is probably here to remove the compiler warning or to make explicit that the value of the statement expression is not used.
Now (void) ({ }) is the same as a simple compound statement {} and there is no point of using it.
One application of ({ }) is the ability to replace expressions with code blocks. In this way very complex macros can be embedded in to expressions.
#define myfunc() { } // can be a typical way to automatize coding. e.g.
myfunc(x,y,z);
myfunc(y,x,z);
myfunc(x,z,y); // would work to eg. unroll a loop
int a = myfunc()*123; // but this wouldn't work
Instead
#define myfunc(a,b,c) ({printf(a#b#c);})
int a= myfunc(a,b,c) * 3; // would be legal
While programming I have come to an unusual error. When I initialize an integer in a loop, sometimes it says that the expression is not valid, but at times it accepts it.
This is my code which gives error:
int pow(int x,int n);
int main()
{
int x,n,result;
printf("Enter a number:\n");
scanf("%d",&x);
printf("Enter its power:\n");
scanf("%d",&n);
result=pow(x,n);
printf("Result is %d\n",result);
getch();
return 0;
}
int pow(int x,int n)
{
for(int i=1;i<n;i++) //<-- here it says that declaration syntax error
x=x*i;
return x;
}
While when i change it like this :
int pow(int x,int n)
{
int i;
for(i=1;i<n;i++)
x=x*i;
return x;
}
C89 and earlier versions only support declaration statements at the head of a block (IOW, the only thing that can appear between an opening { and a declaration is another declaration):
/* C89 and earlier */
int main(void)
{
int x; /* this is legal */
...
for (x = 0; x < 10; x++)
{
int i; /* so is this */
printf("x = %d\n", x);
int j = 2*x; /* ILLEGAL */
}
int y; /* ILLEGAL */
...
}
With C99, declaration statements can appear pretty much anywhere, including control expressions (with the caveat that something must be declared before it is used):
// C99 and later, C++
int main(void)
{
int x; // same as before
...
for (int i = 0; i < 10; i++) // supported as of C99
{
printf("i = %d\n", i);
int j = 2 * i; // supported as of C99
}
int y; // supported as of C99
}
Turbo C predates the C99 standard, so if you want to write code like in the second example, you will need to use a more up-to-date compiler.
In C, before C99, you need to declare your variables before your loop. In C++, and now in C99, you can declare them within the loop as you try here. The different results you are getting may be because you are sometimes compiling as C++, and sometimes compiling as C.
You could try to make sure you are always compiling your code as C++, or if you're using GCC or Clang, compile with the --std=c99 flag. C99 is unsupported by MSVC, so you will either need to use C++ or move the declaration outside of the loop if you're using MSVC.
It sounds like you have a C89 compiler (rather than C99 compiler).
In C89, you are only allowed to declare variables at the beginning of a function. You are simply not allowed to declare variables elsewhere in a function, including in the initialization part of a for statement. That's why the second syntax works and the first fails.
The second syntax is valid for C99 and C++ but not for C89.
What C compiler are you using?
Older versions of C prior to C99 require all variable declarations be made at the top of a code block.