C variable definition vs extern decleration - c

This seems like the sort of question that has already been covered but I can't seem to find out where.
I have come across a peculiar behaviour of gcc.
I file A I have the following definition:
struct SomeStruct {
unsigned int uiVarA;
unsigned int uiVarB;
} SomeVar;
I file B the extern decleration differs:
extern struct SomeStruct {
unsigned char filler;
unsigned int uiVarA;
unsigned int uiVarB;
} SomeVar;
In fact I can make the definition be a double and the extern decleration be an int and gcc will happily compile without so much as a warning (with -Wall and -Wextra even).
I can only conclude this must mean it is perfectly legal behaviour but how it that so?
I am aware that the linker does the job of mapping the two variables but is there no errorchecking at this stage?

Unless you include one file in the other, the compiler will not see both of your definitions.
Normally you would simply declare the struct in a header file separately and then declare variables of that type, so like:
in a.h:
struct SomeStruct {
unsigned int uiVarA;
unsigned int uiVarB;
}
In a.c you could then do: struct SomeStruct SomeVar; and in b.c extern struct SomeStruct SomeVar after including a.h. Or better yet, put the extern struct in a header file and include that in both a.c and b.c.

From the C Spec.
6.2.7 Compatible type and composite type
2 All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.
So, it is not perfectly legal behavior in that the behavior is undefined. But that doesn't mean that your linker has the ability to check for it. It is syntactically correct (as long as each declaration is in different translation units).

Related

structure extern with different type

I found the below warning in map file , the struct extern declaration TYPE got changed in file2.c declaration. Below warning from IAR compiler,
Please let me know what is the impact of below type cast.
what is the memory size in file2.c while link time ?
I am trying to find impacts of this implementation not the solution
"struct_tag" to "uint8"
Warning[w6]: Type conflict for external/entry "Block_01", in module file2.c against external/entry in module file1.c; different basic types
/* In module file2.c: */
uint8 NvM_Block_01;
typedef unsigned char uint8;
/* In module file1.c: */
Block_01_T Block_01;
typedef struct Block_01_Tag Block_01_T;
typedef struct Block_01_Tag
{/* 4 bytes with CRC16 */
uint16 Data_01;
uint16 Crc_01;
}Block_01_T;
File1.c
Block_01_T Block_01 = {1234,1};
File2.c
extern unsigned char Block_01;
#define RPM_BLOCK (&Block_01)
Thanks
Damodaran
In file2.c, Block_01 is given type unsigned char, which is 1 byte in size. This will access the first byte of the structure Block_01_t, which will be part of Block_01_T.Data_01.
You have two variables of the same name. Block_01 and different types. This is not allowed. You need to change one of them.
Block_01_T Block_01 = {1234,1}; // in File1.c
extern unsigned char Block_01; // in File2.c
If you want both files to refer to the same variable, you can use.
extern Block_01_T Block_01; // in File2.c
The linker generally doesn't care about the size of object, just their addresses. The compiler reserves space in the object file that defines the object. In this case, the definition is in File1.c, which declares a structure containing two uint16, so it will most reserve at least 4 bytes.
According to the C standard, the consequences are undefined when you use Block_01 in File2.. But I think it's likely that it will be equivalent to giving it the same structure type declaration, and then using *(char *)(&Block_01).

Are multiple identical prototypes legal?

