How to use pointers in an union in C? - c

I want to initialize this structure:
typedef struct
{
int num;
union
{
const char** ppStrList;
const char* pStr;
};
union
{
int num1;
int num2;
};
} tMyStruct;
If I try to initialize this structure when I declare the variable I get an error: For example;
const char *gpStr = "str";
const char *gpStrList[2] =
{
{"str1"},
{"str2"}
};
tMyStruct myStruct[2] =
{
{0,{gpStrList},{3}},
{1,{gpStr},{4}}
};
The variable gpStr can´t be used to initialize the structure
But it can be initialized inside of a function without problem:
int main(int argc, char *argv[])
{
myStruct[0].num = 0;
myStruct[0].ppStrList = gpStrList;
myStruct[0].num1 = 3;
myStruct[1].num = 0;
myStruct[1].pStr = gpStr;
myStruct[1].num2 = 3;
}
Why the structure can't be initialized when it is declared?
I think the union has a special behavior because if I don’t use an union the problem doesn’t exist. For example:
typedef struct
{
int num;
union /* Union to contain ppStrList and pStr pointers */
{
const char** ppStrList;
const char* pStr;
};
union
{
int num1;
int num2;
};
} tMyStruct1;
typedef struct
{
int num;
/* I don´t use an union */
const char** ppStrList;
const char* pStr;
union
{
int num1;
int num2;
};
} tMyStruct2;
const char gStr[] = "str";
const char *gpStrList[2] =
{
{"str1"},
{"str2"}
};
tMyStruct1 myStruct1[2] = /* Structure with union inside */
{
{0,{gpStrList},{3}},
{1,{gStr},{4}} /* <--- Error here if I use gStr address with union */
};
tMyStruct2 myStruct2[2] = /* Structure without union inside */
{
{0,gpStrList,NULL,{3}},
{1,NULL,gStr,{4}} /* <--- No poblem here if I use gStr address */
};

Variables initialized at file scope (whether static or not) and variables initialized at block scope with static duration can only be initialized with constants. Automatic variables (necessarily at block scope) can be initialized with expressions.
Also, when a union is initialized, the initializer must match the first member unless a designated initializer is used. With C99 or later, you could write:
typedef struct
{
int num;
union
{
const char** ppStrList;
const char* pStr;
};
union
{
int num1;
int num2;
};
} tMyStruct;
const char gpStr[] = "str";
const char *gpStrList[2] =
{
"str1",
"str2"
};
tMyStruct myStruct[2] =
{
{ .num = 0, { .ppStrList = gpStrList }, { .num1 = 3 } },
{ .num = 1, { .pStr = gpStr }, { .num2 = 4 } },
};
Note the tweaked type for gpStr. You don't have to use the designated initializers for all the elements, but consistency suggests you probably should. Also note that a union of two differently named int members is modestly pointless. The different elements of a union should normally be of different types.

The unscientific answer:
I think you can only initialize with constants. In the first instance you are initializing (i.e. allocating a value before the program runs) with a variable; by definition variables don't have a "accessible" value until the program runs. In your second example the "initialization" is really an "assignment" with a variable, and that's OK. I don't think it has anything to do with the fact that you have a union.
Simplest example:
#include <stdio.h>
int one = 1;
int two = one;
int main(void) {
printf("one is %d; two is %d\n", one, two);
}
When I try to compile, I get
consts.c:3: error: initializer element is not constant
I hope this clarifies things.

The problem is that you can only initialize the FIRST element of a union, unless you use an explicit designator. So the following works:
tMyStruct myStruct[2] =
{
{0,{gpStrList},{3}},
{1,{.pStr = "str"},{4}}
};

Related

Pointer in const C struct

