Problem with enums, const char* and structs

E

Esash

Hello all,

I am trying to compile this code with gcc. I wrote this sample code to test this pattern so that I can incorporate the same pattern in my project. But I am not able to compile this code. Here is the code.

#include <stdio.h>

#define EK "1"
#define DO "2"

const char *num[] = { EK, DO };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

int main()
{
int i;
for ( i = 0; i < 2; i++ ) {
printf("%s\t%d\n", dat->ptr, dat->len);
}
return 0;
}

I get the following errors :

index.c:19:2: warning: braces around scalar initializer [enabled by default]
index.c:19:2: warning: (near initialization for 'dat[0]') [enabled by default]
index.c:19:2: error: initializer element is not constant
index.c:19:2: error: (near initialization for 'dat[0]')
index.c:19:2: warning: excess elements in scalar initializer [enabled by default
]
index.c:19:2: warning: (near initialization for 'dat[0]') [enabled by default]
index.c:20:2: warning: braces around scalar initializer [enabled by default]
index.c:20:2: warning: (near initialization for 'dat[1]') [enabled by default]
index.c:20:2: error: initializer element is not constant
index.c:20:2: error: (near initialization for 'dat[1]')
index.c:20:2: warning: excess elements in scalar initializer [enabled by default
]
index.c:20:2: warning: (near initialization for 'dat[1]') [enabled by default]

Please help.

Thanks,
Esash
 
B

Ben Bacarisse

Esash said:
I am trying to compile this code with gcc. I wrote this sample code to
test this pattern so that I can incorporate the same pattern in my
project. But I am not able to compile this code. Here is the code.

#include <stdio.h>

#define EK "1"
#define DO "2"

const char *num[] = { EK, DO };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

One problem (probably not related to what you are trying to do) is that
you've got a spurious * there. If you really do want an array of
pointer to dat_t you will another level of initialisation.

Another detail is that sizeof num[ONE] is the size of a pointer and
that's probably not what you really want.

But the "main" problem is that num[ONE] does not meet the definition of
an allowable address constant. Basically, you can't access an array
element to get an address constant. The rules are a little fiddly but
for reference I've appended the paragraph below.

It's not clear what the best course of action is because I am not sure
what the essential elements are. You've not made any of the data static
so I have to assume that everything is being accessed from everywhere
(though I know that's probably not what you intended).

You make be happy with:

const dat_t dat[] = {
{ EK, sizeof EK },
{ DO, sizeof DO },
};

From the recent C11 draft (it did not change from C99):

An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type. The
array-subscript [] and member-access . and -> operators, the address
& and indirection * unary operators, and pointer casts may be used in
the creation of an address constant, but the value of an object shall
not be accessed by use of these operators.

<snip>
 
J

Joe Pfeiffer

Esash said:
Hello all,

I am trying to compile this code with gcc. I wrote this sample code to test this pattern so that I can incorporate the same pattern in my project. But I am not able to compile this code. Here is the code.

#include <stdio.h>

#define EK "1"
#define DO "2"

const char *num[] = { EK, DO };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

int main()
{
int i;
for ( i = 0; i < 2; i++ ) {
printf("%s\t%d\n", dat->ptr, dat->len);
}
return 0;
}

I get the following errors :

index.c:19:2: warning: braces around scalar initializer [enabled by default]
index.c:19:2: warning: (near initialization for 'dat[0]') [enabled by default]
index.c:19:2: error: initializer element is not constant
index.c:19:2: error: (near initialization for 'dat[0]')
index.c:19:2: warning: excess elements in scalar initializer [enabled by default
]
index.c:19:2: warning: (near initialization for 'dat[0]') [enabled by default]
index.c:20:2: warning: braces around scalar initializer [enabled by default]
index.c:20:2: warning: (near initialization for 'dat[1]') [enabled by default]
index.c:20:2: error: initializer element is not constant
index.c:20:2: error: (near initialization for 'dat[1]')
index.c:20:2: warning: excess elements in scalar initializer [enabled by default
]
index.c:20:2: warning: (near initialization for 'dat[1]') [enabled by default]


You've declared dat[] as an array of pointers to dat_t, but are
initializing the array as if it were an array of dat_t. Changing the
declaration and initialization to

const dat_t dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

will clear up that problem, but will expose other errors.

Something that I notice isn't an error per se, but is likely to cause
significant confusion, is in your data_t_ enumeration -- ONE will have
the value 0, and TWO will have the value 1. Also, in a program longer
than about 10 lines, having various types, enums, and variables with
names data_t_, data_t, dat_t, and dat will cause you to pull your hair
out trying to figure out your own code.
 
E

Esash

I am trying to compile this code with gcc. I wrote this sample code to
test this pattern so that I can incorporate the same pattern in my
project. But I am not able to compile this code. Here is the code.

#include <stdio.h>

#define EK "1"
#define DO "2"
const char *num[] = { EK, DO };
typedef enum data_t_ {

} data_t;
typedef struct dat_t {
char *ptr;
} dat_t;
const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },



