I am doing a little experimenting on the source code of Doom. I am not very familiar with C, but I am giving it a go. The code I am trying to modify looks like this:
{"sndserver", (int *) &sndserver_filename, (int) "sndserver"},
And I am trying to do this instead (to remove having the same location hardcoded in multiple places):
{"sndserver", (int *) &sndserver_filename, (int) sndserver_filename},
But it gives me the error in the title. I tried declaring another variable in the same location sndserver_filename originates from that does the conversion there but as soon as I try to use it here I get the same error.
Is there a reason that the string will convert to an int, but the variable won't, and is there a way around it?
Context:
#ifdef SNDSERV
extern char* sndserver_filename; // value is "./sndserver"
extern int mb_used;
#endif
Struct declaration and initialization
typedef struct
{
char* name;
int* location;
int defaultvalue;
int scantranslate; // PC scan code hack
int untranslated; // lousy hack
} default_t;
default_t defaults[] =
{
{"mouse_sensitivity",&mouseSensitivity, 5},
{"sfx_volume",&snd_SfxVolume, 8},
{"music_volume",&snd_MusicVolume, 8},
{"show_messages",&showMessages, 1},
#ifdef NORMALUNIX
{"key_right",&key_right, KEY_RIGHTARROW},
{"key_left",&key_left, KEY_LEFTARROW},
{"key_up",&key_up, KEY_UPARROW},
{"key_down",&key_down, KEY_DOWNARROW},
{"key_strafeleft",&key_strafeleft, ','},
{"key_straferight",&key_straferight, '.'},
{"key_fire",&key_fire, KEY_RCTRL},
{"key_use",&key_use, ' '},
{"key_strafe",&key_strafe, KEY_RALT},
{"key_speed",&key_speed, KEY_RSHIFT},
// UNIX hack, to be removed.
#ifdef SNDSERV
{"sndserver", (int *) &sndserver_filename, (int) "sndserver"},
{"mb_used", &mb_used, 2},
#endif
#endif
#ifdef LINUX
{"mousedev", (int*)&mousedev, (int)"/dev/ttyS0"},
{"mousetype", (int*)&mousetype, (int)"microsoft"},
#endif
{"use_mouse",&usemouse, 1},
{"mouseb_fire",&mousebfire,0},
{"mouseb_strafe",&mousebstrafe,1},
{"mouseb_forward",&mousebforward,2},
{"use_joystick",&usejoystick, 0},
{"joyb_fire",&joybfire,0},
{"joyb_strafe",&joybstrafe,1},
{"joyb_use",&joybuse,3},
{"joyb_speed",&joybspeed,2},
{"screenblocks",&screenblocks, 9},
{"detaillevel",&detailLevel, 0},
{"snd_channels",&numChannels, 3},
{"usegamma",&usegamma, 0},
{"chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 },
{"chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 },
{"chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 },
{"chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 },
{"chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 },
{"chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 },
{"chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 },
{"chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 },
{"chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 },
{"chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 }
};
Let me try to offer an explanation.
You declare
extern char* sndserver_filename;
And then use sndserver_filename in a context where a compile-time address (well, link-time really) is required.
But the value of sndserver_filename is not known at compile time. At the start of the program it's NULL. Later you can assign to it:
sndserver_filename = "aaa";
and assign again:
sndserver_filename = "bbb";
This is not useful for compile-time initialization.
What you really need is a statically allocated array, not a pointer:
extern char sndserver_filename[];
Note the square brackets.
This tells the compiler that there is an array of characters that is statically allocated (the size of the array is not known at this time).
In one of the C files, in global scope, you'll need to do something like this:
char sndserver_filename[256];
to actually allocate the memory (and specify size).
Then you can use sndserver_filename as static initializer, and you can set its value by using strcpy (really, strncpy to make sure you don't copy past array size):
strcpy(sndserver_filename, "bbb");
Note that
sndserver_filename = "aaa";
would now be a compile-time error.
What your compiler said: the initializer element is not a constant expression. An address like &sndserver_filename is a constant expression (the linker fills in the symbol's value), but a pointer variable may hold any address--the actual value may depend on the flow of the program, so it can't be used to initialize anything.
From C11 6.7.9 Initialization:
4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.
That's a constraint and compilers are required to diagnose violations of constraints.
Related
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);
}
I have a simple program as below:
static const char* DeviceID = (char*)"my id";
int length = strlen(DeviceID);
int main(){
}
and the compiler throw the following error:
initializer element is not constant
I don't know why the compiler can't understand my statement:
strlen's prototype is like the following code:
size_t strlen ( const char * str );
Try sizeof which yields a compile time constant
#define MY_ID "my id"
static const char *DeviceID = MY_ID; // no cast needed
int length = sizeof MY_ID - 1; // sizeof also includes the '\0'
int main(void) {
/* ... */
}
As far as I know, there is different with C and C++ when you're trying to initailize global variables.
in C, Rvalue of the global initializing statement should be evaluated at compile time.
for instance,
static const char* DeviceID = (char*)"my id"; <-- compiler can evaluate address of "my_id". so, this is valid.
int length = strlen(DeviceID); <-- but this can't be at compiletime. means that, we should run the process in order to know
its value.
But, C++ doesn't care about like above. it can initialize global ones at runtime.
so if you use C++ compiler instead of, errors'll be disappeared.
I'm trying to initialize a const struct with a designated initializer. However, one of the struct elements is a fixed-width array. I already have the contents I would like to initialize the array with in another fixed-width array of appropriate size.
Is there any way to do this with a designated initializer? A simple (failing example) of what I'm trying to accomplish is demonstrated below.
struct foo {
uint8_t array1[4];
uint8_t array2[4];
}
uint8_t array[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = array, // incompatible pointer to integer conversion
.array2 = { *array } // only copies the first element
};
Short answer: you can't. C does not copy arrays (without the use of (standard library-)functions). The warnings come from the fact that you cannot assign an array as a whole, even when they are static or constant. When an array is used as an r-value in an assignment it decays to a pointer and thus cannot be assigned to another array (as a whole).
The easiest way to go would be to use memcpy, but obviously that must be inside a function.
If bar has global scope, or is declared static, then you won't be able use designated initializers to initialize from non-immediate values, regardless of whether or not the members in question are arrays.
However, if:
bar is declared on the stack of some function, and
Your fixed-size array really does only have 4 elements,
then you might be able to get away with something like this:
#include <stdio.h>
#include <stdint.h>
struct foo {
uint8_t array1[4];
uint8_t array2[4];
};
#define ARRAY_INIT(a) { a[0], a[1], a[2], a[3] }
int main (int argc, char **argv) {
uint8_t arr_init[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = ARRAY_INIT(arr_init),
.array2 = ARRAY_INIT(arr_init),
};
printf("%d, %d\n", bar.array1[0], bar.array2[3]);
return (0);
}
The initializer array must appear before what is being initialized in the stack frame. Or it may come from a function parameter.
Of course if your array is much bigger than this, then using a macro like this will get very messy indeed.
While you may not be able to initialize the array by copying from another array, it may be helpful to use a preprocessor macro:
#define ARRAY_INIT {1, 2, 3, 4}
struct foo const bar = {
.array1 = ARRAY_INIT,
.array2 = ARRAY_INIT
};
Is there a nice way to combine designated initializers from C99, with the result of a malloc?
The following seems to have needless duplication:
typedef struct {
int a, b, c;
} Type;
Type *t = malloc(sizeof *t);
*t = (Type) {
.a = 2,
.b = 3,
.c = 5,
};
Can the use of Type, and *t be removed from the above code?
Since you asked ;) there is one tool in C to avoid explicit duplication of code, macros. That said I don't see a way not to repeat at least the name of the type. But in C++ they can't either, so C is at least as good :)
The easiest I see is
#define DESIGNATE_NEW(T, ...) \
memcpy(malloc(sizeof(T)), \
&(T const){ __VA_ARGS__ }, \
sizeof(T))
which would give
Type *t = DESIGNATE_NEW(Type,
.a = 2,
.b = 3,
.c = 5,
);
this has several advantages.
It initializes all members correctly, even on architectures with non
standard representations of the 0 for float types or pointers.
Other than Keith' version it is "coding style" acceptable since it is just an expression that looks like an initialization and anybody should immediately capture visually what the second code snipset is supposed to do.
NB: Observe the const in the macro, this allows several instances of the compound literal to be folded, if the compiler decides this to be relevant. Also there are means to have a variant where the list of designators is optional, see P99 below.
The disadvantage is the memcpy and I would be happier with an assignment. Second there is no check for failure of malloc before using the result, but one could probably come across with some weirdness to have the code exit nicely.
In P99 I go a slightly different way. There we always have an initialization function for a type, something like
inline
Type* Type_init(Type* t, int a, int b, int c) {
if (t) {
*t = (Type const){ .a = a, .b = b, .c = c };
}
return t;
}
which by macro magic can be made to provide default arguments for a, b and c if they are omitted. Then you can simply use something like
Type *t = P99_NEW(Type, 1, 2, 3);
in your application code. This is better, since it avoids dereferrencing the pointer when the call to malloc failed. On the other hand this reintroduces an order to the initializers, so not perfect either.
You can with a variadic macro. I'm not going to claim that this is a good idea, but it works:
#include <stdlib.h>
#include <stdio.h>
#define CREATE(type, ptr, ...) \
type *ptr = malloc(sizeof *ptr); \
if (ptr) *ptr = (type){__VA_ARGS__}
int main(void)
{
typedef struct {
int a, b, c;
} Type;
CREATE(Type, t, .a = 2, .b = 3, .c = 5);
printf("t->a = %d, t->b = %d, t->c = %d\n", t->a, t->b, t->c);
return 0;
}
Note that I wasn't able to use the usual do { ... } while (0) macro definition trick (it would create a new scope, and t wouldn't be visible), so you'd have to be careful about the context in which you use this.
Personally, I think I'm happier with the needless duplication.
No, that's the only way to use designated initializers. Without the (Type){}, the compiler doesn't know how to validate the contents.
When declaring a const table, it is possible to get the size of the table using sizeof. However,
once you stop using the symbol name, it does not work anymore. is there a way to have the following program output the correct size for table A, instead of 0 ?
#include <stdio.h>
struct mystruct {
int a;
short b;
};
const struct mystruct tableA[] ={
{
.a = 1,
.b = 2,
},
{
.a = 2,
.b = 2,
},
{
.a = 3,
.b = 2,
},
};
const struct mystruct tableB[] ={
{
.a = 1,
.b = 2,
},
{
.a = 2,
.b = 2,
},
};
int main(int argc, char * argv[]) {
int tbl_sz;
const struct mystruct * table;
table = tableA;
tbl_sz = sizeof(table)/sizeof(struct mystruct);
printf("size of table A : %d\n", tbl_sz);
table = tableB;
tbl_sz = sizeof(tableB)/sizeof(struct mystruct);
printf("size of table B : %d\n", tbl_sz);
return 0;
}
Output is :
size of table A : 0
size of table B : 2
This is the intended behavior of sizeof. But is there a way for a compiler to know the size of a const table, given a pointer to the table instead of the symbol name ?
you are asking for the sizeof a pointer. That is always the pointer size (ie usually 4 bytes on a 32-bit machine and 8 bytes on a 64-bit machine). In the 2nd attempt you are asking for the sizeof the array and hence you get the result you'd expect.
Is there a way for a compiler to know the size of a const table, given a pointer to the table instead of the symbol name?
No, because sizeof() is evaluated at compile-time (unless it is a VLA, but a VLA is not a constant table), and the compiler cannot, in general, tell which table the pointer is pointing to. Granted, in the scenario shown, it might be possible in some hypothetical variation of the C language, but that would mean varying definitions of what sizeof() returns, which would be a bigger problem than not getting the answer you might like but do not get.
So, as everyone else ably pointed out, when you take the size of a pointer, you get the size of the pointer. Assuming a standard 32-bit machine since the results are consistent with that assumption, your structure is 8 bytes and your pointers are 4 bytes, so the result of the division is zero, as expected.
No - you're asking for the sizeof() a pointer. But since what you're really trying to get is the number of elements in an array, you can use a macro that will return that value but will generally give you an error if you pass a pointer instead of an array:
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
See this SO answer for more details: Is there a standard function in C that would return the length of an array?
For an even safer solution when using C++ instead of C, see this SO answer that uses templates to ensure that trying to get an array count on a pointer will always generate an error: Compile time sizeof_array without using a macro
Short answer is no; if all you have is a pointer, then there's no (standard) way to get the size of the thing being pointed to through that pointer.
Although syntactically correct, your sample is more conventionally written as:
const struct mystruct tableA[] = {
{1, 2},
{2, 2},
{3, 3},
};
Which is less verbose and therefore more readable.