Assuming that the following structures exist...
typedef struct MyFirstStruct
{
uint8_t someContent;
}
typedef struct MySecondStruct
{
MyFirstStruct* firstStructs;
uint8_t firstStructCount;
}
... and a function gets the following Parameter.
const MySecondStruct* const secondStruct
Is it allowed to change any value?
I am sure that this is not correct:
secondStruct->firstStructCount++.
But neither the Compiler nor PC-Lint complains about secondStruct->firstStructs->someContent++.
Is it allowed to do Change someContent because firstStructs is not const or is the behavior undefined?
Thanks!
The values of the nested struct(s) firstStructs can change as long as the pointer does not change. The constness of the pointer prevents the pointer value from changing, but the constness of the struct only means that its values must not change (i.e. the value of the pointer and the count).
You can modify the struct(s) pointed to by firstStructs arbitrarily without changing the pointer. You can also clearly see from the struct definition that this is legal, because firstStructs is a pointer to struct MyFirstStruct, not a pointer to const struct MyFirstStruct.
Here is an example to understand the principle without the const-pointer to const elements:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int x;
} Simple;
typedef struct {
Simple* s;
int scount;
} Nested;
int main()
{
Nested x;
x.scount = 10;
x.s = malloc(sizeof(Simple) * x.scount);
const Nested n = x;
for (int i = 0; i < n.scount; ++i)
{
n.s[i].x = i;
printf("%d\n", n.s[i].x);
}
}
It is OK and it was easy to check yourself:
typedef struct
{
uint8_t someContent;
} MyFirstStruct;
typedef struct
{
MyFirstStruct* firstStructs;
uint8_t firstStructCount;
}MySecondStruct;
MyFirstStruct fs;
MySecondStruct str = {.firstStructs = &fs};
const MySecondStruct* const secondStruct = &str;
int main()
{
secondStruct->firstStructs->someContent++;
}
but did not as the posted code was full of syntax errors.
You cant of course change the pointer itself:
This will give you errors:
typedef struct
{
uint8_t someContent;
} MyFirstStruct;
typedef struct
{
MyFirstStruct* firstStructs;
uint8_t firstStructCount;
}MySecondStruct;
MyFirstStruct fs[2];
MyFirstStruct ss;
MySecondStruct str = {.firstStructs = fs};
const MySecondStruct* const secondStruct = &str;
int main()
{
secondStruct->firstStructs++->someContent++;
secondStruct->firstStructs = &ss;
}

Struct array typedef in struct shows incomplete data

