I want to assign a particular value to a (user defined) global variable in C programming language. When I am doing this from within any other function or main it is fine. But when I am doing it from global space (outside of any function) it is giving the following compilation error:
[expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘.’ token]
Following is a code snippet which causing issue:
#include <stdio.h>
#define MAX_SIZE 5
typedef struct
{
int val[MAX_SIZE];
int top;
}stack_t;
stack_t s;
s.top = -1; // <== Initialization from here is causing compilation error
main()
{
//s.top = -1; < === Initialization from here is fine
printf("s.top =%d\n", s.top);
return 0;
}
But same kind of assignment for integer variables is not giving only warning
#include <stdio.h>
int i,j,k,l;
k=10;
main()
{
printf("i= %d, j=%d k=%d l=%d\n", i, j, k, l);
return 0;
}
Can anyone please tell the reason for this?
The error from the assignment of s.top is not surprising. It is not an initialization but an assignment, and those are different concepts in C. You can't have assignments outside of functions.
The interesting part here is that it looks like you can do an assignment of the integer variable k. But that is an illusion, since in that case it's not an assignment but an initialization!
The line
k = 10;
is interpreted not as an assignment but as a variable definition. A hint to this is that GCC gives the warnings "data definition has no type or storage class" and "type defaults to 'int' in declaration of 'k'". So the line could be written as
int k = 10;
As Matt wrote in a comment below, leaving out the data type like this seems to be a GCC extension, and not something that the standard allows.
(By the way, do turn on warnings in your compiler, and pay attention to them!)
But wait, wasn't there already a definition of k on the line above, and you can't have more than one definition, can you?
First, remember that C makes a distinction between definitions and declarations. A definition of a variable is when you "create" the variable, and optionally give it an initial value. A declaration is when you just tell the compiler that the variable exists, and its name and data type, but the definition is elsewhere. You can have both a definition and one or more declarations of the same variable. For example:
int xxx = 10; // This is the definition of xxx
int xxx; // This is a declaration of xxx
int xxx; // Another delaration of xxx
But sometimes the compiler can't determine if what it sees is a declaration or a definition, and then it is interpreted as a "tentative definition", or in other words "perhaps a definition, perhaps just a declaration, we'll decide later". For example:
int yyy;
Is that a definition of the variable yyy (without an initial value), or is it just a declaration, where the definition will be found elsewhere? The compiler doesn't know, so it waits to decide, and calls this a tentative definition.
When the compiler sees your first declaration of k (along with the other variables i, j and l) it is interpreted as a tentative definition, and when the later definition is found, that tentative definition of k will be decided to be a declaration, not a definition.
C code has to be inside a function. You can declare a global outside of a function but you can't write an assignment statement outside of a function. That is you can set the initial value as part of declaring the struct but you can't then change it. Here your declaration is only the "stack_t s;" line.
Everything in C ends up compiling to symbols in a binary format (an example would be the ELF format). Symbols have names, so functions are named chunks of code, and globals are named chunks of data. There are no "free floating" chunks in the compiled binary, everything has to go under a name.
In the C model, code floating around outside of a function doesn't make sense because C doesn't have a place where it would run that code. C never runs a file in the way that bash or Python or javascript does; it only runs binaries. So it only runs named
functions. The files are only known at compile time not runtime.
Initializing the outside the main is reason for that error. You can do that using like this.
stack_t s={.top=-1};
It will allow you to do the initialization while declaring. Refer this link. or this It can be useful.
There are differences between assignment and initialization. You should note that, assignment can't be done outside of a function. The statement
s.top = -1;
is an assignment not an initialization.
C99 and latter provides designated initialization of structs and arrays. So, you can initialize only the top member of struct s as
stack_t s = { .top = -1 };
and other members will be initialized to 0 by the compiler.
In the global space you can only initialize and declare the variables but cannot assign the value to variables. In your code you are assigning the value to member of struct so its throwing error and same holds true for integers for your below code.
Try the below code in the global space and it works fine:
typedef struct
{
int val[MAX_SIZE];
int top;
}stack_t;
stack_t s={{},-1};
main()
{
printf("s.top=%d",s.top);
return 0;
}
Related
In this page I cannot understand why Example 3 throws an error:
// CODE 1:
extern int var;
int main(void)
{
var = 10;
return 0;
}
There are several answers mentioning that var inside main is a local variable. But why would it not be a global variable at the same time. This code below works tough:
// CODE 2:
int var; // global
int main(void)
{
var = 10;
return 0;
}
In the case of multiple files...
... the code below works:
// CODE 3:
// File 1
extern int var;
int main(void)
{
print("%d",var);
return 0;
}
----------------
// File 2
var = 4;
... while this one does not:
// CODE 4:
// File 1
extern int var;
int main(void)
{
print("%d",var);
return 0;
}
----------------
// File 2
int func(int a) {
extern int var;
var = 3;
return 0;
}
So I could not find a meaningful explanation to the behavior of the extern keyword. How do you explain/ interpret that keyword? Also what should I change in the codes to make them work as intended?
In C, every definition is a declaration. Some declarations are definitions, and some are not.
The page you link to, has mistakes. One is it says that “Declaration of a variable or function simply declares that the variable or function exists somewhere in the program, but the memory is not allocated for them.” Actually, that is two mistakes, at least. Per C 2018 6.7 5, “A declaration specifies the interpretation and attributes of a set of identifiers…” This does not necessarily mean any object or function with the declared name actually exists in the program. For example, if we include the <stdio.h> header, the fputc function is declared, but, if we never use it, it might never be linked into the program, and so no such function might exist in the program, even in the file executable file. A declaration that is not a definition only tells the compiler about the name (which we call the identifier); it does not convey information about whether something exists.
A second mistake in that sentence is that it says memory is not allocated for the declared variable or function. This is incorrect because each definition is a declaration. C 2018 6.7 5 continues “… A definition of an identifier is a declaration for that identifier that: — for an object, causes storage to be reserved for that object; — for a function, includes the function body…” For example, int x = 3; is a declaration of x, and it is a definition of x, and it causes memory to be allocated for x, but the page you link to says a declaration does not cause memory to be allocated. So the page is wrong.
The rules regarding extern and what is or is not a definition are complicated because C was developed by multiple people doing different things with it. When C was standardized, the committee had to reconcile different practices. As a consequence, there is no single rule for extern.
A note about terminology: A variable is actually two things: an identifier (its name) and an object (the memory that stores a representation of its value).
In your CODE 1, the linked page’s Example 3, extern int var; is a declaration that is not a definition. There is no definition. If an identifier with external linkage is used in an expression, there must be exactly one external definition of it in the program (by C 2018 6.9 5). Since there is no external definition, the linker complains.
There are several answers mentioning that var inside main is a local variable.
If an identifier is declared inside a block, such as the { … } that forms the body of a function, then effects of the declaration are local to that block. If an identifier is declared outside of any function, the effects of the declaration continue through the rest of the file being compiled, except where they are hidden by a nested declaration.
If var is declared outside of any function and before main, and then we use that identifier inside main without declaring it again, that use of var refers to the previous declaration. It does not create or refer to a new local object named var.
But why would it not be a global variable at the same time.
At any one point in source code, an identifier refers to at most one thing.
.. the code below works:
…
// File 2
var = 4
var = 4 is not proper C code because it does not have a semicolon. If it were var = 4;, some compilers might accept this outside a function, but it is archaic. In modern C, it should have a type, such as int var = 4;. That would then be a proper definition of var. If your compiler accepted var = 4; outside a function, you should be aware it is accepting old code, and it would be preferable to use switches to tell the compiler to require modern C code.
This question already has answers here:
In C, is it valid to declare a variable multiple times?
(1 answer)
In C,why is multiple declarations working fine for a global variable but not for a local variable?
(3 answers)
Closed last year.
So far I have understood the following:
A variable declaration is the declaration of a type and name of a variable without allocating memory space for it.
A variable definition means that the variable is declared and memory space is allocated for it.
So it has nothing to do with the initialization of the variable, whether you speak of a definition or a declaration.
In C, a declaration is always a definition e.g. if one write int i;.
But there is one exception. If you write extern int i; no memory space is allocated, only the variable is declared.
So int i; is always declaration and definition at the same time. But extern int i; is just declaration.
Is it true that in C you can declare a variable as often as you want, but you can only define the variable once?
I ask because I've tried the following and the compiler results confuse me. I use gcc and don't set the -std flag.
Neither this program:
int i;
int i;
void main(void){
i = 2;
}
nor this program:
int i=0;
int i;
void main(void){
i = 2;
}
lead to problems. The compiler compiles both without error. I would have expected since I didn't use the "extern" keyword here that the compiler would say something like "error: multiple definition".
But it doesn't give an error message. Is it possible that the compiler automatically writes an "extern" before all global defined "int i;" if I don't initialize them at the same time?
Isn't it then superfluous for the programmer to ever use the extern keyword for variables since the compiler will do that automatically anyway?
I think my considerations are confirmed by the following behavior. The following programs return errors:
int i;
i=0;
void main(void){
i = 2;
}
leads to:
"warning: data definition has no type or storage class
i=0;
warning: type defaults to 'int' in declaration of 'i' [-Wimplicit-int]"
and
float i;
i=0;
void main(void){
i = 2;
}
leads to:
"warning: data definition has no type or storage class
i=0;
warning: type defaults to 'int' in declaration of 'i' [-Wimplicit-int]
error: conflicting types for 'i'
note: previous declaration of 'i' was here
float i;"
So to me again it looks like there is an implicit "extern" before the first int i; respectively float i; is written because they are not assigned a value. As a result, no storage space is allocated for i.
But there is no other file in which storage space is allocated for i. Therefore there is no definition for i and the compiler therefore thinks in the 2nd line that i should be defined here.
Therefore there are no problems with the 1st program because the automatic type assignment fits, but with the 2nd program it no longer fits, which is why an error is returned.
The following program also throws an error:
void main(void){
int i;
int i;
}
If I write the declaration (and thus also the definition) in a scope, the compiler returns the following error message.
"error: redeclaration of 'i' with no linkage int i;
note: previous declaration of 'i' was here int i;"
I can only explain it again with the fact that the compiler does not automatically set an "extern" before a variable that is not a global variable and therefore there are 2 definitions here.
But then I ask myself why is it called redeclaration and not redefinition or multiple definition?
It would be very nice if someone could confirm my assumptions or enlighten me on how to understand it correctly. Many Thanks!
A variable declaration is the declaration of a type and name of a variable without allocating memory space for it.
Even if memory is reserved for an object, it a declaration. We do not exclude definitions from declarations; there are declarations that are definitions and declarations that are not definitions.
The declaration x = 3; causes memory to be reserved for x, but it also makes the name and type of x known, so it declares x.
So int i; is always declaration and definition at the same time.
Not quite. Inside a function, int i; is a definition. Outside of a function, int i; is a tentative definition. This is a special category that was necessary due to the history of C development. The language was not designed all at once with foresight about how it would be used. Different implementors tried different things. When a standard for the C language was developed, the committee working on it had to accommodate diverse existing uses.
When there is a tentative definition, the program can still supply a regular definition later in the translation unit. (The translation unit is the source file being compiled along with all the files included in it.) If the program does not supply a regular definition by the end of the translation unit, then the tentative definition becomes a regular definition as if it had an initializer of zero, as in int i = 0;.
Some C implementations treat multiple tentative definitions of an identifier in different translation units as referring to the same object. Some C implementations treat them as errors. Both behaviors are allowed by the C standard.
Is it true that in C you can declare a variable as often as you want, but you can only define the variable once?
Not always. Variables with no linkage (declared inside a function without static or extern) may not be declared more than once. (An identical declaration can appear inside a nested block, but this declares a new variable with the same name.)
Repeated declarations must have compatible types, and there are additional rules about which repeated declarations are allowed. For example, an identifier may not be declared with static after it has been declared with extern.
The compiler compiles both without error.
As described above, int i; outside a function is a tentative definition. Initially, it acts only as a non-definition declaration. So it may be repeated, and it may be replaced by a regular definition.
So to me again it looks like there is an implicit "extern" before the first int i;
No, there is not. int i; is a tentative definition, and it has nothing to do with the error messages you are getting. The error messages “data definition has no type or storage class” and “type defaults to 'int' in declaration of 'i'” are from the i=0;. This is a statement, not a declaration, but the C grammar does not provide for statements outside of functions. Outside of functions, the compiler is looking for only declarations. So it expects to see a type, as in int i=0;. The first message tells you the compiler does not see a type or a storage class. The second message tells you that, since it did not see a type, it is assuming int. This is a relic of old behavior in C where the int type would be taken as a default, so it could be left off. (Do not use that in new C code.)
The following program also throws an error:
Inside a function, int i; is a definition, so two of them causes multiple definitions of i.
When you define a struct globally, why can't you define the structure members globally as well (outside of using the initializer syntax)? The error I'm getting from clang is that system_1 has an "unknown type name".
If you define the struct within a function, such as main(), then you don't run into any issues.
typedef struct Light_System {
int redLightPin;
int yellowLightPin;
int blueLightPin;
} Light_System;
Light_System system_1;
# "Light_System system_1 = {4, 0, 0}" works
system_1.redLightPin = 4; # doesn't work
int main(int argc, char *argv[]) {
# placing everything in here works
# placing just "system_1.redLightPin = 4;" here makes it work.
printf("%d\n", system_1.redLightPin);
return 0;
}
I would expect that I would be able to define the structure's members within the global scope.
When you define a struct globally, why can't you define the structure members globally (outside of using the initializer syntax)?
Because what you're trying to do is not a definition or an initialization. It's an assignment. You can declare, define and initialize in global scope, but not assign.
This is not only for structs. It goes for variables too. Outside a function, you can only declare and initialize variables. You cannot do regular assignments, or anything else for that matter. I'm not 100% sure of the details, but I'm fairly confident that you cannot do anything in global scope that cannot be done during compile time.
So this would work:
int x=5;
But not this:
int x;
x = 5;
Well, actually it is valid but will yield cryptic warnings about warning: data definition has no type or storage class and warning: type defaults to ‘int’ in declaration of ‘x’ However, just because it compiles, it will not do what you think it will. Actually, cryptic warnings that you don't understand is very often a good indicator that the code will do something that you neither want nor understand. What this code does is an implicit redeclaration of x followed by an initialization. It's allowed if it has not been initialized earlier, so this not valid:
int x = 3;
x = 5; // Not ok, because x is already initialized
And the error message here makes it clear why the previous example compiled: error: redefinition of ‘x’.
Similarly, this is not valid either:
int x = foo();
It gives this error:
main.c:3:7: error: initializer element is not constant
int x = foo();
^~~
As others have stated. You can not assign in a global scope. But you can initialise. So if you want to initialise struct members globally, you can try it like this.
#include <stdio.h>
typedef struct Light_System {
int redLightPin;
int yellowLightPin;
int blueLightPin;
} Light_System;
Light_System ls = {10,5,3};
int main()
{
printf("%d %d %d",ls.redLightPin,ls.yellowLightPin,ls.blueLightPin);
}
That should compile.
So if you're wondering why this happens, it's because global variables are stored in the .data section of an application with a given value at compile time. But assignments are instructions which can only be executed within the scope of a function. So what gets compiled keeps their compile time value.
You can't execute statements outside of a function in C or C++. But you can use C99-style "designated initializers" in a lot of compilers (not all):
Light_System system_1 = {
.redLightPin = 4
};
This will become standard in C++20, but as the same syntax has existed for years in C, many compilers allow it in C++ as well.
Ref: https://en.cppreference.com/w/cpp/language/aggregate_initialization
I just found a quirk in C that I find really confusing. In C it's possible to use a pointer to a struct before it has been declared. This is a very useful feature that makes sense because the declaration is irrelevant when you're just dealing with a pointer to it. I just found one corner case where this is (surprisingly) not true, though, and I can't really explain why. To me it looks like a mistake in the language design.
Take this code:
#include <stdio.h>
#include <stdlib.h>
typedef void (*a)(struct lol* etc);
void a2(struct lol* etc) {
}
int main(void) {
return 0;
}
Gives:
foo.c:6:26: warning: ‘struct lol’ declared inside parameter list [enabled by default]
foo.c:6:26: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
foo.c:8:16: warning: ‘struct lol’ declared inside parameter list [enabled by default]
To remove this problem we can simply do this:
#include <stdio.h>
#include <stdlib.h>
struct lol* wut;
typedef void (*a)(struct lol* etc);
void a2(struct lol* etc) {
}
int main(void) {
return 0;
}
The unexplainable problem is now gone for an unexplainable reason. Why?
Note that this question is about the behavior of language C (or possible the compiler behavior of gcc and clang) and not the specific example I pasted.
EDIT:
I won't accept "the order of declaration is important" as an answer unless you also explain why C would warn about using a struct pointer for the first time in a function argument list but allow it in any other context. Why would that possibly be a problem?
To understand why the compiler complains, you need to know two things about C "struct"s:
they are created (as a declared, but not yet defined, type) as soon as you name them, so the very first occurrence of struct lol creates a declaration
they obey the same "declaration scope" rules as ordinary variables
(struct lol { declares and then begins defining the structure, it's struct lol; or struct lol * or something else that does not have the open-brace that stops after the "declare" step.)
A struct type that is declared but not yet defined is an instance of what C calls an "incomplete type". You are allowed to use pointers to incomplete types, as long as you do not attempt to follow the pointer:
struct lol *global_p;
void f(void) {
use0(global_p); /* this is OK */
use1(*global_p); /* this is an error */
use2(global_p->field); /* and so is this */
}
You have to complete the type in order to "follow the pointer", in other words.
In any case, though, consider function declarations with ordinary int parameters:
int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */
Variables named a and b here are declared inside the parentheses, but those declarations need to get out of the way so that the the next function declaration does not complain about them being re-declared.
The same thing happens with struct tag-names:
void gronk(struct sttag *p);
The struct sttag declares a structure, and then the declaration is swept away, just like the ones for a and b. But that creates a big problem: the tag is gone and now you can't name the structure type ever again! If you write:
struct sttag { int field1; char *field2; };
that defines a new and different struct sttag, just like:
void somefunc(int x) { int y; ... }
int x, y;
defines a new and different x and y at the file-level scope, different from the ones in somefunc.
Fortunately, if you declare (or even define) the struct before you write the function declaration, the prototype-level declaration "refers back" to the outer-scope declaration:
struct sttag;
void gronk(struct sttag *p);
Now both struct sttags are "the same" struct sttag, so when you complete struct sttag later, you're completing the one inside the prototype for gronk too.
Re the question edit: it would certainly have been possible to define the action of struct, union, and enum tags differently, making them "bubble out" of prototypes to their enclosing scopes. That would make the issue go away. But it wasn't defined that way. Since it was the ANSI C89 committee that invented (or stole, really, from then-C++) prototypes, you can blame it on them. :-)
The compiler is warning you about a forward declaration of struct lol. C allows you to do this:
struct lol; /* forward declaration, the size and members of
struct lol are unknown */
This is most used when defining self-referencing structs, but it is also useful when defining private structs that are never defined in the header. Because of this latter use case, it is allowed to declare functions that receive or return pointers to incomplete structs:
void foo(struct lol *x);
However, just using an undeclared struct in a function declaration, as you did, will be interpreted as a local incomplete declaration of struct lol whose scope is constrainted to the function. This interpretation is mandated by the C standard, but it is not useful (there is no way to construct the struct lol to pass to the function) and is almost certainly not what the programmer intended, so the compiler warns.
This is because, in the first example, the struct is previously undefined and so the compiler tries to treat this first reference to that struct as a definition.
In general, C is a language where the order of your declarations matters. Everything you use should be properly declared in advance in some capacity, so that the compiler can reason about it when it's referenced in other context.
This is not a bug or a mistake in the design of the language. Rather, it's a choice that I believe was made to simplify the implementations of the first C compilers. Forward declarations allow a compiler to translate the source code serially in one pass (as long as some information such as sizes and offsets is known). If this weren't the case, the compiler would have be able to go back and forth in the program whenever it meets an unrecognized identifier, requiring its code emission loop to be much more complex.
I see this same warning before. My fix is to include the proper header file which contains the definition of the struct.
In addition to other answers, I would like to post a code example, which makes the problem more obvious. Please consider the following:
int testFunc(struct SomeStruct {int a; int b;} param1) // gcc: warning ...
{
return param1.a + param1.b;
}
int main(void)
{
struct SomeStruct params; // gcc: error: storage size of 'params' isn't known
params.a = 25;
params.b = 15;
return testFunc(params);
}
As you can see, the function declaration of testFunc is considered a valid C code (tested with GCC 12, Clang 15 and MSVC 19.32). However, you can't really use it because SomeStruct is only valid within the scope of the function, which is what compiler warns you about.
I've stumbled on this myself when working on my own C parser as the allowance of such syntax makes parser development easier as you can reuse the same implementation to parse struct declaration inside function parameter lists. It is surprising, however, that such bizarre syntax is still considered valid nowadays (as of C17) and instead of error you just get a warning.
Lookout for typos and DOUBLE CHECK your line numbers! I concat all my source files before compiling, so the line numbers from the source I am working in are meaningless. I have to take extra time to open up the concatted payload and examine it. So usually I don't and I just assume I know what line I am looking at from the console output message.
Example:
GCC Says:
EXAMPLE.C11:27:1: error: 'struct THIS_STRUCT_IS_OK' declared inside
parameter list [-Werror] ){
#include <stdio.h> //:for: printf(...)
struct THIS_STRUCT_IS_OKAY{
int whatever;
};
int LookingAtThisFunction(
struct THIS_STRUCT_IS_OKAY* arg
){
//: (Because you are not checking line numbers, you )
//: (assume you are looking here. But you are not. )
//: (Maybe you are concatenating all of your source )
//: (files together before compiling and line numbers )
//: (don't correspond to the original source and you )
//: (didn't examine your concatted source code payload?)
return( arg -> whatever );
}
//:You are not looking here because this is later in the
//:file, so the compiler would be complaining about the
//:FIRST usage of the struct, not the second one, you assume.
//:And you would be correct, if there wasn't a typo.
void WhereYouAreNotLooking(
struct THIS_STRUCT_IS_OK* arg
){
LookingAtThisFunction( arg );
}
int main( void ){
}
In summary: If you know what the error message means. And you swear to god
the compiler is broken because you already checked that...
1. Look for typos.
2. Make sure you are really looking at the correct line number.
I get that this is kinda stupid. But it had been scratching my head for
half an hour. So hopefully it helps someone who's already looked at the
obvious solutions.
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 */ }