The following code does not emit any warnings when compiled with both gcc and clang on Linux x64:
#include <stdio.h>
#include <stdlib.h>
void foo(void);
void foo(void);
void foo(void);
int main(void)
{
return 0;
}
IMO, it's legal according to the following snippets from C99:
All declarations that refer to the same object or function shall have
compatible type; otherwise, the behavior is undefined.
(...)
For two function
types to be compatible, both shall specify compatible return types
(...)
Moreover, the parameter type lists, if both are present, shall agree in the
number of parameters and in use of the ellipsis terminator; corresponding
parameters shall have compatible types.
(...)
Two types have compatible type if their types are the same.
Am I right? I want to make sure it is not UB and that my understanding is correct.
Multiple identical prototypes are legal, and in fact common, because it is typical in modern C for a function definition to comprise a prototype for that function, and for there also to be a prototype for the function in scope from inclusion of a header file. That is, given
foo.h:
void foo(int x);
foo.c:
#include "foo.h"
void foo(int x) {
printf("%d\n", x);
}
/* ... */
there are two identical prototypes for foo() in scope in the body of function foo's definition and throughout the rest of the file. This is fine.
It is also ok to have multiple declarations of the same object or function that are not identical, as long as they are compatible. For example, the
declaration
void foo();
declares foo as a function taking unspecified parameters and returning nothing. This declaration is compatible with the ones already present in foo.c and foo.h, and it could be added to either one or both of those files with zero additional effect.
And this all applies to objects (variables), too, where some applications are quite common. For example, if you want to declare a global variable that is accessed from multiple files, then it is common to put a declaration of that variable in a header file. The C source file containing and the definition of that variable -- which is also a declaration -- typically #includes the header, yielding two declarations:
global.h:
extern int global;
global.c:
#include "global.h"
int global = 42;
Or there is the case of forward declaration of compound data types:
struct one;
struct two {
struct one *my_one;
struct two *next;
};
struct one {
struct two *my_two;
}
Note the multiple compatible, but not identical, declarations of struct one. This particular set of data structures cannot be declared at all without multiple declaration of one of the types.

Declaration of structures in multiple files

If I include the declaration of the part structure(for example) into two different files,
will part variables in one file be of the same type as part variables in the other file?
Yes, they're of the same type if the declarations are structurally the same. If this weren't true, it wouldn't be possible to call library functions that use structure parameters, since the caller and callee are in different files.
The declarations don't have to be literally the same. As long as they specify the same types in the same order, things like member names and the name of the struct type don't have to match. So if you do:
file1.c:
struct {
int i;
char c;
} var1;
and in file2.c:
typedef struct newstruct {
int v1;
char v2;
} newstruct_t;
newstruct_t var2;
then var1 and var2 are of the same type.
The full details are a bit more complex, but this is a useful approximation.
However, programming like this will be confusing. If you intend to share a type between files, you should put the declaration in a header file, and #include it in all the source files that use it.

extern with global definition of variable in c

I have the following source code which interests me.
#include <stdio.h>
extern int foo;
int foo = 32;
int main()
{
printf("%d", foo);
}
This a perfectly normal piece of code, and when I compile it with
gcc -Wall -Wextra -pedantic foo.c
I get no warnings.
And it seems weird, because a variable is defined both as external, and also global in the same file.
I'm quite sure that it's easy to the linker to find the reference for the external variable in the same file, but doesn't it look like a coding error? And if so, why doesn't the compiler warn about this?
There's nothing weird. You first made a declaration of a variable (you promised the compiler that it exist) and then you actually defined it. There's no problem in that.
Also, by default, all variables that aren't local to functions and aren't defined as static are extern.
You seem to misunderstand what extern does. extern simply makes your declaration just a declaration instead of a definition.
int i; //definition of i
extern int i; //declaration of i
It is perfectly normal to have multiple declarations of the same variable, but only one definition should be present in the whole program. Compare this with a function
void f(void); //declaration
void f(void) //definition(and redeclaration)
{
} //definition
In order to use a variable or function, you only need its declaration. Its definition may appear anywhere in the program (the linker will find it). Anywhere can be the same file, another file, or even an external library.
And it's seems weired, because a variable is defined both as external, and also global in the same file.
extern int foo;
says: it declares without defining an object of type int named foo.
int foo = 32;
it declares and defines an object of type int named foo with external linkage.
There is no contradiction and it is valid C code.
The difference is that the former is a declaration -> extern declares a variable and says it will be available somewhere around. You can have as many declarations as you want and the latter is a definition which must be there exactly once.
So there should be no warning and no error.
extern is a way to provide visibility to a variable that is defined elsewhere...
extern is like a promise...
in example.h
extern int g;// promises that this will be in the object code to anything that includes example.h
example. c
int g;

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