I'm having trouble with a piece of code where a typedef array is created of a struct. That typedef is then used in another struct.
When receiving the typedef in a function and initialising the struct with the typedef in it, I only get data of the first element in the array.
Below I have a simplified example of what I'm getting at the moment.
struct simple_struct {
double a;
};
typedef struct simple_struct arr_typedef[2];
struct struct_with_typedef {
const arr_typedef typedef_arr;
};
void foo(const arr_typedef arg_s) {
struct struct_with_typedef test = {
*arg_s
};
int i;
for (i = 0; i < 2; i++) {
printf("value: %f \n", test.typedef_arr[i].a);
}
}
int main(int argc, char* argv[]) {
arr_typedef d_test = { {1}, {2} };
foo(d_test);
return 1;
}
When compiled, using gcc 4.4, and run I see the following output:
~/public > ./test
value: 1.000000
value: 0.000000
Would someone be able to explain why the value of the second item isn't available?
If I leave out the dereference I get the following error whilst compiling, which I also don't get:
test.c:82: error: incompatible types when initializing type 'double' using type 'const struct simple_struct *'
I have removed the typedef and the same result persists. Sort of understand that indeed that one initialiser value is given. But there is only one member in the struct, or are they expanded in the background?
If so, how would you initialise both values?
struct simple_struct {
double a;
};
struct struct_with_arr {
const struct simple_struct struct_arr[2];
};
void foo(const struct simple_struct arg_s[2]) {
struct struct_with_arr test = {{*arg_s}};
int i;
for (i = 0; i < 2; i++) {
printf("value: %f \n", test.struct_arr[i].a);
}
}
int main(int argc, char* argv[]) {
struct simple_struct d_test[2] = { {1}, {2} };
foo(d_test);
return 1;
}
In this declaration
struct struct_with_typedef test = {
*arg_s
};
there is used an object of the type struct simple_struct to initialize an array of the type const arr_typedef.
Firstly you need to enclose the initializer in braces and add an initializer for the second element of the array if you want to do so.
A correct way to initialize the array is the following
#include <stdio.h>
struct simple_struct
{
double a;
};
typedef struct simple_struct arr_typedef[2];
struct struct_with_typedef
{
const arr_typedef typedef_arr;
};
void foo( const arr_typedef arg_s )
{
struct struct_with_typedef test =
{
{ arg_s[0], arg_s[1] }
};
for ( int i = 0; i < 2; i++ )
{
printf("value: %f \n", test.typedef_arr[i].a);
}
}
int main( void )
{
arr_typedef d_test = { {1}, {2} };
foo(d_test);
return 0;
}
The program output is
value: 1.000000
value: 2.000000
You can write the initializers also the following way
struct struct_with_typedef test =
{
{ *arg_s, *( arg_s + 1 ) }
};
Take into account that an array designator used as an initializer expression is implicitly converted to pointer to its first element. Dereferencing the pointer you get the first element itself.
There is no syntax in C for initializing an array with a single initializer representing another array to copy values from.
To fix your code you could do one of these options:
List the members: struct struct_with_arr test = {{arg_s[0], arg_s[1]}};
Change the function to accept struct struct_with_arr as the parameter type, in which case you can use struct struct_with_arr test = arg;
Copy without an initializer: struct struct_with_arr test; memcpy(&test.struct_arr, arg_s, sizeof test.struct_arr);. (Actually you can't use this option since you have defined the struct element as const ... not really a great idea in the first place in my opinion)
For completeness I will mention the code:
struct struct_with_arr foo = *(struct struct_with_arr *)arg_s;
This is one of those things that is technically undefined behaviour (if the argument source was not actually a struct_with_arr) but is likely to work on any actual compiler that exists. My advice would be to not do this.

initializing a struct array with array inside

So I made a struct with a with an uninitialized array inside but the outer struct is an array that is initialized. I then loop through and print the values but I'm not getting anything. NUM is already defined as 12.
#include "header.h"
#include <stdio.h>
#include <stdlib.h>
void make() {
struct suit {
char *type;
int people[];
} deck[4] = {"Hunter", NUM,
"Fighter", NUM,
"Jumper", NUM,
"Strider", NUM};
};
//print type and numbers 1-12
for (int i = 0; i < 4; i++) {
for (int j = 0; j < NUM; i++) {
printf(deck[i].type);
printf(deck[i].people[j]);
}
}
}
Allocate the memory of int people[]; array, So, your struct should look like this, since it is an object definition:
struct suit {
char *type;
int people[10]; //compile time allocation
} deck[4] = {"Hunter", NUM,
"Fighter", NUM,
"Jumper", NUM,
"Strider", NUM};
people is a flexible array member and Standard C does not allow initialization of flexible array member. Though, GCC allows static initialization of flexible array as an extension. So,
struct suit {
char *type;
int people[];
} deck = {"Hunter", NUM};
is valid snippet as per GCC, but at the same time GCC doesn't allow nested array initialization when flexible array member is involved and therefore the initializer for deck[4] is not valid.
Of course, this extension only makes sense if the extra data comes at the end of a top-level object, as otherwise we would be overwriting data at subsequent offsets. To avoid undue complication and confusion with initialization of deeply nested arrays, we simply disallow any non-empty initialization except when the structure is the top-level object. For example:
struct foo { int x; int y[]; };
struct bar { struct foo z; };
struct foo a = { 1, { 2, 3, 4 } }; // Valid.
struct bar b = { { 1, { 2, 3, 4 } } }; // Invalid.
struct bar c = { { 1, { } } }; // Valid.
struct foo d[1] = { { 1, { 2, 3, 4 } } }; // Invalid.
Also note that you should use appropriate format specifiers in printf for different data types.

