Why can't you define struct members globally? - c

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

Related

Clang-Tidy: Initializing non-local variable with non-const expression depending on uninitialized non-local variable, C pointer reference

We have a very simple bit of code which is giving a warning but seems like it should be fine, and runs fine. We understand that in general there may be issues with order of initialisation between compilation units, but in this case we are initialising with a pointer, so don't understand how order can cause a problem, or how any problems might arise with this code. Note that in the real code we have more complex scenario with structs, but code below shows the basic issue.
EDIT: have removed const qualifiers as they don't affect the warning
file1.c
int foo=42;
file2.c
#include <stdio.h>
extern int foo;
// Clang-Tidy: Initializing non-local variable with non-const expression depending on uninitialized non-local variable 'foo'
int *bar=&foo;
int main() {
printf("%d\n", *bar); // prints '42'
}
Warning is not from gcc but from clang-tidy. To reproduce run:
clang-tidy -checks=* file1.c file2.c
clang-tidy is warning about something that just is not a problem: whether foo is initialized or not does not make a difference in this initializer where only its address is taken.
Furthermore foo is a global variable, hence it is either statically initialized in the module that defines it or it does not have an initializer and is initialized to 0 by the loader at run time. If the program was compiled as C++, foo might be initialized at runtime, before main is called, potentially by running initializer code, but again this does not make a difference as only its address is used in int *bar = &foo;
You should just disable this warning, as explained by Cameron Tacklind.
As others have mentioned, clang-tidy is telling you precisely what is wrong.
In this case, I think the clincher is the first part of the sentence: "Initializing non-local variable ...".
The particular warning will go away if bar is declared (and defined) inside main():
#include <stdio.h>
extern int foo;
int main() {
int *bar=&foo; // Still warns about const issues
printf("%d\n", *bar);
}
Alternatively, you can ignore the specific rule for those specific lines pretty easily:
#include <stdio.h>
extern int foo;
// NOLINTNEXTLINE(cppcoreguidelines-interfaces-global-init)
int *bar=&foo;
int main() {
printf("%d\n", *bar);
}

Structure member assignment causing syntax error when not inside a function

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;
}

How are extern variables defined?

I have a few doubts regarding the use of extern keyword with variables in C. I did go through the links related to this question. However, there are still a few things that I didn't gather very well
#include<stdio.h>
main( )
{
extern int i;
printf ( "\n%d ",i) ) ;
}
int i = 31 ;
In the above code, how does I get printed before its definition statement?
Now in the following code:
#include<stdio.h>
int x = 21 ;
main( )
{extern int i;
i=20;
printf ( "\n%d ", i ) ;
}
Isn't the statement "i=20;" a definition statement? I get an error for this. Is it because i'm trying to change the variable that is defined in some other source file? If this is the case how is the statement "int i=31;" in the top most code snippet right to use?
Also, I read, "int i;" is a definition. I don't really follow how.
In your first program, the print statement is printing the value of i based on the extern int i declaration. This is similar to calling a function based on its prototype declaration without seeing its definition. The compiler generates code to retrieve the value in the global variable named i. The symbol is resolved to the correct variable and address at link time.
In your second program, no definition for i is provided, only the extern int i declaration, and the attempt to set its value with i = 20. At link time this fails, since there is no definition, and so the attempt to resolve the reference to the global variable fails. Changing i = 20 to int i = 20 instead creates a local variable named i within the scope of the function main(), and is no longer referencing the globally declared extern int i.
When int i; is used at global scope, it is treated as a declaration and may be treated as a kind of definition. A global variable declared with an initializer, like:
int i = 20;
is always treated as a definition. Only one definition of this type is allowed, even if each is using the same initializer value. However,
int i;
is treated like a declaration. If more than one of these declarations appear, they are all treated as declarations of the same variable. At the same time, if no declaration with an initializer is present, this variable is implicitly defined with an initializer of 0, and the linker will be able to resolve references to it.
Declaration of a variable/function simply declares that the variable/function exists somewhere in the program but the memory is not allocated for them.
Coming to the definition, when we define a variable/function, apart from the role of declaration, it also allocates memory for that variable/function.
So in the 1st program, int i = 31 outside the main() defines the variable i. This variable is then merely declared as an extern variable inside the main(). Hence the program works.
In the 2nd program, without defining the variable i elsewhere, it is directly declared as an extern variable. Hence it gives an error.
In the second program you haven't defined the variable anywhere , not outside the main and instead you have defined it inside main so you are getting that error.
But in the first program you are defining the variable once and that is outside the main so you don't get that error.Program looks outside the main and find its definition.
int i; is a variable definition declaration both for AUTO variables (i.e inside main or any function ) but not for the external variables for extern variables you have to declare it like :
extern int i;
and then define outside main like this :
int i;
The idea behind extern is that you tell the compiler you're going to use a variable that was already defined outside the scope of where it is used.
It doesn't make much sense in your second case as there i is defined as a local variable.
If you were to define it as a global, like you do in the first case, it would be fine.
If I can demonstrate the use of extern with a very simple (contrived) case:
main.c:
#include <stdio.h>
extern int a; /* we tell the compiler that a will come from outside the scope of where it is used */
int main()
{
printf("%d\n", a);
return 0;
}
source.c:
int a = 3; /* we define a here */
You then compile both .c files like this:
$ gcc main.c source.c
This outputs 3.
You might also want to have a read at this article.

