When declaring a variable or pointer, the compiler assumes the variable or pointer itself is already declared when being assigned as a value during declaration.
I have tried both gcc and clang and they compile the "faulty" code without complaining.
CASE 1: This will not compile since "a" is not declared:
void main()
{
int b=sizeof(a);
}
CASE 2: This compiles without a problem:
void main()
{
int a=sizeof(a);
}
Shouldn't the compiler generate the "a is undeclared" error instead, just like in case 1?
Shouldn't the compiler generate the "a is undeclared" error instead, just like in case 1?
Why? It just saw you declare a.
int a = sizeof(a);
// ^--- here it is, before its first use
The declaration of a variable begins after its declarator is seen, right before its (optional) initializer. So you can even write the truly faulty
int a = a;
Note however that in your case there is nothing faulty being done. The result of sizeof depends only on the type of a, and the type is known. This is a well defined initializtion (with a conversion from size_t to int, but not one to be worried about).
sizeof is not a function depending on the value of a; it is a builtin that is evaluated at compile time, so it becomes equivalent to
int a = 4;
Related
I have read in multiple places that when declaring an extern variable, the memory is not designated until the definition is made. I was trying this code which is giving contradictory output in gcc.
#include <stdio.h>
int main() {
extern int a;
printf("%lu", sizeof(a));
return 0;
}
it should have shown error or zero size. but the output was following. please justify the output. Is it example of another undefined behavior?
aditya#theMonster:~$ ./a
4
You're able to get away with it here because a is never actually used. The expression sizeof(a) is evaluated at compile time. So because a is never referenced, the linker doesn't bother looking for it.
Had you done this instead:
printf("%d\n", a);
Then the program would have failed to link, printing "undefined reference to `a'"
The size of a variable is the size of its data type, whether it is presently only an extern or not. Since sizeof is evaluated at compile time, whereas symbol resolution is done at link time, this is acceptable.
Even with -O0, gcc doesn't care that it's extern; it puts 4 in esi for the argument to printf: https://godbolt.org/z/Zv2VYd
Without declaring a, however, any of the following will fail:
a = 3;
printf("%d\n", a);
int *p = &a;
The a is an integer, so its size is 4.
Its location(address) and value are not currently known.(it is extern somewhere at some other location)
But the size is well defined.
size_t sizeof(expr/var_name/data_type) 1 is a unary operator which when not provided with a variable length array, do not evaluate the expression. It just check the data type of expression.
Similarly, here, in sizeof(a), the complier only checks the data type of a which is int and hence returns the size of int.
Another example to clear your confusion is in sizeof(i++), i do not get incremented. Its data type is checked and returned.
One more example:
void main(){
int p=0;
printf("%zu",sizeof(p=2+3.0));
printf("%d",p);
}
will give u output on gcc as:
4
0
There is indeed a problem in your code, but not where you expect it:
passing a value of type size_t for printf conversion specification %ld has undefined behavior if size_t and unsigned long have different sizes or representations, as is the case on many systems (16-bit systems, Windows 64-bit...).
Here is a corrected version, portable to non C99-conforming systems, whose C library printf might not support %zu:
#include <stdio.h>
int main(void) {
extern int a;
printf("%lu\n", (unsigned long)sizeof(a));
return 0;
}
Regarding why the program compiles and executes without an error:
Variable a is declared inside the body of main with extern linkage: no space is allocated for it and a would be undefined outside the body of main.
sizeof(a) is evaluated at compile time as the constant value sizeof(int), which happens to be 4 on your platform.
No further reference to a is generated by the compiler, so the linker does not complain about a not being defined anywhere.
The Problem
I'm writing the following code, which, as excerpted below, should return the total amount of money depending on the number of each type of coin.
static int qNum = 0;
static int dNum = 0;
static int nNum = 0;
static int pNum = 0;
int main(){
float totalCost;
totalCost = totalMoneyCalc();
}
float totalMoneyCalc(void){
float total;
total = qNum*.25 + dNum*.10 + nNum*.05 + pNum*.01;
return total;
}
The Errors I'm Getting
note: previous implicit declaration of ‘totalMoneyCalc’ was here
totalCost = totalMoneyCalc();
error: conflicting types for ‘totalMoneyCalc’
float totalMoneyCalc(){
However, when I remove the "float" part of the totalMoneyCalc() function, it doesn't return any more errors. Aren't you supposed to assign functions types?
What I've Tried
I looked at this question on SO, which says something about having to pass in void as a param for functions that don't take in anything but adding that to the original code didn't work either.
I also checked out some primers on using floats for functions in C, but I followed those and it's still not working.
Am I supposed to be doing something with pointers? I don't understand why returning a value in a function to be assigned to a variable is different from ints to floats. If anyone has suggestions, fixes, or explanations, I'd really appreciate it.
Yes, all C functions have types. Prior to the 1999 standard, it was legal to omit the return type from a declaration; that would implicitly give it a return type of int. As of C99, the return type must always be specified explicitly.
Also prior to C99, it was legal to call a function with no visible declaration. The compiler would assume a function with a return type of int (that's the "previous implicit declaration" referred to in the error message). C99 dropped that rule; now all functions must be visibly declared before you can call them.
Assuming you're using gcc (that looks like a gcc error message), the compiler enforces the C90 rules by default. The problem it's reporting is that the implicit declaration of totalMoneyCalc (as a function returning int) conflicts with the later explicit declaration as a function returning float.
The solution is to declare totalMoneyCalc before the call. You can either add a separate "forward" declaration before main:
float totalMoneyCalc(void);
or you can move the entire definition above main. (This is a good idea even in C90.)
A minor point: int main() should be int main(void).
The compiler needs to know about the function before you use it. If the function is unknown to the compiler then it will not be able to know whether the function returns double or float or if it's first parameter is a pointer or a struct, so it will by default think of them all as int's.
You can to add a function prototype before main()
float totalMoneyCalc(void);
or move the entire function definition before main().
The following code is similar to that of question Is there a difference between initializing a variable and assigning it a value immediately after declaration? downvoted twice, so I am at risk ;-)
short i;
i = 2;
It does not compile with MinGW and -std=c99 -- why? The first line is a declaration of identifier i, not a definition of an object. However, the identifier has file scope and thus external linkage by default. It may be declared and defined elsewhere. The second line could be an assignment to that object. But gcc complains about a missing type or storage class and --after guessing the type as int-- about a type conflict.
You say that short i; has file scope, which implies to me that it (edit: and the subsequent i = 2;) is outside a function. Outside a function, i = 2; on its own is complete nonsense; as a statement, it cannot appear outside a function. (edit) As statements cannot appear outside functions, the "assignment" must be a definition. In old C code, a definition without a storage class was an int definition, so your code is equivalent (under those rules, which it looks like GCC is applying) to:
short i;
int i = 2;
which of course is complete nonsense to a C compiler. (end edit)
You can get more-or-less the effect you're after by defining and initializing:
short i = 2;
This does not work if you merely wish to declare an external variable; in that case, put the initialization in the file with the definition, or in one of your functions (as below).
extern short i;
int main(int argc, char **argv) { i = 2; /* more code */ }
I don't understand why the following code generates a warning:
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)() = func;
return 0;
}
I thought that in C, a function with an empty parameters list mean a function that can receive an unknown number of parameters. func4 happens to receive 3 parameters. So why is it incompatible with myPointer?
Its especially confusing because the following does compile without warning:
void boo()
{
return;
}
int main()
{
int(*pointerDude)(double) = boo;
return 0;
}
What's going on?
The difference between the two cases is explained like this: if you pass a parameter to a function that accepts none, you are just consuming some memory on the stack. If you don't pass any parameter to a function that accepts a few, the latter is going to read random data from the stack.
Update: changed to community wiki to add these corrections from Pascal Cuoq:
Casts of function pointer in both directions are legal, informative warnings from the compiler notwithstanding. Calling a function with the wrong prototype is illegal in both cases. Some ABIs mandate that the function clean up the stack, in which case passing parameters to functions that accept none corrupt the stack as surely as not passing parameters to functions that expect them.
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
This is the warning you get:
warning: initialization from incompatible pointer type
You are passing void which means that will require a cast. You should use the right types.
IIRC C compiler has certain assumptions which can cause very odd bugs because you're not telling it how much stack space it needs whenever that function is called. Especially since the definitions don't match this might allow someone to inadvertently read your function pointer declaration and assume no parameters and have the function return garbage. If you really want to take variable numbers of parameters use my_func(...) and varargs.
A warning is just that, a warning. Honest.
In that case, the compiler knows you have an incorrect function pointer type and it blatantly tells you that. If you try to pass a number of parameters different then 3, then it will be issued an error.
I think that the problem is related to the casting of the parameters, because char is not 32 bits. Note that if you change the char to int this works fine. It is related to automatic promotion of variables.
Compare the two:
int func();
int func(double a, int b, int c)
{
return 0;
}
and
int func();
int func(double a, int b, char c)
{
return 0;
}
gcc gives you warning for the second, but not for the first
In C an empty parameter list means that the function does not take any parameters. I believe you are confusing an empty parameter list with varargs.
Changing your first example to include the parameters will remove the warning.
int func(double a, int b, char c)
{
return 0;
}
int main()
{
int(*myPointer)(double, int, char) = func;
return 0;
}
#include <stdio.h>
int *top;
int a=1;
top=&a;
void main()
{
printf("%d\n",*top);
}
error C2440: 'initializing' : cannot convert from 'int *' to 'int'
UPDATE
I know how to make it work,but I'm asking why it DOESN'T work.
You're actually tripping over the compiler's support for ancient C syntax. The original C compiler allowed declarations without a type, defaulting it to int. So outside of any function,
foo;
would declare a global int variable called foo. So when you say
top = &a;
it declares a global int variable called top and tries to initialize it with the address of a. This gives you the two errors you see -- two conflicting declarations for top and an inability to convert an int * to an int. Of course those same ancient C compilers would not give you the second error, happily converting the address to an int without complaint.
This also tells you why int i; i = 100; works --- its two declarations for i as a global int variable (which is ok, as they're the same type), and the second initializes it to 100 (which is ok as only one declaration has an initializer)
There's lots of fascinating stuff in Dennis Ritchie's The Development of the C Language
I'm a bit surprised at the exact error message you got about it, but the problem (or at least one of the obvious problems) is that outside of a function, you're allowed to define and initialize variables, but you're not allowed to do a normal assignment -- that has to be done as part of executing a function. As such, your top=&a; isn't allowed.
Another problem is that you have main returning void, where it should return an int (though most compilers will accept that without even a warning, not to mention an error message).
Actually, from your original code:
int *top;
int a=1;
top=&a;
As others have mentioned - in global space you can declare, or declare and initialize. You can not do assignment.
What is actually happening in the line top = &a; is that you are actually re-declaring a variable called top, and it defaults to the int type. The compiler should actually warn about creating a new variable named top that has the same name as a previously declared variable, as well as generate an additional warning that you are creating a variable of default type int.
In C, variables that are declared without a type default to int, and that would generate the error you see.
error C2440: 'initializing' : cannot convert from 'int *' to 'int'
What this is really complaining about is that top = &a; as in your code actually looks like int top = &a; to the compiler, so you are trying to bind an int* to int.
This works:
#include <stdio.h>
int *top;
int a=1;
int main()
{
top=&a;
printf("%d\n",*top);
}
You need to be sure not do global assignment like that. You can only do initialization, nothing else for global variables.
You also have not mentioned the other errors, please mention all errors, sometimes one error is based of another and when one is fixed the others get fixed. In this case it was:
Conflicting types for 'top'.
Another option:
#include <stdio.h>
int a=1;
int *top=&a;
void main()
{
printf("%d\n",*top);
}
I honestly have no idea why that fails, but this works:
#include <stdio.h>
int a = 1;
int* top = &a;
void main()
{
printf("%d\n", *top);
}
And remember: ALWAYS INITIALIZE YOUR POINTERS!!
If you're not going to assign them immediately, at least do something like this:
int* top = 0;