How to hold a const value in a struct in C (not C++)?

How can I hold a constant value in a struct? If I put const at LEBEL0 I would not be able to assign to it at LEBEL1. But if I do not put const at LEBEL0, then I will get qualifier lost warning at LEBEL1. Is there any way to do this? I came up with a dirty solution (below) but I think there could be a better one...
typedef struct _MyStruct
{
const SomePointerType pktData; <--LABEL0
}MyStruct;
const SomePointerType SomeFunctionICannotModify()
{
…
}
void SomeFunction()
{
MyStruct data;
….
data.pktData = SomeFunctionICannotModify(); <--LABEL1
....
SomeSubFunction(&data);
}
void SomeSubFunction(MyStruct* data)
{
this function does not modify any fields in "data".
}
PS: The code above is a "model", not a real code, to illustrate my problem. It does not have all the codes in my actual program. Please do not ask questions like "why would you do that", "you do not have to do that", "that code does not compile" and so on.
My dirty solution
typedef struct _ConstHolder
{
const SomePointerType pktData;
}ConstHolder;
typedef struct _MyStruct
{
ConstHolder holder;
}MyStruct;
void SomeFunction()
{
MyStruct data;
….
ConstHolder holder = {SomeFunctionICannotModify()};
data.holder = holder;
....
SomeSubFunction(&data);
}
Your question is difficult to understand, but you may be under the misapprehension that you cannot modify pktData after you've assigned to it. This is not true. Consider the following, which is perfectly legal:
const char * str = "hello";
printf("%s\n", str);
str = "goodbye";
printf("%s\n", str);
The const does not say that you cannot reassign the pointer, but instead that you cannot alter the data to which it points.
It looks to me like the problem is that you are using typedef along with const. The thing about C is that const is not as well implemented and usable as it is in C++. And typedef is not well done either.
This article talks a bit about const in C. As does this article as well on const in C.
For instance if you use the following, it will compile with no problems in Visual Studio 2005
typedef char * pchar;
typedef struct {
int i;
int j;
} Thing;
typedef Thing * SomePtr;
typedef struct {
// const SomePtr pSome;
// const pchar mychar;
const char * mychar;
const Thing *pSome;
} MyStruct;
const SomePtr SomePtrFunc ()
{
static Thing jj = {1, 2};
return &jj;
}
int main(int argc, char **argv)
{
MyStruct josey;
josey.pSome = SomePtrFunc();
josey.mychar = "this";
return 0;
}
If I were to then try to modify what is pointed to by josey.pSome by doing something like adding a line of code such as josey.pSome->i = 0; then I will see a compilation error of error C2166: l-value specifies const object.
However if you use the following code with comments switched around, you get an error message of error C2166: l-value specifies const object
typedef char * pchar;
typedef struct {
int i;
int j;
} Thing;
typedef Thing * SomePtr;
typedef struct {
const SomePtr pSome;
const pchar mychar;
// const char * mychar;
// const Thing *pSome;
} MyStruct;
const SomePtr SomePtrFunc ()
{
static Thing jj = {1, 2};
return &jj;
}
int main(int argc, char **argv)
{
MyStruct josey;
josey.pSome = SomePtrFunc(); // <- error C2166: l-value specifies const object
josey.mychar = "this"; // <- error C2166: l-value specifies const object
return 0;
}
Finally what you could do is to make the typedef to include the const qualifier which would then make the const qualifier part of the type:
typedef const char * pchar;
typedef struct {
int i;
int j;
} Thing;
typedef const Thing * SomePtr;
typedef struct {
SomePtr pSome;
pchar mychar;
// const char * mychar;
// const Thing *pSome;
} MyStruct;

How can I do a structure type conversion like this?