What is wrong with extern short i; i=2; ? gcc complains type conflict

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 */ }

Declaring a struct in a header makes it global

There are three files:
source1.c
source2.c
header.h
The two source-files includes the header.
This is the code of the header:
struct
{
int a;
int b
} x;
What happens now is that the struct becomes global and the two source-files now shares the struct called x. Why does this happen?
I know that if you write the following code it will make two global variables. One for each of the source-files. (they don't share the globals)
int x = 0;
The last piece of code makes sense to me but i really don't understand the one with the struct..
EDIT:
Hmm everybody here think I should get linker errors. My current code is for an embedded system (nxtOSEK). I'll try to convert it to a regular C program later.
EDITEDIT:
I'm back with examples in regular C. As you can see it is not only possible with structs but also with regular variables.
source1.c
#include "header.h"
int main(void)
{
f();
x = 1;
f();
}
source2.c
#include "header.h"
void f()
{
printf("source2: %i\n", x);
}
header.h
#include <stdio.h>
int x;
Output
source2: 0
source2: 1
Note that x must not be declared for it to work or it gives a linker error like everyone here said. (I don't know why it work with the embeded system..)
It also looks like I misread Eric Postpischil's answer which looks correct.
An external declaration of an object identifier at file scope that has an initializer is a definition. The declaration int x = 0; is a definition because x is initialized.
An external declaration of an object identifier at file scope that does not have an initializer is a tentative definition. The declaration struct {…} x; is a tentative definition because x is not initialized.
Multiple definitions at link time cause an error.
Multiple tentative definitions at link time may be coalesced to a single definition, which is initialized with zero. This was traditional Unix behavior and was the default behavior with GCC prior to version 10; GCC marked tentative definitions as “common” symbols by default. With GCC version 10 and later, multiple-definition errors may result. The old behavior can be selected with the -fcommon command-line switch to GCC.
If you change int x = 0; to int x;, you will not a get a link error while using build tools that treat tentative definitions as common symbols. If you change struct {…} x; to struct {…} x = {0};, you will get a link error.
The piece of code below
struct
{
int a;
int b;
} x;
declares a variable x of type struct with no tag. This is OK for a static struct that you plan to use in a single compilation unit, but if you plan to share a struct among several .c files, you should not do it like that. Instead, you should define a tag for your struct or make a typedef for it, and declare the variable of that type separately, using the srtruct my_struct syntax.
Here is an example:
Put this struct declaration in the header:
struct a_and_b
{
int a;
int b;
};
Put this variable declaration in the .c file:
static struct a_and_b x;
Now x is no longer global: you can access it inside your .c file, but it is not visible from the outside. If you want to make it global, but avoid linker errors, add
extern struct a_and_b x;
to the header, and remove static from the declaration in the .c file.

Resources