Compiling C structs - c

This is my code:
#include <stdio.h>
typedef struct {
const char *description;
float value;
int age;
} swag;
typedef struct {
swag *swag;
const char *sequence;
} combination;
typedef struct {
combination numbers;
const char *make;
} safe;
int main(void)
{
swag gold = { "GOLD!", 100000.0 };
combination numbers = { &gold, "6503" };
safe s = { numbers, "RAMCON" };
printf("Contents = %s\n", s.numbers.swag->description);
getchar();
return 0;
}
Whenever I compile it with the VS developer console, I get this error: error C2440: 'initializing' : cannot convert from 'combination' to 'swag *'.
However if I use gcc the console just prints: "GOLD!". Don't understand what's going on here.

What you stumbled upon is an implementation-specific variant of a popular non-standard compiler extension used in various C89/90 compilers.
The strict rules of classic C89/90 prohibited the use of non-constant objects in {} initializers. This immediately meant that it was impossible to specify an entire struct object between the {} in the initializer, since that would violate the above requirement. Under that rule you could only use scalar constants between the {}.
However, many C89/90 compilers ignored that standard requirement and allowed users to specify non-constant values when writing {} initializers for local objects. Unfortunately, this immediately created an ambiguity if user specified a complex struct object inside the {} initializer, as in your
safe s = { numbers, "RAMCON" };
The language standard did not allow this, for which reason it was not clear what this numbers initializer should apply to. There are two ways to interpret this:
The existing rules of the language said that the compiler must automatically enter each level of struct nesting and apply sequential initializers from the {} to all sequential scalar fields found in that way (actually, it is a bit more complicated, but that's the general idea).
This is exactly what your compiler did. It took the first initializer numbers, it found the first scalar field s.numbers.swag and attempted to apply the former to the latter. This expectedly produced the error you observed.
Other compiler took a more elaborate approach to that extension. When the compiler saw that the next initializer from the {} list had the same type as the target field on the left-hand side, it did not "open" the target field and did not enter the next level of nesting, but rather used the whole initializer value to initialize the whole target field.
This latter behavior is what you expected in your example (and, if I am not mistaken, this is the behavior required by C99), but your C89/90 compiler behaved in accordance with the first approach.
In other words, when you are writing C89/90 code, it is generally OK to use that non-standard extension when you specify non-constant objects in local {} initializers. But it is a good idea to avoid using struct objects in such initializers and stick to scalar initializers only.

Looks like an issue with the initializers. If you use the proper options with gcc, it will tell you this:
$ gcc -Wall -ansi -pedantic x.c
x.c: In function ‘main’:
x.c:21: warning: initializer element is not computable at load time
x.c:22: warning: initializer element is not computable at load time
which is propably the same issue VS is trying to tell you. You can make these go away if you declare gold and numbers static.

Related

C: Reading 8 bytes from a region of size 0 [-Wstringop-overread] [duplicate]

Just curious, what actually happens if I define a zero-length array int array[0]; in code? GCC doesn't complain at all.
Sample Program
#include <stdio.h>
int main() {
int arr[0];
return 0;
}
Clarification
I'm actually trying to figure out if zero-length arrays initialised this way, instead of being pointed at like the variable length in Darhazer's comments, are optimised out or not.
This is because I have to release some code out into the wild, so I'm trying to figure out if I have to handle cases where the SIZE is defined as 0, which happens in some code with a statically defined int array[SIZE];
I was actually surprised that GCC does not complain, which led to my question. From the answers I've received, I believe the lack of a warning is largely due to supporting old code which has not been updated with the new [] syntax.
Because I was mainly wondering about the error, I am tagging Lundin's answer as correct (Nawaz's was first, but it wasn't as complete) -- the others were pointing out its actual use for tail-padded structures, while relevant, isn't exactly what I was looking for.
An array cannot have zero size.
ISO 9899:2011 6.7.6.2:
If the expression is a constant expression, it shall have a value greater than zero.
The above text is true both for a plain array (paragraph 1). For a VLA (variable length array), the behavior is undefined if the expression's value is less than or equal to zero (paragraph 5). This is normative text in the C standard. A compiler is not allowed to implement it differently.
gcc -std=c99 -pedantic gives a warning for the non-VLA case.
As per the standard, it is not allowed.
However it's been current practice in C compilers to treat those declarations as a flexible array member (FAM) declaration:
C99 6.7.2.1, §16: As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member.
The standard syntax of a FAM is:
struct Array {
size_t size;
int content[];
};
The idea is that you would then allocate it so:
void foo(size_t x) {
Array* array = malloc(sizeof(size_t) + x * sizeof(int));
array->size = x;
for (size_t i = 0; i != x; ++i) {
array->content[i] = 0;
}
}
You might also use it statically (gcc extension):
Array a = { 3, { 1, 2, 3 } };
This is also known as tail-padded structures (this term predates the publication of the C99 Standard) or struct hack (thanks to Joe Wreschnig for pointing it out).
However this syntax was standardized (and the effects guaranteed) only lately in C99. Before a constant size was necessary.
1 was the portable way to go, though it was rather strange.
0 was better at indicating intent, but not legal as far as the Standard was concerned and supported as an extension by some compilers (including gcc).
The tail padding practice, however, relies on the fact that storage is available (careful malloc) so is not suited to stack usage in general.
In Standard C and C++, zero-size array is not allowed..
If you're using GCC, compile it with -pedantic option. It will give warning, saying:
zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]
In case of C++, it gives similar warning.
It's totally illegal, and always has been, but a lot of compilers
neglect to signal the error. I'm not sure why you want to do this.
The one use I know of is to trigger a compile time error from a boolean:
char someCondition[ condition ];
If condition is a false, then I get a compile time error. Because
compilers do allow this, however, I've taken to using:
char someCondition[ 2 * condition - 1 ];
This gives a size of either 1 or -1, and I've never found a compiler
which would accept a size of -1.
Another use of zero-length arrays is for making variable-length object (pre-C99). Zero-length arrays are different from flexible arrays which have [] without 0.
Quoted from gcc doc:
Zero-length arrays are allowed in GNU C. They are very useful as the last element of a structure that is really a header for a variable-length object:
struct line {
int length;
char contents[0];
};
struct line *thisline = (struct line *)
malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
In ISO C99, you would use a flexible array member, which is slightly different in syntax and semantics:
Flexible array members are written as contents[] without the 0.
Flexible array members have incomplete type, and so the sizeof operator may not be applied.
A real-world example is zero-length arrays of struct kdbus_item in kdbus.h (a Linux kernel module).
I'll add that there is a whole page of the online documentation of gcc on this argument.
Some quotes:
Zero-length arrays are allowed in GNU C.
In ISO C90, you would have to give contents a length of 1
and
GCC versions before 3.0 allowed zero-length arrays to be statically initialized, as if they were flexible arrays. In addition to those cases that were useful, it also allowed initializations in situations that would corrupt later data
so you could
int arr[0] = { 1 };
and boom :-)
Zero-size array declarations within structs would be useful if they were allowed, and if the semantics were such that (1) they would force alignment but otherwise not allocate any space, and (2) indexing the array would be considered defined behavior in the case where the resulting pointer would be within the same block of memory as the struct. Such behavior was never permitted by any C standard, but some older compilers allowed it before it became standard for compilers to allow incomplete array declarations with empty brackets.
The struct hack, as commonly implemented using an array of size 1, is dodgy and I don't think there's any requirement that compilers refrain from breaking it. For example, I would expect that if a compiler sees int a[1], it would be within its rights to regard a[i] as a[0]. If someone tries to work around the alignment issues of the struct hack via something like
typedef struct {
uint32_t size;
uint8_t data[4]; // Use four, to avoid having padding throw off the size of the struct
}
a compiler might get clever and assume the array size really is four:
; As written
foo = myStruct->data[i];
; As interpreted (assuming little-endian hardware)
foo = ((*(uint32_t*)myStruct->data) >> (i << 3)) & 0xFF;
Such an optimization might be reasonable, especially if myStruct->data could be loaded into a register in the same operation as myStruct->size. I know nothing in the standard that would forbid such optimization, though of course it would break any code which might expect to access stuff beyond the fourth element.
Definitely you can't have zero sized arrays by standard, but actually every most popular compiler gives you to do that. So I will try to explain why it can be bad
#include <cstdio>
int main() {
struct A {
A() {
printf("A()\n");
}
~A() {
printf("~A()\n");
}
int empty[0];
};
A vals[3];
}
I am like a human would expect such output:
A()
A()
A()
~A()
~A()
~A()
Clang prints this:
A()
~A()
GCC prints this:
A()
A()
A()
It is totally strange, so it is a good reason not to use empty arrays in C++ if you can.
Also there is extension in GNU C, which gives you to create zero length array in C, but as I understand it right, there should be at least one member in structure prior, or you will get very strange examples as above if you use C++.