One problem (probably not related to what you are trying to do) is that

you've got a spurious * there. If you really do want an array of

pointer to dat_t you will another level of initialisation.



Another detail is that sizeof num[ONE] is the size of a pointer and

that's probably not what you really want.



But the "main" problem is that num[ONE] does not meet the definition of

an allowable address constant. Basically, you can't access an array

element to get an address constant. The rules are a little fiddly but

for reference I've appended the paragraph below.



It's not clear what the best course of action is because I am not sure

what the essential elements are. You've not made any of the data static

so I have to assume that everything is being accessed from everywhere

(though I know that's probably not what you intended).



You make be happy with:



const dat_t dat[] = {

{ EK, sizeof EK },

{ DO, sizeof DO },

};



From the recent C11 draft (it did not change from C99):



An address constant is a null pointer, a pointer to an lvalue

designating an object of static storage duration, or a pointer to a

function designator; it shall be created explicitly using the unary &

operator or an integer constant cast to pointer type, or implicitly by

the use of an expression of array or function type. The

array-subscript [] and member-access . and -> operators, the address

& and indirection * unary operators, and pointer casts may be used in

the creation of an address constant, but the value of an object shall

not be accessed by use of these operators.



<snip>

Thanks a lot Ben. I got the point why I got those errors. But actually whatI need is the initialization of char* member here :

const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

As you said, this had to be :

const dat_t dat[] = {
{ num[ONE], strlen(num[ONE]) },
{ num[TWO], strlen(num[TWO]) },
};

But the problem lies in num[ONE] and num[TWO]. So, is there no way to initialize a structure member with a variable even if it is a const ? I understand that the compiler cannot be sure of such initializations since they might be subject to changes in the future. But any workaround ? Here is the modified code..

const char *num[] = { "this" , "that" };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

const dat_t dat[2] = {
{ num[ONE], strlen(num[ONE])},
{ num[ONE], strlen(num[TWO])},
};

int main()
{
int i;
for (i=0; i<2; i++) {
printf("%s\t%d\n", dat.ptr, dat.len);
}
return 0;
}
 
E

Esash

I am trying to compile this code with gcc. I wrote this sample code to
test this pattern so that I can incorporate the same pattern in my
project. But I am not able to compile this code. Here is the code.
#include <stdio.h>
#define EK "1"
#define DO "2"
const char *num[] = { EK, DO };
typedef enum data_t_ {
} data_t;
typedef struct dat_t {
char *ptr;
const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
One problem (probably not related to what you are trying to do) is that
you've got a spurious * there. If you really do want an array of
pointer to dat_t you will another level of initialisation.
Another detail is that sizeof num[ONE] is the size of a pointer and
that's probably not what you really want.
But the "main" problem is that num[ONE] does not meet the definition of
an allowable address constant. Basically, you can't access an array
element to get an address constant. The rules are a little fiddly but
for reference I've appended the paragraph below.
It's not clear what the best course of action is because I am not sure
what the essential elements are. You've not made any of the data static
so I have to assume that everything is being accessed from everywhere
(though I know that's probably not what you intended).
You make be happy with:
const dat_t dat[] = {
{ EK, sizeof EK },
{ DO, sizeof DO },

From the recent C11 draft (it did not change from C99):
An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type. The
array-subscript [] and member-access . and -> operators, the address
& and indirection * unary operators, and pointer casts may be used in
the creation of an address constant, but the value of an object shall
not be accessed by use of these operators.

--
Ben.



Thanks a lot Ben. I got the point why I got those errors. But actually what I need is the initialization of char* member here :



const dat_t *dat[] = {

{ num[ONE], sizeof(num[ONE]) },

{ num[TWO], sizeof(num[TWO]) },

};



As you said, this had to be :



const dat_t dat[] = {

{ num[ONE], strlen(num[ONE]) },

{ num[TWO], strlen(num[TWO]) },

};



But the problem lies in num[ONE] and num[TWO]. So, is there no way to initialize a structure member with a variable even if it is a const ? I understand that the compiler cannot be sure of such initializations since they might be subject to changes in the future. But any workaround ? Here is the modified code..



const char *num[] = { "this" , "that" };



typedef enum data_t_ {

ONE,

TWO,

} data_t;



typedef struct dat_t {

char *ptr;

int len;

} dat_t;



const dat_t dat[2] = {

{ num[ONE], strlen(num[ONE])},

{ num[ONE], strlen(num[TWO])},

};



int main()

{

int i;

for (i=0; i<2; i++) {

printf("%s\t%d\n", dat.ptr, dat.len);

}

return 0;

}


And the problems posed here with this revised code are the same as Joe's quoted in his reply.

index.c:54:2: error: initializer element is not constant
index.c:54:2: error: (near initialization for 'dat[0].ptr')
index.c:55:2: error: initializer element is not constant
index.c:55:2: error: (near initialization for 'dat[1].ptr')

So is there any workaround ? I got the point that replacing #defined valueswould solve the problem but in my case I cannot give the #defined values since the actual value in place of num[ONE] and num[TWO] are functions. These are big functions which cannot be written as a macro or inline functions.So they cannot be #defined.

Please advise.

Thanks,
Esash
 
K

Keith Thompson

Esash said:
I am trying to compile this code with gcc. I wrote this sample code to
test this pattern so that I can incorporate the same pattern in my
project. But I am not able to compile this code. Here is the code.

#include <stdio.h>

#define EK "1"
#define DO "2"

const char *num[] = { EK, DO };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

None of the following is relevant to the problem you're asking about.

For an enum, struct, or union type, there's really no benefit in using
distinct identifiers for the tag and the typedef. You're using
different identifiers "data_t_" and "dat_t" for your enum type, and the
same identifier, "dat_t" for your struct type. Since tags are in a
separate namespace, appending "_" to get a unique name is neither
necessary nor helpful.

If you want to have a single identifier as the name of your type, you
can just omit the tag:

typedef enum { ONE, TWO } data_t;

Or you can omit the typedef:

enum data_t { ONE, TWO };

and refer to the type as "enum data_t". The same applies to struct
and union types, except that things get a little more complicated
for self-referential types (e.g., if a struct type contains a
pointer to itself).

And even for this example, using names like "dat_t" and "data_t" is
unnecessarily confusing.

[...]
int main()

I suggest writing this as "int main(void)", since it's more explicit.

Another thing: Google Groups has completely messed up its Usenet
interface. In your followups in this thread, quoted text is
double-spaced, or even quadruple- or octuple-spaced. I don't know
what Google did to cause this problem, or why, but you can work
around it by copy-and-pasting your article into your favorite text
editor, deleting the extra lines, and then copy-and-pasting it back
into your browser before posting. It's also helpful to wrap your
lines to 72 columns or so. Or you could consider using a real Usenet
server (I use news.eternal-september.org) and client (I use Gnus,
which runs inside Emacs; there are a number of other free clients).
 
B

Barry Schwarz

Thanks a lot Ben. I got the point why I got those errors. But actually what I need is the initialization of char* member here :

const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

As you said, this had to be :

const dat_t dat[] = {
{ num[ONE], strlen(num[ONE]) },
{ num[TWO], strlen(num[TWO]) },
};

But the problem lies in num[ONE] and num[TWO]. So, is there no way to initialize a structure member with a variable even if it is a const ? I understand that the compiler cannot be sure of such initializations since they might be subject to changes in the future. But any workaround ? Here is the modified code..

const char *num[] = { "this" , "that" };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

const dat_t dat[2] = {
{ num[ONE], strlen(num[ONE])},
{ num[ONE], strlen(num[TWO])},
};

int main()
{
int i;
for (i=0; i<2; i++) {
printf("%s\t%d\n", dat.ptr, dat.len);
}
return 0;
}


First, you need to realize that const and constant are two different
things. Even though the value of num[0] is const, it is not a compile
time constant. This is the same reason
const int x = 2;
static int y[x];
cannot be used.

Other than the fact that you are attempting to use num in the
initialization of dat, is there any reason it needs to be at file
scope or for that matter even exist? Could you use
const dat_t dat[2] = {
{ "this", sizeof "this" -1},
{ "that", sizeof "that" -1},
};

If you really need num at file scope with external linkage, consider
moving dat inside main. Once it no longer has static duration, you
have a lot more flexibility with the initialization. If you need dat
to be "global", add a global pointer to it that you assign in main.
Other source files will still be able to access dat and as long as you
don't call main recursively, it is effectively static. Something like

const char *num[] = { "this" , "that" };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;
int len;
} dat_t;

dat_t *dat_ptr; /* <================ */

int main()
{
const dat_t dat[2] = {
{ num[ONE], strlen(num[ONE])},
{ num[ONE], strlen(num[TWO])},
};

int i;
dat_ptr = dat; /* <================= */
for (i=0; i<2; i++) {
printf("%s\t%d\n", dat.ptr, dat.len);
}
return 0;
}

May I suggest that ONE being 0 and TWO being 1 is not the most
readable choice you could make.
 
J

James Kuyper

You make be happy with:



const dat_t dat[] = {

{ EK, sizeof EK },

{ DO, sizeof DO },

};



From the recent C11 draft (it did not change from C99):



An address constant is a null pointer, a pointer to an lvalue
designating an object of static storage duration, or a pointer to a
function designator; it shall be created explicitly using the unary &
operator or an integer constant cast to pointer type, or implicitly by
the use of an expression of array or function type. The
array-subscript [] and member-access . and -> operators, the address
& and indirection * unary operators, and pointer casts may be used in
the creation of an address constant, but the value of an object shall
not be accessed by use of these operators.



<snip>

Thanks a lot Ben. I got the point why I got those errors. But actually
what I need is the initialization of char* member here :

Could you explain in more detail why you can't use Ben's suggestion?
The char* member does get initialized with his suggestion.
const dat_t *dat[] = {
{ num[ONE], sizeof(num[ONE]) },
{ num[TWO], sizeof(num[TWO]) },
};

As you said, this had to be :

const dat_t dat[] = {
{ num[ONE], strlen(num[ONE]) },
{ num[TWO], strlen(num[TWO]) },
};
But the problem lies in num[ONE] and num[TWO]. So, is there no way
to initialize a structure member with a variable even if it is a const ?

Correct, at least for objects with static or thread storage duration.
Note that strlen() is also a problem here, for the same reason - it's
not a constant expression.
I understand that the compiler cannot be sure of such initializations > since they might be subject to changes in the future. But any workaround?

The simplest solution is to give this array automatic storage duration,
by defining it inside a function. The restriction that the initializers
must be constant expressions applies only for objects with static or
thread storage duration.

If you need to refer to that array from multiple different functions,
pass around a pointer to the first element of the array, rather than
referring to the array directly. In general, I don't recommend using
global variables. However, if you must use one, if you replaced the
global array with a global pointer to the first element of an
automatically allocated array array, most uses of the pointer would use
the same exact syntax as for an array, so very little code would need to
be re-written.

If you need C90 compatibility, one work-around would be to remove the
'const' from the declaration of dat, explicitly set the length of the
array, and set the values explicitly, rather than using initialization
syntax:

dat[0].ptr = num[ONE];
dat[0].len = strlen(dat[0].ptr);
// etc.

You can still pass the array around using a pointer that retains the
'const', that you had to remove from 'dat' itself.
Here is the modified code..

const char *num[] = { "this" , "that" };

typedef enum data_t_ {
ONE,
TWO,
} data_t;

typedef struct dat_t {
char *ptr;

In most contexts, including this one, a string literal causes the
creation of an anonymous char array, and has a value which is a pointer
to the first element of that array. Code which attempts to modify any
element of that array has undefined behavior. That's why you were right
to declare 'num' using 'const'. However, since ptr will end up
containing copies of those same pointers, it should also be declared as

const char *ptr;

so that any code which attempts to modify those arrays through that
pointer will force the compiler to issue a diagnostic message.
 
B

Ben Bacarisse

You might want to consider using another new reader; it's Google's
awful interface that's adding all the extra blank lines. If that's not
possible, please consider snipping some of your replies. You don't need
all of this text to make the point below.

And the problems posed here with this revised code are the same as Joe's quoted in his reply.

index.c:54:2: error: initializer element is not constant
index.c:54:2: error: (near initialization for 'dat[0].ptr')
index.c:55:2: error: initializer element is not constant
index.c:55:2: error: (near initialization for 'dat[1].ptr')

So is there any workaround ?

Not without more information. What can and can not be changed? How
complex is the real situation?
I got the point that replacing #defined
values would solve the problem but in my case I cannot give the
#defined values since the actual value in place of num[ONE] and
num[TWO] are functions. These are big functions which cannot be
written as a macro or inline functions. So they cannot be #defined.

I don't understand because you are being a little loose with your terms.
Do you really mean the values are function, or do you mean function
calls? When you say "in place of" surely you don't mean you posted code
that uses array elements when the "real" situation does not? maybe you
mean the values *in* num[ONE] and num[TWO] are the result of function
calls? If so, the situation is even worse than the example you
posted -- in C no functions can be called during the initialisation of a
file-scope object. But, as I said, maybe that's not what you mean.

But fear not: we all understand C so post the real code, not an
approximation to it, and you will get more realistic answers. In
addition, you need to say what can be sacrificed. If your code does not
work, something has to change so what can be changed and what can't be?
 
J

James Kuyper

On Wednesday, October 31, 2012 10:57:57 PM UTC+5:30, Esash wrote: ....
And the problems posed here with this revised code are the same as
Joe's quoted in his reply.

index.c:54:2: error: initializer element is not constant
index.c:54:2: error: (near initialization for 'dat[0].ptr')
index.c:55:2: error: initializer element is not constant
index.c:55:2: error: (near initialization for 'dat[1].ptr')

So is there any workaround ? I got the point that replacing #defined
values would solve the problem but in my case I cannot give the
#defined values since the actual value in place of num[ONE] and
num[TWO] are functions. These are big functions which cannot be
written as a macro or inline functions. So they cannot be #defined.

If the expressions you're actually using where you wrote num[ONE] and
num[TWO] are actually the result of function calls, you certainly cannot
use them to initialize a statically allocated array. I've already told
you how you can deal with this, but I'll fill in the details below.

I'll assume that the pointers returned by that function point to
unmodifiable memory, just like num[ONE] and num[TWO]; if that's not the
case, the first and third uses of 'const' below can be removed.

I'll assume that you know the maximum number of elements in your array
(that's implied by your use of enumerations constants in the
initialization), and that you have a justified reason for needing global
access to it (which seems less likely to be true). I recommend against
using type names ending with _t, at least if your code might ever need
to be ported to a POSIX-compliant system. POSIX has reserved all such
names (other than those already reserved by C) for it's own use.

data.h:
enum {
ZERO,
ONE,
NUM_DATA
};

typedef struct {
const char *ptr;
int len;
} mydata;

extern const mydata *data_list;

main.c:
#include <string.h>
#include "data.h"
extern const char*get_string(int);
const mydata *data_list;

int main(void)
{
const mydata data_array[NUM_DATA] = {
{get_string(ZERO), strlen(get_string(ZERO)) },
{get_string(ONE), strlen(get_string(ONE)) },
};
data_list = data_array;
// The rest of your program
return 0;
}

Any module that needs access to the array should #include "data.h", and
access it through data_list.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top