say if I have two structure types:
typedef struct
{
unsigned int * a;
char * b;
char * c;
}TYPEA;
typedef struct
{
unsigned int * a;
unsigned int * b;
}TYPEB;
And I also have a pointer array, which contains pointer pointing to TYPEA types:
TYPEA* AArray[1] =
{
&ga,
};
ga is defined as:
TYPEA ga =
{
&num1,
&b,
&c,
};
If I have another TYPEA var defined, I can modify AArray[0]'s content's value, like this:
TYPEA another_ga =
{
&num3,
&b,
&c,
};
void change_AArray()
{
*AArray[0] = another_ga;
}
This works perfectly good, but the question is, as TYPEA and TYPEB's first 32bits are same(Both are unsigned int) is it possible for me to change AArray's ga's content to a TYPEB structure?
Here are my attempts:
void change_AArray()
{
/* *AArray[0] = another_ga; */
/* Compile Error */
/* *AArray[0] = gb; */
/* Compile Error */
/* *AArray[0] = (TYPEA)gb; */
/* Passed compile, but AArray[0] is not changed */
/* AArray[0] = (TYPEA*)&gb; */
}
I then tried to add another pointer and put it in AArray[0] instead of using &ga, but it can't pass compile stage, I think C is requiring a constant in the initialize list.
TYPEA * pga = &ga;
TYPEA* AArray[1] =
{
pga, // error, needs a constant expression.
};
void change_AArray()
{
AArray[0] = (TYPEA*)&gb;
}
Is it possible to do the type conversion?
The entire code:
#include <stdio.h>
typedef struct
{
unsigned int * a;
char * b;
char * c;
}TYPEA;
typedef struct
{
unsigned int * a;
unsigned int * b;
}TYPEB;
int num1 = 10, num2 = 20, num3 = 30;
char b = 'b', c = 'c';
TYPEA ga =
{
&num1,
&b,
&c,
};
TYPEA another_ga =
{
&num3,
&b,
&c,
};
TYPEB gb =
{
&num2,
&num3,
};
TYPEA* AArray[1] =
{
&ga,
};
void change_AArray()
{
*AArray[0] = another_ga;
/* How can I do this conversion? */
/* *AArray[0] = gb; */
}
int main(int argc, const char *argv[])
{
printf("ga.a: %d\n", *ga.a);
printf("gb.b: %d\n", *gb.a);
change_AArray();
printf("After calling change_AArray()\n");
printf("ga.a: %d\n", *ga.a);
printf("gb.b: %d\n", *gb.a);
return 0;
}
void change_AArray()
{
*AArray[0] = *((TYPEA*) &gb); //#1
/* error:
*(AArray[0]) = (TYPEA) gb; //#2
*/
}
You can use #1 for your purpose.
If use #2, gcc reports an error:
error: conversion to non-scalar type requested
Questions are:
- What's scalar/non-scalar type?
- What does this error message mean?
- Why #2 fails?
What's scalar/non-scalar type?
int, char, pointers are scalar type, and a structure is not.
See details here,http://herbert.the-little-red-haired-girl.org/en/prgmsc1/docs/part2a.pdf
What does this error message mean?
If you do assignment between scalar and non-scalar type, you'll get this error.
Why #2 fails?
For *(AArray[0]) = (TYPEA) gb;, both sides are non-scalar types. So, it looks like it shouldn't get the above error.
However, C doesn't allow cast between structures. See http://msdn.microsoft.com/en-us/library/d9f2bsy2.aspx.
A test program,
struct a{ int i;};
struct b{ int i; };
int main (int argc, char *argv[])
{
struct a aaa;
struct b bbb;
bbb = (struct b)aaa;
return 0;
}
gcc reports an error:
error: conversion to non-scalar type requested
So to conclude, the reason for failure of #2 is that C language doesn't allow cast between structures.
Well, you can use union
union UNION_AB {
TYPEA a;
TYPEB b;
};
UNION_AB* UARRAY[1] = { &ua };
You can't cast two different types of struct directly, you have to assign the fields like this:
(*AArray[0]).a = gb.a;

Resources