struct issue probably related to codeblocks but I am not sure

While writing a simple program where I had to create structs for saving information about a film and a film director, both of those had the same variables inside but with different names
struct Regista
{
char nome[30];
char cognome[30];
int nascita;
int doIexist;
};
typedef struct Regista regista;
struct Film
{
char titolo[30];
char reg[30];
int anno;
int doIexist;
};
I had created an array for both and I wanted to pass those as argument in different functions in order to add elements in those array, and now the question I have: I wrongly wrote this:
int insertfilm(film arrayfilm[]);
int insertreg(regista arrayreg[]);
int main(){
//other stuff
film arrayfilm[SIZE];
regista arrayreg[SIZE];
//other stuff
switch (mainMenuChoice)
{
case 1:
{
insertfilm(arrayfilm);
break;
}
case 2:
{
insertreg(arrayfilm);
break;
}
//other stuff
it should be noted that i passed an array of struct film while it should expect an array of struct regista in insertreg()
I was expecting that it would report an error of wrong type but instead it went all silent and run it without any problem.
My question is: is this caused by my IDE (codeeblocks) or by the C implementation?
The gcc/mingw compiler that Codeblocks uses by default is lax when it comes to give compiler errors for C language type compatibility violations. If you run it with default settings, you get:
warning: passing argument 1 of 'insertreg' from incompatible pointer type
Now as far as the C language is concerned, the above is sufficient for the compiler to be compliant. C doesn't speak of errors and warnings, only of diagnostic messages.
Your code is not valid C, since two structs are only compatible if they have the same struct tag, if all their members have the same type and if they have the same variable name. You use different struct tags and different variable names both, so they aren't compatible.
Formally, since the types aren't compatible, your code is a "constraint violation of the simple assignment rule" and a compiler must give you a diagnostic message, which it did.
I strongly recommend all beginners to compile with strict standard compliance and maximum warnings though. With gcc this means -std=c11 -pedantic-errors -Wall -Wextra. Pedantic errors in particular will block the code from compiling into an executable even though there are C language violations.
In Codeblocks specifically: go Settings -> Compiler, then check the corresponding options there, for example "Enable extra compiler warnings [-Wextra]" to enable -Wextra.

Why is this statement producing a linker error with gcc?

I have this extremely trivial piece of C code:
static int arr[];
int main(void) {
*arr = 4;
return 0;
}
I understand that the first statement is illegal (I've declared a file-scope array with static storage duration and file linkeage but no specified size), but why is it resulting in a linker error? :
/usr/bin/ld: /tmp/cch9lPwA.o: in function `main':
unit.c:(.text+0xd): undefined reference to `arr'
collect2: error: ld returned 1 exit status
Shouldn't the compiler be able to catch this before the linker?
It is also strange to me that, if I omit the static storage class, the compiler simply assumes array is of length 1 and produces no error beyond that:
int arr[];
int main(void) {
*arr = 4;
return 0;
}
Results in:
unit.c:5:5: warning: array 'arr' assumed to have one element
int arr[];
Why does omitting the storage class result in different behavior here and why does the first piece of code produce a linker error? Thanks.
Empty arrays static int arr[]; and zero-length arrays static int arr[0]; were gcc non-standard extensions.
The intention of these extensions were to act as a fix for the old "struct hack". Back in the C90 days, people wrote code such as this:
typedef struct
{
header stuff;
...
int data[1]; // the "struct hack"
} protocol;
where data would then be used as if it had variable size beyond the array depending on what's in the header part. Such code was buggy, wrote data to padding bytes and invoked array out-of-bounds undefined behavior in general.
gcc fixed this problem by adding empty/zero arrays as a compiler extension, making the code behave without bugs, although it was no longer portable.
The C standard committee recognized that this gcc feature was useful, so they added flexible array members to the C language in 1999. Since then, the gcc feature is to be regarded as obsolete, as using the C standard flexible array member is to prefer.
As recognized by the linked gcc documentation:
Declaring zero-length arrays in other contexts, including as interior members of structure objects or as non-member objects, is discouraged.
And this is what your code does.
Note that gcc with no compiler options passed defaults to -std=gnu90 (gcc < 5.0) or -std=gnu11(gcc > 5.0). This gives you all the non-standard extensions enabled, so the program compiles but does not link.
If you want standard compliant behavior, you must compile as
gcc -std=c11 -pedantic-errors
The -pedantic flag disables gcc extensions, and the linker error switches to a compiler error as expected. For an empty array as in your case, you get:
error: array size missing in 'arr'
And for a zero-length array you get:
error: ISO C forbids zero-size array 'arr' [-Wpedantic]
The reason why int arr[] works, is because this is an array declaration of tentative definition with external linkage (see C17 6.9.2). It is valid C and can be regarded as a forward declaration. It means that elsewhere in the code, the compiler (or rather the linker) should expect to find for example int arr[10], which is then referring to the same variable. This way, arr can be used in the code before the size is known. (I wouldn't recommend using this language feature, as it is a form of "spaghetti programming".)
When you use static you block the possibility to have the array size specified elsewhere, by forcing the variable to have internal linkage instead.
Maybe one reason for this behavior is that the compiler issues a warning resulting in a non-accessed static variable and optimizes it away - the linker will complain!
If it is not static, it cannot simply be ignored, because other modules might reference it - so the linker can at least find that symbol arr.

Can I enforce explicitly-initialized array size at compile-time with gcc extensions?

Can I force GCC to throw a warning or error at compile-time if the number of elements in a certain explicitly-initialized C array is not equal to a certain value?
Consider the following simple C program:
#include <stdio.h>
enum my_enum {
MY_ENUM_FIRST,
MY_ENUM_SECOND,
MY_ENUM_THIRD,
MY_ENUM_COUNT
};
// indexable by my_enum
const char *my_enum_names[] = {
"first",
"second",
"third",
};
int main(void) {
int i;
for (i = 0; i < MY_ENUM_COUNT; i++)
{
printf("%s\n", my_enum_names[i]);
}
}
Unless they are directly adjacent in the code, a developer might not realize that the enum and array must be kept "synchronized" with each other. A developer may add an entry to the enum but not to the array (or vice-versa) and therefore expose an out-of-bounds vulnerability.
Can I add some sort of pragma or attribute to the definition of my_enum_names so that if its size is not equal to MY_ENUM_COUNT, the compiler will throw a warning or error?
Some clarifications:
I am referring specifically to arrays which are explicitly initialized, meaning their size is known at compile-time.
I am referring specifically to the GCC compiler, including compiler extensions.
I swear I've done this before, possibly using one of GCC's __attribute__ extensions, but now I can't find any documentation on any feature that does what I want.
_Static_assert(sizeof my_enum_names / sizeof *my_enum_names == MY_ENUM_COUNT,
"my_enum_names is the wrong size.");
Prior to the addition of _Static_assert to the language, you could force errors in these situations with declarations such as:
extern char my_enum_namesIsTheWrongSize[1];
extern char my_enum_namesIsTheWrongSize[sizeof my_enum_names / sizeof *my_enum_names == MY_ENUM_COUNT];
If the test in the latter were false, it would attempt to declare an array with zero elements, which is an error itself, but, just in case the compiler does not report zero-size arrays, also conflicts with the preceding declaration, and so it should generate an error message.
How about
const char *my_enum_names[MY_ENUM_COUNT] = { ... };
Then the array will always contain enough elements, but some may be NULL which you then need to add check for instead. It's still better than risking going out of bounds.
Also, with the above, if you remove enumerations then the compiler will warn you about to many initializers if you forget to update the array initialization.

Quickest way to initialize an array of structures to all-0's?

I'm trying to initialize an array of structures to all-0's, using the below syntax:
STRUCTA array[MAX] = {0};
However, I'm getting the following warning from gcc :
warning: missing braces around initializer
What am i doing wrong - is there another/better way to do this ?
It the first member of your struct has a scalar type, use
STRUCTA array[MAX] = {{ 0 }};
If the first member of your struct happens to be another struct object, whose first member has scalar type, then you'll have to use
STRUCTA array[MAX] = {{{ 0 }}};
and so on. Basically, you have to open a new level of nested {} every time you "enter" another nested aggregate (a struct or an array). So in this case as long as the first member of each nested aggregate is also an aggregate, you need to go deeper with {}.
All these extra braces are only there to avoid the warning. Of course, this is just a harmless warning (in this specific case). You can use a simple { 0 } and it will work.
Probably the the best way to deal with this is to disable this warning entirely (see #pmg's answer for the right command-line option). Someone working on GCC wasn't thinking clearly. I mean, I understand the value of that warning (and it can indeed be very useful), but breaking the functionality of { 0 } is unacceptable. { 0 } should have been given special treatment.
gcc is being a nuisance. It should accept that without warnings.
Try this
STRUCTA array[MAX] = {{0}};
That gcc behaviour can be controlled with the option -Wmissing-braces or -Wno-missing-braces.
-Wall enables this warning; to have -Wall but not the missing braces, use -Wall -Wno-missing-braces
This is merely a harmful warning issued by gcc, and I would disable it with -Wno-braces. {0} is an extremely useful "universal zero initializer" for types whose definition your code is not supposed to be aware of, and gcc's discouraging its use is actively harmful to the pursuit of good code.
If gcc wants to keep this warning, it should at least special-case {0} and disable the warning in this one case.
You can avoid the warning by using completely empty braces:
STRUCTA array[10] = {};
The array will be aggregate-initialized, which means that each of the structs in it will in turn be value-initialized. Value-initialization with empty brackets turns into aggregate-initializion of each struct, which sets all fields to 0, which is what you want.
This works in all cases as long as your structs are POD (see the links above for precise description).
Arrays are initialized with braces, but so are structs. You probably need to put an extra set of braces around your 0 and, depending on how STRUCTA is defined, some extra 0s separated by commas.
It depends on the STRUCTA. For example :
typedef struct structa
{
int a, b;
} STRUCTA;
int main (int argc, char const* argv[])
{
STRUCTA array[10] = {{0,0}};
return 0;
}
Can STRUCTA be assigned to 0?
You can always use memset(), too.

Resources