How can I initialize this nested struct in C?
typedef struct _s0 {
int size;
double * elems;
}StructInner ;
typedef struct _s1 {
StructInner a, b, c, d, e;
long f;
char[16] s;
}StructOuter; StructOuter myvar = {/* what ? */ };
To initialize everything to 0 (of the right kind)
StructOuter myvar = {0};
To initialize the members to a specific value
StructOuter myvar = {{0, NULL}, {0, NULL}, {0, NULL},
{0, NULL}, {0, NULL}, 42.0, "foo"};
/* that's {a, b, c, d, e, f, s} */
/* where each of a, b, c, d, e is {size, elems} */
Edit
If you have a C99 compiler, you can also use "designated initializers", as in:
StructOuter myvar = {.c = {1000, NULL}, .f = 42.0, .s = "foo"};
/* c, f, and s initialized to specific values */
/* a, b, d, and e will be initialized to 0 (of the right kind) */
To highlight struct labels in particular:
StructInner a = {
.size: 1,
.elems: { 1.0, 2.0 }, /* optional comma */
};
StructOuter b = {
.a = a, /* struct labels start with a dot */
.b = a,
a, /* they are optional and you can mix-and-match */
a,
.e = { /* nested struct initialization */
.size: 1,
.elems: a.elems
},
.f = 1.0,
.s = "Hello", /* optional comma */
};
double a[] = { 1.0, 2.0 };
double b[] = { 1.0, 2.0, 3.0 };
StructOuter myvar = { { 2, a }, { 3, b }, { 2, a }, { 3, b }, { 2, a }, 1, "a" };
It seems a and b cannot be initialized in-place in plain C
Following also works with GCC, C99. GCC doesnt complain about it. I am not sure if this is standard.
double arr[] = { 1.0, 2.0 }; // should be static or global
StructOuter myvar =
{
.f = 42,
.s = "foo",
.a.size = 2,
.a.elems = &arr,
.b.size = 0, // you can explicitly show that it is zero
// missing members will actually be initialized to zero
};
Related
I have a struct defined with typedef
typedef struct
{
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
}TArea;
Now I want to create some areas that depends on each other.
Something like
AreaDefs.h
extern TArea UI_AREA_1;
extern TArea UI_AREA_2;
AreaDefs.c
TArea UI_AREA_1 = {.x = 0, .y = 0, .width = 5, .height = 10};
TArea UI_AREA_2 = {.x = UI_AREA_1.x, .y = 200, .width = 5, .height = 10};
Why do i get "Error: expression must have a constant value" and whats a better solution? I dotn want to make something like
#define X_VAL 0
TArea UI_AREA_1 = {.x = X_VAL, .y = 0, .width = 5, .height = 10};
TArea UI_AREA_2 = {.x = X_VAL, .y = 200, .width = 5, .height = 10};
because of in my real application I have more than this two areas that depends on each other.
When initializing a variable, the initializer cannot be the value of another variable, even if that variable is defined const. It must be a constant value.
The best you can do is what your second example does: use a #define to initialize certain variables to the same value.
The following code gives me a GCC error for missing braces, but no matter which combinations I try (including just a single value of 0), it does not resolve. Can anyone give some insight?
#define NUM_TABLE_ELEMENTS 1
typedef struct parameter_t
{
struct
{
uint8_t A : 1;
uint8_t B : 1;
uint8_t C : 1;
uint8_t D : 5;
} W;
uint8_t X;
uint8_t Y;
uint8_t Z;
} parameter_t;
parameter_t MyTable[NUM_TABLE_ELEMENTS] =
{
{ { 1, 1, 1, 0 }, 0, 0, 0 },
};
This is why you don't shortcut the question with things you make assumptions on:
The ACTUAL struct I was using was:
typedef struct parameter_t
{
struct
{
uint8_t A : 1;
uint8_t B : 1;
uint8_t C : 1;
uint8_t D : 5;
} W;
uint8_t X;
uint8_t Y[1];
uint8_t Z[1];
} parameter_t;
parameter_t MyTable[NUM_TABLE_ELEMENTS] =
{
{ { 1, 1, 1, 0 }, 0, 0, 0 },
};
So it was missing braces around the other arrays, even though they were single element... Feel free to downvote me for my slacking.
I'm using GCC 4.6.2 (Mingw) and compiling with -Wextra. I'm getting strange warnings whenever I use designated initializers. For the following code
typedef struct
{
int x;
int y;
} struct1;
typedef struct
{
int x;
int y;
} struct2;
typedef struct
{
struct1 s1;
struct2 s2[4];
} bug_struct;
bug_struct bug_struct1 =
{
.s1.x = 1,
.s1.y = 2,
.s2[0].x = 1,
.s2[0].y = 2,
.s2[1].x = 1,
.s2[1].y = 2,
.s2[2].x = 1,
.s2[2].y = 2,
.s2[3].x = 1,
.s2[3].y = 2,
};
I get warnings
bug.c:24:3: warning: missing initializer [-Wmissing-field-initializers]
bug.c:24:3: warning: (near initialization for 'bug_struct1.s1.y') [-Wmissing-field-initializers]
So what exactly is missing? I've initialized every member. Is this warning merely too blunt to work with designated initializers, am I doing something wrong, or is it a compiler bug?
It seems that the warning is, as you say, "too blunt".
This pattern of access, initializing each member struct as a whole, satisfies the compiler:
bug_struct bug_struct1 =
{
.s1 = {.x = 1, .y = 2},
.s2[0] = {.x = 1, .y = 2},
.s2[1] = {.x = 1, .y = 2},
.s2[2] = {.x = 1, .y = 2},
.s2[3] = {.x = 1, .y = 2}
};
I have the part of the following C code that uses data from a file names WMM.COF and uses the data stored in the file to compute the magnetic field of the earth. The program works perfectly except I can't have the program access the external file; I want to have all of the data already stored in the program. I tried using a structure array to replicate the data and then put the array into a string but this causes an error in the program and doesn't produce the correct results. Here is the code of the program that I'm trying to modify.
static void E0000(int IENTRY, int *maxdeg, double alt, double glat, double glon, double time, double *dec, double *dip, double *ti, double *gv)
{
static int maxord,i,icomp,n,m,j,D1,D2,D3,D4;
static double c[13][13],cd[13][13],tc[13][13],dp[13][13],snorm[169],
sp[13],cp[13],fn[13],fm[13],pp[13],k[13][13],pi,dtr,a,b,re,
a2,b2,c2,a4,b4,c4,epoch,gnm,hnm,dgnm,dhnm,flnmj,otime,oalt,
olat,olon,dt,rlon,rlat,srlon,srlat,crlon,crlat,srlat2,
crlat2,q,q1,q2,ct,st,r2,r,d,ca,sa,aor,ar,br,bt,bp,bpp,
par,temp1,temp2,parp,bx,by,bz,bh;
static char model[20], c_str[81], c_new[5];
static double *p = snorm;
char answer;
FILE *wmmdat;
wmmdat = fopen("WMM.COF","r");
/* INITIALIZE CONSTANTS */
maxord = *maxdeg;
sp[0] = 0.0;
cp[0] = *p = pp[0] = 1.0;
dp[0][0] = 0.0;
a = 6378.137;
b = 6356.7523142;
re = 6371.2;
a2 = a*a;
b2 = b*b;
c2 = a2-b2;
a4 = a2*a2;
b4 = b2*b2;
c4 = a4 - b4;
/* READ WORLD MAGNETIC MODEL SPHERICAL HARMONIC COEFFICIENTS */
c[0][0] = 0.0;
cd[0][0] = 0.0;
fgets(c_str, 80, wmmdat);
S3:
if (fgets(c_str, 80, wmmdat) == NULL) goto S4;
/* CHECK FOR LAST LINE IN FILE */
for (i=0; i<4 && (c_str[i] != '\0'); i++)
{
c_new[i] = c_str[i];
c_new[i+1] = '\0';
}
icomp = strcmp("9999", c_new);
if (icomp == 0) goto S4;
/* END OF FILE NOT ENCOUNTERED, GET VALUES */
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
if (n > maxord) goto S4;
if (m > n || m < 0.0)
{
fprintf(stderr, "Corrupt record in model file WMM.COF\n");
exit(1);
}
if (m <= n)
{
c[m][n] = gnm;
cd[m][n] = dgnm;
if (m != 0)
{
c[n][m-1] = hnm;
cd[n][m-1] = dhnm;
}
}
goto S3;
/* CONVERT SCHMIDT NORMALIZED GAUSS COEFFICIENTS TO UNNORMALIZED */
S4:
*snorm = 1.0;
fm[0] = 0.0;
for (n=1; n<=maxord; n++)
{
*(snorm+n) = *(snorm+n-1)*(double)(2*n-1)/(double)n;
j = 2;
for (m=0,D1=1,D2=(n-m+D1)/D1; D2>0; D2--,m+=D1)
{
k[m][n] = (double)(((n-1)*(n-1))-(m*m))/(double)((2*n-1)*(2*n-3));
if (m > 0)
{
flnmj = (double)((n-m+1)*j)/(double)(n+m);
*(snorm+n+m*13) = *(snorm+n+(m-1)*13)*sqrt(flnmj);
j = 1;
c[n][m-1] = *(snorm+n+m*13)*c[n][m-1];
cd[n][m-1] = *(snorm+n+m*13)*cd[n][m-1];
}
c[m][n] = *(snorm+n+m*13)*c[m][n];
cd[m][n] = *(snorm+n+m*13)*cd[m][n];
}
fn[n] = (double)(n+1);
fm[n] = (double)n;
}
k[1][1] = 0.0;
otime = oalt = olat = olon = -1000.0;
fclose(wmmdat);
return;
The code that I came up with to include the data in the program is as follows:
struct wmm
{
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
}book[90]= {{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
{2, 0, -2396.6, 0.0, -12.1, 0.0},
{2, 1, 3026.1, -2707.7, -4.4, -22.5},
{2, 2, 1668.6, -576.1, 1.9, -11.8},
{3, 0, 1340.1, 0.0, 0.4, 0.0},
/* 50+ similar lines of code */
{12, 8, -0.4, 0.1, 0.0, 0.0},
{12, 9, -0.4, 0.3, 0.0, 0.0},
{12, 10, 0.2, -0.9, 0.0, 0.0},
{12, 11, -0.8, -0.2, -0.1, 0.0},
{12, 12, 0.0, 0.9, 0.1, 0.0}};
for (i = 0; i < 90 && offset < buf_size; i++)
{
offset += snprintf(c_str + offset,buf_size - offset, "%d %d %7.1lf %7.1lf %7.1lf %7.1lf \n", book[i].alpha, book[i].beta , book[i].gamma , book[i].delta, book[i].epsilon, book[i].zeta);
}
sscanf(c_str,"%d%d%lf%lf%lf%lf",&n,&m,&gnm,&hnm,&dgnm,&dhnm);
The problem is the snprintf causes the program to freeze and terminate every time it is placed in the program. When the code that I wrote is run on it's own it seems to create c_str properly except when I try to view the variables n,m,gnm,hnm,dgnm, and dhnm only a single value for each is displayed.
I need to continue in an answer due to a lack of space/formatting in a comment.
First of all, you do have 90 entries but you can let the compiler figure out how many entries the book array needs:
struct wmm {
int alpha;
int beta;
float gamma;
float delta;
float epsilon;
float zeta;
} book[] = {
{1, 0, -29496.6, 0.0, 11.6, 0.0},
{1, 1, -1586.3, 4944.4, 16.5, -25.9},
/* ... */
{12, 12, 0.0, 0.9, 0.1, 0.0}
};
And, more importantly, you don't need to put them in a string and pull them back out when you already have them on hand:
for(i = 0; i < sizeof(book)/sizeof(book[0]); ++i) {
n = book[i].alpha;
m = book[i].beta;
gnm = book[i].gamma;
hnm = book[i].delta;
dgnm = book[i].epsilon;
dhnm = book[i].zeta;
/* Do whatever you need to do with the above variables. */
}
This will neatly side step whatever buffer overflow you were causing with your snprintf.
Your c_str is only a char[81] and you're going through your loop 90 times and incrementing your offset into c_str each time; so, you'll run off the end of c_str before long and then you'll tell snprintf to scribble all over unallocated memory. Hence your segfault.
Is there any way to do this in a condensed form?
GLfloat coordinates[8];
...
coordinates[0] = 1.0f;
coordinates[1] = 0.0f;
coordinates[2] = 1.0f;
coordinates[3] = 1.0f;
coordinates[4] = 0.0f;
coordinates[5] = 1.0f;
coordinates[6] = 0.0f;
coordinates[7] = 0.0f;
return coordinates;
Something like coordinates = {1.0f, ...};?
If you really to assign values (as opposed to initialize), you can do it like this:
GLfloat coordinates[8];
static const GLfloat coordinates_defaults[8] = {1.0f, 0.0f, 1.0f ....};
...
memcpy(coordinates, coordinates_defaults, sizeof(coordinates_defaults));
return coordinates;
Although in your case, just plain initialization will do, there's a trick to wrap the array into a struct (which can be initialized after declaration).
For example:
struct foo {
GLfloat arr[10];
};
...
struct foo foo;
foo = (struct foo) { .arr = {1.0, ... } };
The old-school way:
GLfloat coordinates[8];
...
GLfloat *p = coordinates;
*p++ = 1.0f; *p++ = 0.0f; *p++ = 1.0f; *p++ = 1.0f;
*p++ = 0.0f; *p++ = 1.0f; *p++ = 0.0f; *p++ = 0.0f;
return coordinates;
You can use:
GLfloat coordinates[8] = {1.0f, ..., 0.0f};
but this is a compile-time initialisation - you can't use that method in the current standard to re-initialise (although I think there are ways to do it in the upcoming standard, which may not immediately help you).
The other two ways that spring to mind are to blat the contents if they're fixed:
GLfloat base_coordinates[8] = {1.0f, ..., 0.0f};
GLfloat coordinates[8];
:
memcpy (coordinates, base_coordinates, sizeof (coordinates));
or provide a function that looks like your initialisation code anyway:
void setCoords (float *p0, float p1, ..., float p8) {
p0[0] = p1; p0[1] = p2; p0[2] = p3; p0[3] = p4;
p0[4] = p5; p0[5] = p6; p0[6] = p7; p0[7] = p8;
}
:
setCoords (coordinates, 1.0f, ..., 0.0f);
keeping in mind those ellipses (...) are placeholders, not things to literally insert in the code.
I went with an array initialization method:
#include <stdarg.h>
void int_array_init(int *a, const int ct, ...) {
va_list args;
va_start(args, ct);
for(int i = 0; i < ct; ++i) {
a[i] = va_arg(args, int);
}
va_end(args);
}
called like,
const int node_ct = 8;
int expected[node_ct];
int_array_init(expected, node_ct, 1, 3, 4, 2, 5, 6, 7, 8);
The C99 array initialization, like this:
const int node_ct = 8;
const int expected[node_ct] = { 1, 3, 4, 2, 5, 6, 7, 8 };
And in the configure.ac:
AC_PROG_CC_C99
had the compiler on my dev box perfectly happy. The compiler on the server complained with:
error: variable-sized object may not be initialized
const int expected[node_ct] = { 1, 3, 4, 2, 5, 6, 7, 8 };
and
warning: excess elements in array initializer
const int expected[node_ct] = { 1, 3, 4, 2, 5, 6, 7, 8 };
for each element
It doesn't complain at all about, for example:
int expected[] = { 1, 2, 3, 4, 5 };
I like the check on size, and that the varargs support is acting more robustly than the support for the array initializer.
Find PR with sample code at https://github.com/wbreeze/davenport/pull/15/files
Regarding https://stackoverflow.com/a/3535455/608359 from #paxdiablo, I liked it; but, felt insecure about having the number of times the initializaion pointer advances synchronized with the number of elements allocated to the array. Worst case, the initializing pointer moves beyond the allocated length. As such, the diff in the PR contains,
int expected[node_ct];
- int *p = expected;
- *p++ = 1; *p++ = 2; *p++ = 3; *p++ = 4;
+ int_array_init(expected, node_ct, 1, 2, 3, 4);
The int_array_init method will safely assign junk if the number of
arguments is fewer than the node_ct. The junk assignment ought to be easier
to catch and debug.
Exactly, you nearly got it:
GLfloat coordinates[8] = {1.0f, ..., 0.0f};
If you are doing these same assignments a lot in your program and want a shortcut, the most straightforward solution might be to just add a function
static inline void set_coordinates(
GLfloat coordinates[static 8],
GLfloat c0, GLfloat c1, GLfloat c2, GLfloat c3,
GLfloat c4, GLfloat c5, GLfloat c6, GLfloat c7)
{
coordinates[0] = c0;
coordinates[1] = c1;
coordinates[2] = c2;
coordinates[3] = c3;
coordinates[4] = c4;
coordinates[5] = c5;
coordinates[6] = c6;
coordinates[7] = c7;
}
and then simply call
GLfloat coordinates[8];
// ...
set_coordinates(coordinates, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f);
typedef struct{
char array[4];
}my_array;
my_array array = { .array = {1,1,1,1} }; // initialisation
void assign(my_array a)
{
array.array[0] = a.array[0];
array.array[1] = a.array[1];
array.array[2] = a.array[2];
array.array[3] = a.array[3];
}
char num = 5;
char ber = 6;
int main(void)
{
printf("%d\n", array.array[0]);
// ...
// this works even after initialisation
assign((my_array){ .array = {num,ber,num,ber} });
printf("%d\n", array.array[0]);
// ....
return 0;
}