Cannot initialize an array of struct within a struct - c

I'm trying to create an RPG-esque inventory, my inventory contains a type 'sword' which is an array.
Here's the code for my sword struct:
typedef struct
{
char weaponName[35];
int damage;
int rarity;
float price;
} sword;
Here's the one for the inventory:
typedef struct
{
sword SwordInvent[size];
} inventory;
I tried to initialize the SwordInvent array in the main function but it ends up with an error.
[Error] expected expression before '{' token
main()
{
inventory inv;
inv.SwordInvent[0] = {"Paper Sword", 1, 1, 1};
}
Can anyone be kind enough to help me get out of this hole? :(
EDIT
I could not thank you all enough! I wasn't really expecting to get this much of help! Seriously, thank you!

You can't just start listing stuff inside braces and have the compiler magically figure out what you want. It doesn't work like that, except with initializers. Initializers, e.g.
const sword excalibur = { "Excalibur!", INT_MAX, INT_MAX, FLT_MAX };
are different from assignment. With initializers, it's assumed that the right-hand side is going to match the left-hand side, since it's an initializer. Also, they existed longbefore compound literals, so way back this was all you could to with = and structures.
Your code (assignment) is a bit like this:
float x;
x = 1 / 2;
This does not do a floating-point division, since 1 and 2 are both integers; the compiler does not figure out that this should have a floating point result based on the type of the left-hand side (and no, with an initializer for a float it still doesn't help, float x = 1 / 2; will not assign 0.5 but 0; end of analogy).
The same is true (kind of) for your brace-list assignment. There needs to be something on the right hand side that indicates the type of the braced list.
Thus, you need to use the compound literal syntax:
inv.SwordInvent[0] = (sword) {"Paper Sword",1,1,1};
Also, your sword is not an array, it's a struct.

When you write:
inventory inv;
inv.SwordInvent[0]={"Paper Sword",1,1,1};
you are not doing initialization; you are doing (attempting to do) an assignment. Initialization would look like:
inventory inv = { { "Paper Sword", 1, 1, 1 } };
Also note that this initializes all elements of the inv variable; the ones without an explicit value are zeroed.
If you want to do assignment, use a compound literal (C99 or later):
inventory inv;
inv.SwordInvent[0] = (sword){ "Paper Sword", 1, 1, 1 };
Note that after this executes, only element 0 of the array has values assigned; all the other elements are still uninitialized (have indeterminate values). So, unless size == 1, there's a fairly big difference between the initialization and the assignment.

You have to specify the type before assignment:
inv.SwordInvent[0] = (sword) { "Paper Sword", 1, 1, 1 };

You can do it at the declaration line.
The simplest way to emulate what you're trying to do is:
inventory inv = {{"Paper Sword",1,1,1}};
A more general (and perhaps readable) manner would be:
inventory inv =
{
.SwordInvent =
{
{
.weaponName = "Paper Sword",
.damage = 1,
.rarity = 1,
.price = 1
}
}
};
If you want to initialize, say, two entries in the array, then you could use something like:
inventory inv =
{
.SwordInvent =
{
{
.weaponName = "Paper Sword",
.damage = 1,
.rarity = 1,
.price = 1
},
{
.weaponName = "Light Saber",
.damage = 100,
.rarity = 100,
.price = 100
}
}
};

inv.SwordInvent[0]={"Paper Sword",1,1,1};
Such assignment is illegal. Expression at the right side of = should have proper type. The easiest way is to use compound literal (as mentioned in other answers):
inv.SwordInvent[0] = (sword){"Paper Sword",1,1,1};
Alternatively you can use next initialization:
inventory inv = {
{
{"Paper Sword",1,1,1}, /* init SwordInvent[0] */
{"Paper Sword",1,1,1}, /* init SwordInvent[1] */
/* ... */
}
};
This variant conforms to C89, that may be useful if your compiler doesn't support newest language features.

Related

can I reduce a long array initialization to a short initializer list in C?

I have some C99 code that looks like this:
struct foo {
char* buf;
size_t buf_sz;
};
struct foo list[3];
list[0].buf = "The first entry";
list[0].buf_sz = strlen(list[0].buf);
list[1].buf = "The second entry";
list[1].buf_sz = strlen(list[1].buf);
list[2].buf = "The third entry";
list[2].buf_sz = strlen(list[2].buf);
Is there a shorthand way of writing this in an initializer list? Is something like this safe?
struct foo list[] = {
{ "The first entry", strlen(list[0].buf) },
{ "The second entry", strlen(list[1].buf) },
{ "The third entry", strlen(list[2].buf) }
};
How about this one:
#define S(str) { str, sizeof(str) - 1 }
struct foo list[] = {
S("The first entry"),
...
};
#undef S
I #undefed the macro right after the initialiser. Such short names should be used in a compact&short piece of code only. If you want to use it elsewhere, use a self-explanatory name (it should include the name of the struct type) and remove the #undef.
Note this only works with a string literal. But it works at file-level and does not introduce any run-time overhead. Thus it also works for const struct list[], i.e. a constant array.
Be cautious about the sizeof(str) - 1. This is the same as strlen yields, but the allocated memory is actually sizeof(str).
One alternative would be:
struct foo {
char* buf;
size_t buf_sz;
};
...
struct foo list[] = {
{ "The first entry" },
{ "The second entry" },
{ "The third entry" }
};
...
for ( int i = 0; i < sizeof(foo)/sizeof(foo[0]); i++ )
list[i].buf_sz = strlen(list[i].buf); // number of chars in string
If you need to put in the number of actual bytes consumed by the string, then you'd need a +1 on your strlen. I don't know what the API requires.
Try
#define STR1 "The first entry"
struct foo list[] = {
{STR1, sizeof STR1},
...
Please note that the size is set to strlen(STR1) + 1, due to the "string"'s 0-terminator. To adjust this do
struct foo list[] = {
{STR1, sizeof STR1 - 1},
...
You cannot use your second definition for a static duration foo array, because initializer lists for static duration objects are required to be constant. Draft n1256 for C99 states in 6.7.8 Initialization §4
4 All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
Even for automatic (block scope) foo arrays it is unspecified by the standard whether your second syntax is valid or undefined behaviour. Item §23 of same paragraph says
23 The order in which any side effects occur among the initialization list expressions is
unspecified.133)
and note 133 precises:
In particular, the evaluation order need not be the same as the order of subobject initialization
That means that if would be acceptable for a compiler implementation to initialize the buf_sz members before the buf members which would lead to UB (even if it gives correct result without any warning on my old MSVC2008).
For that reasons my advice is to initialize only the buf members, and then use a loop to set the buf_sz members that require the structs to be already initialized - as a side effect, if can be used the same for static or dynamic storage duration:
struct foo list[] = {
{ "The first entry" },
{ "The second entry" },
{ "The third entry" }
};
and later in code:
int i;
for(i=0; i<sizeof(foo)/sizeof(foo[0]); i++) {
foo[i].buf_sz = strlen(foo[i].buf);
}

Evaluating a struct inside of a conditional statement in C

Is there a way to evaluate a struct variable inside of a conditional statement as a whole such that each element does not need to be written out? For example given the following struct:
typdef struct
{
int a;
int b;
int c;
} number;
number foo = {1, 2, 3};
I would like to evaluate the elements in an if statement such as:
if (foo.a == 1 && foo.b == 2 && foo.c == 3)
{
...
}
However, I want to evaluate the entire struct without having to list individual elements. I know this is not correct but this is along the lines of what I want to accomplish:
if (foo == {1, 2, 3})
{
...
}
Your help is greatly appreciated. Thanks.
You can represent a temporary struct through a compound literal
(number) { 1, 2, 3 }
so a more logical attempt would look as
if (foo == (number) { 1, 2, 3 })
...
but it won't work, since C language does not provide a built-in operator for comparison of struct objects. You can use memcmp instead
if (memcmp(&foo, &(number) { 1, 2, 3 }, sizeof foo) == 0)
...
but unfortunately, this is not guaranteed to work due to unpredictability of the content of any padding bytes your struct objects might have in them.
The best course of action in this case might be to write a comparison function manually
inline bool is_same_number(const number *lhs, const number *rhs)
{
return lhs->a == rhs->a && lhs->b == rhs->b && lhs->c == rhs->c;
}
and then use it in combination with compound literal feature
if (is_same_number(&foo, &(number) { 1, 2, 3 }))
...
Not sure you need a struct but either way just cast to unsigned char * and use a literal where 0x000000010002 would be the format of the literal representing 3 integers {0 1 2}.
To avoid issues with endianess just declare the integral as a value at runtime and then the representation will match the system endian.
You could also use a macro if run time definition was not allowed.
I wouldn't waste the memcpy.

Is it valid to use a C99-style designated initializer list to initialize the members of a bit field within a union in the following way?

When I wrote a question regarding PC-Lint, I had made an assumption that the following initialization is valid in C99. #JoachimPileborg mentioned that it may not be and I haven't been able to find any information that provides a good example one way or another. I know that it compiles and behaves as I expect, I would just like to know for certain that it is proper C99 code.
Is this a valid way to initialize the following union in C99?
typedef union
{
struct
{
unsigned int a : 4;
unsigned int b : 4;
unsigned int c : 4;
unsigned int d : 4;
} bits;
unsigned short value;
} My_Value;
int main (void)
{
My_value test[] =
{
{
.bits.a = 2,
.bits.b = 3,
.bits.c = 2,
.bits.d = 3,
},
{
.bits.a = 1,
.bits.b = 1,
.bits.c = 1,
.bits.d = 0,
},
};
/* Do something meaningful. */
return 0;
}
Looks sane... if your tame compiler doesn't complain with standards compliance cranked way up, I'd use it. Much more worrying is that you presumably are trying to overlay value and bits, and stuffing data into one alternative of an union an taking it out of the other is undefined. Endianness aside, the union will probably use up a full word, and very well could have the value at one end and the bits at the other (depending on available instructions and their convenience or timing). The standards declare this undefined precisely to give implementations such leeway.

expected expression before '{' token

I am getting: "error: expected expression before '{' token" for the line I've commented before. If the struct is already defined why would it need a "{" before token. Thanks for any help you can provide.
struct sdram_timing {
u32 wrdtr;
u32 clktr;
};
int calibration(void);
unsigned char read_i2c_cal(void);
static unsigned int eepcal[15];
main() {
DQS_autocalibration();
}
int calibration(void)
{
struct sdram_timing scan_list[30];
read_i2c_cal();
if(eepcal[0] == 0){
scan_list = {{eepcal[1], eepcal[2]}, {-1, -1}}; // <-- PROBLEM LINE
}
else {
//foo
}
return 0;
}
unsigned char read_i2c_cal(void) {
eepcal[0] = 0;
eepcal[1] = 02;
eepcal[2] = 03;
}
The error is because you can't assign an array that way, that only works to initialize it.
int arr[4] = {0}; // this works
int arr2[4];
arr2 = {0};// this doesn't and will cause an error
arr2[0] = 0; // that's OK
memset(arr2, 0, 4*sizeof(int)); // that is too
So applying this to your specific example:
struct sdram_timing scan_list[30];
scan_list[0].wrdtr = 0;
scan_list[0].clktr = 0;
or you could use memset the same way, but instead of sizeof(int) you need size of your structure. That doesn't always work... but given your structure, it will.
Arrays in C language are not assignable. You can't assign anything to the entire array, regardless of what syntax you use. In other words, this
scan_list = { { eepcal[1], eepcal[2] }, {-1, -1} };
is not possible.
In C89/90 you'd have to spell out your assignments line by line
scan_list[0].wrdtr = eepcal[1];
scan_list[0].clktr = eepcal[2];
scan_list[1].wrdtr = -1;
scan_list[1].clktr = -1;
In modern C (post-C99) you can use compound literals to assign entire structs
scan_list[0] = (struct sdram_timing) { eepcal[1], eepcal[2] };
scan_list[1] = (struct sdram_timing) { -1, -1 };
Finally, in modern C you can use memcpy and compound literals to copy data to the array
memcpy(scan_list, (struct sdram_timing[]) { { eepcal[1], eepcal[2] }, {-1, -1} },
2 * sizeof *scan_list);
The last variant, albeit not very elegant, is the closest way to "emulate" array assignment.
You can only use an initializer list in the declaration of the variable, not after the fact.
Initializer list can only be used to initialize an array. You cannot use it afterwards.
However if you use GCC, you can use Compound Literal extension:
scan_list = (struct sdram_timing[30]){{eepcal[1], eepcal[2]}, {-1, -1}};
You might need to change scan_list type to be struct sdram_timing *

How do you cast from a bit-field to a pointer?

I've written the following bit of code that is producing a
warning: initialization makes pointer from integer without a cast
OR A
warning: cast to pointer from integer of different size
from gcc (GCC) 4.1.1 20070105 (Red Hat 4.1.1-52)
struct my_t {
unsigned int a : 1;
unsigned int b : 1;
};
struct my_t mine = {
.a = 1,
.b = 0
};
const void * bools[] = { "ItemA", mine->a, "ItemB", mine->b, 0, 0 };
int i;
for (i = 0; bools[i] != NULL; i += 2)
fprintf(stderr, "%s = %d\n", bools[i], (unsigned int) bools[i + 1] ? "true" : "false");
How do I get the warning to go away? No matter what I've tried casting, a warning seems to always appears.
Thanks,
Chenz
Hmm, why do you insist on using pointers as booleans? How about this alternative?
struct named_bool {
const char* name;
int val;
};
const struct named_bool bools[] = {{ "ItemA", 1 }, { "ItemB", 1 }, { 0, 0 }};
const void * bools[] = { "ItemA", mine->a, "ItemB", mine->b, 0, 0 };
There are several problems with this snippet:
mine isn't declared as a pointer type (at least not in the code you posted), so you shouldn't be using the -> component selection operator;
If you change that to use the . selection operator, you'd be attempting to store the boolean value in a or b as a pointer, which isn't what you want;
But that doesn't matter, since you cannot take the address of a bit-field (§ 6.5.3.2, paragraph 1).
If you're trying to associate a boolean value with another object, you'd be better off declaring a type like
struct checkedObject {void *objPtr; int check};
and initialize an array as
struct checkedObject[] = {{"ItemA", 1}, {"ItemB", 0}, {NULL, 0}};
Bit-fields have their uses, but this isn't one of them. You're really not saving any space in this case, since at least one complete addressable unit of storage (byte, word, whatever) needs to be allocated to hold the two bitfields.
Two problems:
Not sure why you are trying to convert unsigned int a:1 to a void*. If you are trying to reference it, the syntax would be &mine->a rather than mine->a, but...
You can't create a pointer to a bit in C (at least as far as I know). If you're trying to create a pointer to a bit, you may want to consider one the following options:
Create a pointer to the bitfield structure (i.e. struct my_t *), and (if necessary) use a separate number to indicate which bit to use. Example:
struct bit_ref {
struct my_t *bits;
unsigned int a_or_b; // 0 for bits->a, 1 for bits->b
}
Don't use a bit field. Use char for each flag, as it is the smallest data type that you can create a pointer to.
Do use a bit field, but implement it manually with boolean operations. Example:
typedef unsigned int my_t;
#define MY_T_A (1u << 0)
#define MY_T_B (1u << 1)
struct bit_ref {
struct my_t *bits;
unsigned int shift;
};
int deref(const struct bit_ref bit_ref)
{
return !!(bit_ref.bits & (1 << bit_ref.shift));
}
There's a few ways you could get rid of the warning, but still use a pointer value as a boolean. For example, you could do this:
const void * bools[] = { "ItemA", mine->a ? &bools : 0, "ItemB", mine->b ? &bools : 0, 0, 0 };
This uses a NULL pointer for false, and a non-null pointer (in this case, &bools, but a pointer to any object of the right storage duration would be fine) for true. You would also then remove the cast to unsigned int in the test, so that it is just:
fprintf(stderr, "%s = %d\n", bools[i], bools[i + 1] ? "true" : "false");
(A null pointer always evaluates as false, and a non-null pointer as true).
However, I do agree that you are better off creating an array of structs instead.

Resources