How to assign an array wrapped into a struct using the struct pointer?
I know this syntax:
size_t initArrayList[] = {1,1,1};
memcpy(pStruct->sizet, initArrayList, sizeof(pStruct->sizet));
is it possible to use a similar syntax as:
Stru = (struct myStruct) {.sizet = {1,1,1}};
using pointers?
I'd appreciate a detailed explanation of what (struct myStruct) {.sizet = {1,1,1}} does.
struct myStruct {
size_t sizet[3];
} ;
void struInit(struct myStruct * pStruct) ;
int main()
{
struct myStruct Stru;
struInit(&Stru);
if (Stru.sizet[1]==1)
printf("\nStru.sizet[1]==1");
return 0;
}
void struInit(struct myStruct * pStruct ) {
// I know this syntax
// size_t initArrayList[] = {1,1,1};
// memcpy(pStruct->sizet, initArrayList, sizeof(pStruct->sizet));
}
Either calling memcpy using a temporary array (just as commented out in your struInit function)
memcpy(pStruct->sizet, initArrayList, sizeof(pStruct->sizet));
or assigning to the pointed-to structure using a compound literal (notice the use of the * dereference operator)
*pStruct = (struct myStruct){.sizet={1,1,1}};
or even a conventional for-loop, copying element by element, does what you want.
Or if you can not rely on designated initializers or compound literals,
both being features that have only been standardized since C99, you can
use an equivalent, C89 way, that doesn't make use of them:
struct myStruct tmp = {{1,1,1}};
*pStruct = tmp;
I'd appreciate a detailed explanation of what (struct myStruct) {.sizet = {1,1,1}} does.
The relevant parts of C99 describing designated initializers and
compound literals are 6.7.8 Initialization and 6.5.2.5 Compound
literals.
The short answer is that if used in the struInit function it
constructs a local unnamed object with the type struct myStruct,
initializing its member sizet to {1,1,1}, this in turn being syntax
for array element initialization, assigning a value of 1 to its
elements [0], [1], and [2], in that exact order.
Related
I have only been learning C for less than a week (with my knowledge of C++ and other languages to help) and I am confused on pointers and their ways of being declared.
Below, I use a simple struct named Object:
struct Object { int id; };
Do the below methods for creating a pointer do the same thing just in a different way, or no?
struct Object obj1 = { .id = 1 };
struct Object* obj1_p = &obj1; // method 1 of getting a pointer
// The same, just in a compound literal?
struct Object* obj2_p = &(struct Object){ .id = 1 }; // method 2 of getting a pointer
// Is this the same, other than being uninitialized?
struct Object* obj3_p = malloc(sizeof(struct Object)); // method 2 of getting a pointer
Is there a time when you only can use one method?
Also, as a side note, why do some people cast malloc, and is it better to do it?
// malloc is casted to object:
struct Object* obj3_p = (Object*)malloc(sizeof(struct Object));
These two “methods” do exactly the same thing. And as you said, the second one is just a compound literal.
struct Object obj1 = { .id = 1 };
struct Object *obj1_p = &obj1;
// The same, just in a compound literal?
struct Object *obj2_p = &(struct Object){ .id = 1 };
This allocates enough memory for struct Object without initializing it. And no you don't need to cast it, because malloc returns void *, which is automatically and safely promoted to any other pointer. But if you do, you should cast it to struct Object* instead of Object*.
struct Object *obj3_p = (struct Object*) malloc(sizeof(struct Object));
That looks very bulky though... My preferred way of doing it is this:
struct Object *obj3_p = malloc(sizeof *obj3_p);
I wrote this piece of code, hope it helps you to better understand some features of pointers:
#include <stdio.h>
#include <stdlib.h>
struct Object { int id; };
struct Object *getObjectBold() {
struct Object* obj2_p = &(struct Object) { .id = 2 };
return obj2_p; // UB: Returns the address of a local object (the compound literal).
}
struct Object *getObject() {
struct Object* obj3_p = malloc(sizeof(*obj3_p)); // Better way of calling malloc than using sizeof(struct Object).
obj3_p->id = 3; // You don't need to do this.
return obj3_p; // This needs to be freed later on!
}
int main(void) {
struct Object obj1 = { .id = 1 };
struct Object* obj1_p = &obj1;
printf("obj1.id = %d\n", obj1_p->id);
obj1_p->id = 10; // You can change values using the pointer
printf("obj1.id = %d\n", obj1_p->id);
// The only different thing with this case is that you don't
// "lose" your object when setting the pointer to NULL
// (although you can only access it through the object, not through the pointer).
obj1_p = NULL;
printf("obj1.id = %d\n", obj1_p->id); // This won't work (undefined behaviour).
printf("obj1.id = %d\n", obj1.id); // This will.
struct Object* obj2_p = &(struct Object) { .id = 1 };
obj2_p->id = 2; // You can change the id
printf("obj2.id = %d\n", obj2_p->id);
// If you make this pointer point to another address, you "lose" your object.
obj2_p = NULL;
printf("obj2.id = %d", obj2_p->id); // This won't work at all (undefined behaviour).
// Both of these pointers point to objects in the stack, so, for example,
// they don't work when returning from a function.
obj2_p = getObjectBold();
obj2_p->id = 20; // This won't work (undefined behaviour).
printf("obj2.id = %d\n", obj2_p->id); // This works if you don't dereference the pointer.
// The third case is not the same as the other two, since you are allocating memory on the heap.
// THIS is a time where you can only use one of these three methods.
struct Object *obj3_p = getObject(); // This works!
printf("obj3.id = %d\n", obj3_p->id);
obj3_p->id = 30; // This works now.
printf("obj3.id = %d\n", obj3_p->id);
free(obj3_p); // You need to do this if you don't want memory leaks.
return 0;
}
This is the output when commenting out undefined behaviour:
obj1.id = 1
obj1.id = 10
obj1.id = 10
obj2.id = 2
obj2.id = 2
obj3.id = 3
obj3.id = 30
I'd recommend you to check out these links, they turned out to be pretty helpful for me:
Returning a pointer from a function
What and where are the stack and heap?
What EXACTLY is meant by “de-referencing a NULL pointer”?
Why dereferencing a null pointer is undefined behaviour?
Do I cast the result of malloc?
There are two distinct topics in your question.
struct Object* obj1_p = .......;
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^
pointer object initialization
definition
Pointer variable definition
You can define the pointer variable only one way:
type *objectname;
Initialization assigns the value to the pointer variable. This value should reference the valid object of the same type as the pointer or valid memory of the size not smaller than the pointer type. The difference in your examples is how the referenced object is created.
Is there a time when you only can use one method?
That only depends on the program logic. You only must remember about the scope of the underlying object to avoid dereferencing objects which does not exist outside the particular scope:
struct Object *valid1(void) //valid
{
struct Object* obj3_p = malloc(sizeof(*obj3_p));
return obj3_p;
}
struct Object obj1 = { .id = 1 };
struct Object *valid2(void) // valid
{
struct Object* obj3_p = &obj1;
return obj3_p;
}
struct Object *invalid1(void) // invalid
{
struct Object obj1 = { .id = 1 };
struct Object* obj3_p = &obj1;
return obj3_p;
}
struct Object *invalid2(void) // invalid
{
struct Object* obj3_p = &(struct Object){ .id = 1 };
return obj3_p;
}
Also, as a side note, why do some people cast malloc, and is it better
to do it?
It is considered as bad practice as it silences the warning if there is no prototype of the malloc. Better do not cast. Modern compilers and recent C standard disallow the use of the functions without prototypes
It is better to use sizeof(object) instead of sizeof(type) as if you change the type of the object you need to change oll of the occurrences of the sizeof(type) in your program. It very easy to miss some and get very hard to discover errors.
First, let's straighten out some terminology -
You are declaring all three pointers the exact same way:struct Object* objn_p ...
The only difference is in how you are initializing them.
Declarations in C have two major components - a sequence of declaration specifiers followed by a comma-separated list of optionally initialized declarators. C++ declarations are structured fundamentally the same way, but for this answer I will stick with C terminology (since that's the language we're talking about the one I'm more familiar with).
Declaration specifiers include storage class specifiers (auto, static, register, typedef, etc.), type specifiers (int, char, double, short, etc.), type qualifiers (const, volatile, restrict), and a few others.
Declarators include the name of the thing being declared, along with information about its pointer-ness, array-ness, and/or function-ness.
Initializers for scalar objects are scalars. Initializers for aggregate objects like arrays, structs, and unions are brace-enclosed lists or, in the case of character arrays, a string literal.
In the declaration
struct Object* obj1_p = &obj_1;
the declaration specifier is struct Object, the declarator is * obj1_p, and the initializer is = &obj_1.
I know the C++ convention for declaring pointer objects is T* p, but the syntax is actually T (*p) - the * operator is always bound to the declarator, not the type specifier. If you write T* p, q; then only p is declared as a pointer to T; q is declared as an instance of T. I know why the C++ convention exists, I know the justifications for it, but it does misrepresent how declaration syntax works in both C and C++ and I consider it a mistake to use. Most C programmers will use the T *p convention instead1.
Here are the basic rules for pointer declarations in C:
Declaration Declarator
Specifier
----------- ----------
T *p; // p is a pointer to T
T *a[N]; // a is an array of pointer to T
T *f(); // f is a function returning a pointer to T
T (*a)[N]; // a is a pointer to an array of T
T (*f)(); // f is a pointer to a function returning T
The rules for const are:
T const *p; // p points to a const T
const T *p; // same as above
T * const p; // p is a const pointer to T
What differs between your three methods is how you initialize the pointer.
Method 1 is just taking the address of a previously-declared variable of the same type:
struct Object *obj1_p = &obj1; // using the C convention for pointer declarations
Method 2 is taking the address of a compound literal - basically, an anonymous variable:
struct Object *obj2_p = &(struct Object){ .id = 1 };
The only difference between obj1 and the anonymous object is that you can refer to obj1 directly as well as through the pointer:
printf( "%d %d %d", obj1.id, obj1_p->id, (*obj1_p).id );
whereas you can only refer to the anonymous object through the pointer variable
printf( "%d %d", obj2_p->id, (*obj2_p).id );
Method 3 dynamically allocates memory and assigns the address of the resulting object (which may be NULL if the malloc call fails).
struct Object *obj3_p = malloc( sizeof( struct Object ) );
The chief difference between this and the other two methods is that the memory allocated by malloc hangs around until you explicitly free it, whether the obj3_p variable goes out of scope or not. If obj1 and the anonymous object are declared within a block and without the static keyword, then that memory is automatically released when the block containing them exits.
Also, as a side note, why do some people cast malloc, and is it better to do it?
There are two times when you must cast the result of malloc (and calloc and realloc):
You are compiling the code as C++;
You are working with an ancient, pre-C89 K&R implementation.
Unlike C, C++ does not allow implicit conversion between void * (the type returned from malloc) and other pointer types. You must explicitly cast conversions to or from void *. Having said that, if you're writing C++ you should not be calling malloc directly. You should either be using a container that manages memory for you under the hood (std::string, std::vector, std::map, etc.) or you should be using the new or new [] operators. If you're in the middle of a lift-and-shift from C to C++, it's acceptable to keep the malloc calls until you can get around to rewriting your memory management code, but ideally C++ code should never use malloc (or calloc or realloc) directly.
In the earliest versions of C, malloc, calloc, and realloc returned char *, so you had to cast the result if you were assigning it to pointers of different types:
int *p = (int *) malloc( sizeof *p * N );
As someone who wrote K&R C in college, this was a pain in the ass. It was cluttered and a constant source of mistakes. If you changed the type of p (say from int * to long *) you had to repeat that change in multiple places. That created a higher maintenance burden, especially if (as was often the case) the pointer declaration was separated from the malloc call by other code:
int *p = NULL;
...
p = (int *) malloc( sizeof *p * N );
Prior to C99, you had to declare all variables before any statements (in that block, anyway), so it was common for pointer declarations to be separated from the malloc call by multiple lines of code. It was really easy to change the type of *p in the declaration but forget to do it in the assignment later, causing subtle (and sometimes not-so-subtle) runtime errors.
The 1989 standard introduced the void type and changed the *alloc functions to return void *. It also introduced the rule that you could assign void * values to other pointer types and vice versa without an explicit cast. So you could write a malloc call as:
int *p = malloc( sizeof *p * N );
or
int *p = NULL;
...
p = malloc( sizeof *p * N );
If you change the type of *p, you only have to make that change in one place. It's cleaner, it's harder to screw up, etc.
Also, under the C89 standard, casting the result of malloc could suppress a useful compiler diagnostic if you forgot to include stdlib.h or otherwise didn't have a declaration for malloc in scope. But since C99 did away with implicit int declarations2, that's not really an issue anymore.
However, there are people who prefer to keep the explicit cast for various reasons. Personally, I have found those reasons wanting, and I've accumulated enough scar tissue from bad casts that I prefer to leave them off entirely. 30+ years of writing code has convinced me that when it comes to memory management, simpler is always better. It doesn't hurt to use them; they don't slow down the code or anything like that. But from a readability and maintenance standpoint, casting malloc is bad juju.
Whitespace in declarations is only significant in that it separates tokens. The * character is a token on its own and not part of any identifier, so you can write any of T *p, T* p, T*p, or T * p and they will all be parsed as T (*p).
Prior to C99, if the compiler saw a function call without a preceding declaration, it assumed the function returned int, so if you forgot to include stdlib.h the compiler would complain if you tried to assign the result of malloc to a pointer since an int can't be implicitly converted to a pointer. However, if you used the cast, then the diagnostic would be suppressed.
Im aware that malloc() returns a pointer to the initialised block of memory, but initialising this memory is difficult for me.
struct state{
int one;
int two;
};
struct state *one = malloc(sizeof(struct state));
*one = (struct state){.one = 10,.two = 20};
free(one);
Im aware that the above thing can be done to initialise a bunch of values for the structure without individually accessing the fields and changing the value manually.But can anyone explain how the above code works?
Also how can I do this for a pointer to several ints. The code below doesn't seem to work:
int *pointer = (int*) {1,2,3,4,5};
can anyone explain how the above code works?
In the same way that the code below works:
int *one = malloc(sizeof(int));
*one = 1020;
free(one);
The only difference is that instead of assigning a numeric value your code assigns the value of the struct, expressed through a struct aggregate. Everything else is the same: the assignment copies the content of one region of memory into another region of memory.
Also how can i do this for a pointer to several ints?
Use memcpy, like this:
int *pointer = malloc(5*sizeof(int));
static int data[] {1, 2, 3, 4, 5};
memcpy(pointer, data, sizeof(data));
free(pointer);
But can anyone explain how the above code works?
I can explain what it means.
struct state{
int one;
int two;
};
That declares a structure type with tag state, which thereafter can be referenced via the type name struct state.
struct state *one = malloc(sizeof(struct state));
That declares one as a pointer to an object of type struct state. Supposing that the malloc() succeeds, it initializes one to point to a dynamically-allocated, uninitialized block of memory exactly the size of a struct state and aligned in a manner suitable for an object of that (or any other) type.
*one = (struct state){.one = 10,.two = 20};
The right-hand side of that expression is a compound literal of type struct state. The part inside the braces is its initialization list, exactly as you might use to initialize an ordinary variable of the same type:
struct state two = { .one = 10, .two = 20 };
The .one and .two name the member initialized by each individual initializer; they are optional in this particular case because in their absence, the initializer expressions would be matched to structure members in the order the members appear in the structure type declaration. Overall, the statement copies the value of the literal struct to the object pointed-to by one.
free(one);
That releases the dynamic memory block.
Also how can i do this for a pointer to several ints[?]
There is no such thing as a pointer to several ints. There is a pointer to one int, or a pointer to specific-size array of ints, but -- and I know I'm being pedantic here -- neither of those is "a pointer to several ints".
It looks like what you want in this case is an array, not (directly) a pointer. You can initialize an array and set its size in one declaration, like so:
int array[] = {1, 2, 3, 4, 5};
Afterward, you can use array in most respects as if it were a pointer to the first int in the array.
Note, however, that unlike structures, whole arrays cannot be assigned to. Although the above code may look like it contains an assignment, it does not -- it contains an initializer. Though the syntax is similar, initializers are not assignments. The latter are executable statements; the former are not.
(struct state){.one = 10,.two = 20}; is a compound literal. It is an object that remains valid until the end of the block scope.
*one = (struct state){.one = 10,.two = 20};
Is equivalent to
struct state temp = {.one = 10,.two = 20};
*one = temp;
In the second example, the definition of the compound literal is wrong, you must specify an array type, and then you point your pointer just like normal:
int *pointer = (int[]) {1,2,3,4,5};
Again this is equivalent to:
int temp[] = {1,2,3,4,5} ;
int *pointer = temp;
It's best to have a function (or macro if you're into those):
struct state{
int one;
int two;
};
struct state* alloc_state(int one, int two)
{
struct state s = {one, two}; // order is per struct definition!
struct state* p = malloc(sizeof(struct state));
memcpy(p, &s, sizeof(s));
return p;
}
This function will always stay 4 lines of code regardless of how many argument are used.
The same principle can be used with other types.
Macro version:
#define ALLOC_STATE(p, ...) \
{ \
struct state s = {__VA_ARGS__}; \
p = malloc(sizeof(struct state)); \
memcpy(p, &s, sizeof(s));\
}
Usage:
struct state* p;
ALLOC_STATE(p, 3, 4);
I want to declare a int num in struct S. Then the same struct should also have a array B of size num(So B will access num from it's own struct).
while in a function, I can do,
func(int A)
{
int max=A; //I could use A directly, I am just trying to explain my plan.
int B[max];
}
same won't work for struct as such,
struct S {
int num;
int data[num]; //this is wrong, num is undeclared
};
Is there any way I can do this?
Use a flexible array member:
struct S {
int num;
int data[];
};
int x = 42;
struct S *p = malloc(sizeof (struct S) + sizeof (int [x]));
p->num = x;
There are several problems with
struct S {
int num;
int data[num];
};
that cause it to not work the way you want to.
The first is that the num being used in the array member declaration isn't the same num that's the member of the struct type; the compiler treats the num in the array declaration as a regular identifier (i.e., it assumes there's a different variable named num in the same scope as the struct declaration)1.
The second problem is that a struct or union type may not have a variable-length array as a member2. However, the last member in the struct may have an incomplete array type:
struct S {
int num;
int data[];
};
Unfortunately, you're still kind of stuck here; if you create an instance of struct S like
struct S foo;
it doesn't actually allocate any space for the array. You'd need to allocate the struct dynamically:
/**
* Note that sizeof doesn't try to actually dereference foo below
*/
struct S *foo = malloc( sizeof *foo + N * sizeof *foo->arr );
to allocate space for the array itself. Note that you cannot declare an array of struct S or use it as a member of another structure or union type if the last member has an incomplete array type. 3
Honestly, your best option is to define the struct as
struct S {
size_t num;
int *data;
};
and then allocate the memory for data as a separate operation from allocating memory for the struct object itself:
struct S foo;
foo.num = some_value();
foo.data = malloc( sizeof *foo.data * foo.num );
Since struct S now has a known size, you can declare arrays of it, and you can use it as a member of another struct or union type:
struct S blah[10];
struct T {
struct S s;
...
};
1. C supports four different name spaces - label names (disambiguated by label syntax), struct/union/enum tag names (disambiguated by the presence of the struct, union, or enum keyword), struct and union member names (disambiguated by the . and -> component selection operators), and everything else. Since the num in your array declaration is not an operand of . or ->, the compiler treats it as a regular variable name.
2. 6.7.2.1/9: "A member of a structure or union may have any complete object type other than a variably modified type."
2. 6.2.7.1/3: A structure or union shall not contain a member with incomplete or function type (hence,
a structure shall not contain an instance of itself, but may contain a pointer to an instance
of itself), except that the last member of a structure with more than one named member
may have incomplete array type; such a structure (and any union containing, possibly
recursively, a member that is such a structure) shall not be a member of a structure or an
element of an array.
First of all, the member num is not declared until the end of the struct definition, which ends at the last }.
Second, how would it make sense to set the array size to be the value of an uninitialized variable?
What I think you attempt to do with int B[max] is to create a variable length array (VLA). But that won't work either, as they are explicitly forbidden inside structs. 6.7.2.1/9:
A member of a structure or union may have any complete object type other than a
variably modified type.
What you could do instead is to declare a flexible array member, as demonstrated in the answer by Ouah.
The reason the compiler complains when you "flexibly declare" the array in the struct in global memory, is because global memory can only be allocated (declared) at compile-time and at compile time all sizes must be known. (The value of a variable is not known at compile time by definition.)
The reason it accepts a flexible array in a function, is because the function's local variables are created at the moment the function is entered and then the compiler can accept a variable size. (It boils down to the compiler allocating more memory on the stack and offsetting all accesses to local variables with the size - but different compilers could have a different approach.)
#include <stdio.h>
int size;
struct S {
int num;
int a[size]; // illegal: size must be known at compile time
};
int f(int size)
{
int a[size]; // legal as a is allocated on the stack
....
}
The following would be legal:
#include <stdio.h>
#define A_SIZE 10
struct S {
int num;
int a[A_SIZE]; // legal: A_SIZE is known at compile time
};
P.s.: I am not a C99 programmer; I may have some mistakes here.
Apperently in C99 you can simply initialize a statically allocated struct in this way
struct sometype {
int a;
double b;
};
sometype a = {
.a = 0;
};
Well, this does not apply to a struct on heap like this.
struct sometype *a = malloc(sizeof(struct sometype));
*a = {
.a = 0;
};
With GCC 4.9.2, the compiler complained
error: expected expression before '{' token
I know this is silly, but is there any syntax or technical reason that I cannot do this?
There is a difference between struct initialization, and assignment.
When using heap memory, it's always assignment, since initialization only happens when you're actually declaring the instance (not just a pointer to an instance).
You can use compound literals:
struct sometype *ms = malloc(sizeof *ms);
*ms = ((struct sometype) { .a = 0 });
But of course this might be worse than just doing:
ms->a = 0;
since it will write to all fields of the structure, setting all the fields that weren't mentioned in the literal to zero. Depending on what you need, this can be needlessly costly.
Well, this does not apply to a struct on heap.
Yes. It will not. That's because there is a difference in initialization and assignment. In case of
sometype a = {.a =0};
this is initialization. In case of dynamic allocation
sometype *a = malloc(sizeof(struct sometype);
*a = {.a =0};
there is assignment.
I implement a file's struct in my program but for some arrays in the struct I don't know the size. The size of the array is stored in another variable but it's unknown before the struct is filled in.
struct Vertex {
float x;
float y;
float z;
};
struct myFile {
ulong nVertices;
Vertex vertices[nVertices];
};
That gives an error: "error C2065: 'nVertices' : undeclared identifier".
You should store a pointer in your struct:
Vertex *vertices;
Then allocate the memory at runtime:
myFile f;
f.vertices = malloc(nVertices * sizeof(Vertex));
if (f.vertices == 0)
handle_out_of_memory();
f.nVertices = nVertices;
Remember to free the memory when done:
free(f.vertices);
C99 introduces 'flexible array members', which may be what you want to use. Your code still ends up looking remarkably like the code suggested by #frast, but is subtly different.
§6.7.2.1 Structure and union specifiers
A structure or union shall not contain a member with incomplete or function type (hence,
a structure shall not contain an instance of itself, but may contain a pointer to an instance
of itself), except that the last member of a structure with more than one named member
may have incomplete array type; such a structure (and any union containing, possibly
recursively, a member that is such a structure) shall not be a member of a structure or an
element of an array.
[...]
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. With two
exceptions, the flexible array member is ignored. First, the size of the structure shall be
equal to the offset of the last element of an otherwise identical structure that replaces the
flexible array member with an array of unspecified length.106) Second, when a . (or ->)
operator has a left operand that is (a pointer to) a structure with a flexible array member
and the right operand names that member, it behaves as if that member were replaced
with the longest array (with the same element type) that would not make the structure
larger than the object being accessed; the offset of the array shall remain that of the
flexible array member, even if this would differ from that of the replacement array. If this
array would have no elements, it behaves as if it had one element but the behavior is
undefined if any attempt is made to access that element or to generate a pointer one past
it.
EXAMPLE Assuming that all array members are aligned the same, after the declarations:
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
the three expressions:
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
have the same value. The structure struct s has a flexible array member d.
If sizeof (double) is 8, then after the following code is executed:
struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);
and assuming that the calls to malloc succeed, the objects pointed to by s1 and s2 behave as if the
identifiers had been declared as:
struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;
Following the further successful assignments:
s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);
they then behave as if the declarations were:
struct { int n; double d[1]; } *s1, *s2;
and:
double *dp;
dp = &(s1->d[0]); // valid
*dp = 42; // valid
dp = &(s2->d[0]); // valid
*dp = 42; // undefined behavior
The assignment:
*s1 = *s2;
only copies the member n and not any of the array elements. Similarly:
struct s t1 = { 0 }; // valid
struct s t2 = { 2 }; // valid
struct ss tt = { 1, { 4.2 }}; // valid
struct s t3 = { 1, { 4.2 }}; // invalid: there is nothing for the 4.2 to initialize
t1.n = 4; // valid
t1.d[0] = 4.2; // undefined behavior
106) The length is unspecified to allow for the fact that implementations may give array members different
alignments according to their lengths.
The example is from the C99 standard.