Initialization of a const matrix implemented as pointer-to-pointer

  • Thread starter Andrea Taverna (Tavs)
  • Start date
A

Andrea Taverna (Tavs)

Subject: Initialization of a const matrix implemented as pointer-to-pointer

Hello everyone.

I've got the following matrix definition in a source file

static const char **a;

I need it to be initialized as an array of strings, something like

static const char **a = {"Alpha", "Beta", Charlie"};

I know the number of strings and their maximum length, but I can't use a
stack-allocated matrix for compatibility reasons with another piece of
code, which simply defines a

char** strings

to which the matrix has to be assigned to.

I thought I could write a static const declaration for each string

static const str1[MAX_LENGTH] ="Alpha"

and then declare the matrix as

static const char* a[NUM_OF_STRINGS] = {str1,str2,...}

would it be correct?

thanks for your attention

AT
 
B

Barry Schwarz

Subject: Initialization of a const matrix implemented as pointer-to-pointer

Hello everyone.

I've got the following matrix definition in a source file

static const char **a;

In this case, sizeof a will probably be 4 or 8.
I need it to be initialized as an array of strings, something like

static const char **a = {"Alpha", "Beta", Charlie"};

I know the number of strings and their maximum length, but I can't use a
stack-allocated matrix for compatibility reasons with another piece of
code, which simply defines a

char** strings
to which the matrix has to be assigned to.

There should be no problem "assigning the array" to this pointer.
I thought I could write a static const declaration for each string

static const str1[MAX_LENGTH] ="Alpha"

and then declare the matrix as

static const char* a[NUM_OF_STRINGS] = {str1,str2,...}

If NUM_OF_STRINGS is 3 (to be consistent with your example), sizeof a
will probably be 12 or 24.
would it be correct?

It depends on how you plan to use a.

In the first example, is a true pointer. It can appear on the left
side of the assignment operator. In the second, a is an array which
cannot appear there. However, in both cases, a can appear on the
right of the assignment operator.

If all you want is a variable name that can be subscripted to evaluate
to different strings, then the second will work. If you delete the
"static", it will become a "stack allocated matrix" (technically an
automatic array of char*). When the array name "a" appears in an
expression context other than as the operand of sizeof and &, it will
be converted to the address of a[0] with type "pointer to type of
a[0]". Since a is an array of char*, a[0] is the first char* in the
array. The type "pointer to type of a[0]" is simply "pointer to
char*" also known as char**. Therefore you can assign the array name
"a" to the variable "strings" in the other code.

On the other hand, if you need a true pointer that can be reassigned
to point to different sets of strings, then something of the form:

const astr1[] = "Alpha";
...
const astrx[] = "Omega";
const bstr1[] = "Alef";
...
const bstrx[] = "Tav";
const char* A[] = {astr1,...,astrx};
const char* B[] = {bstr1,...,bstrx};
const char **a = A;
...
a = B;
and anywhere in your code you can say
strings = a;
 
A

Andrea Taverna (Tavs)

Barry Schwarz ha scritto:
In this case, sizeof a will probably be 4 or 8.



There should be no problem "assigning the array" to this pointer.

Actually there is. GCC warns that they're not compatible
On the other hand, if you need a true pointer that can be reassigned
to point to different sets of strings, then something of the form:

const astr1[] = "Alpha";
...
const astrx[] = "Omega";
const bstr1[] = "Alef";
...
const bstrx[] = "Tav";
const char* A[] = {astr1,...,astrx};
const char* B[] = {bstr1,...,bstrx};
const char **a = A;
...
a = B;
and anywhere in your code you can say
strings = a;

that's precisely what I'm going to do.

Thanks

AT
 
A

Andrea Taverna (Tavs)

Ok, I've tried both ways, Barry's and Pete's. GCC still wars about
compatibility, but it behaves correctly at run time, so the problem is set.

Thank you :D

AT
 
J

jameskuyper

Andrea said:
Barry Schwarz ha scritto:


Actually there is. GCC warns that they're not compatible

That's because the second declaration should be

const char** strings;

If you don't have the power to change the declaration of 'strings',
check the documentation of the other piece of code very carefully. Is
that code going to attempt to write to those strings? If not, then it
was a mistake on the designer's part to declare "strings" without
using const. However, it's a mistake you can deal with by simply use a
cast:

strings = (char**)a;

However, it if will be writing to the strings, then this is not an
appropriate approach; the memory allocated for string literals is not
necessarily writable. In that case, follow the example given below,
but remove the word "const" wherever it appears:
On the other hand, if you need a true pointer that can be reassigned
to point to different sets of strings, then something of the form:

const astr1[] = "Alpha";
...
const astrx[] = "Omega";
const bstr1[] = "Alef";
...
const bstrx[] = "Tav";
const char* A[] = {astr1,...,astrx};
const char* B[] = {bstr1,...,bstrx};
const char **a = A;
...
a = B;
and anywhere in your code you can say
strings = a;
 
K

Keith Thompson

Andrea Taverna (Tavs) said:
Ok, I've tried both ways, Barry's and Pete's. GCC still wars about
compatibility, but it behaves correctly at run time, so the problem is
set.

Don't be satisfied with code that produces warnings. Understand why
the warning is being produced. Most of the time, you should fix the
code so the warning goes away (note: adding a cast is rarely the right
fix). In some cases, it makes sense to leave the code as it is and
accept the warning, but most warnings indicate a real problem in the
code, one that's likely to bite you later on.
 
B

Barry Schwarz

Barry Schwarz ha scritto:


Actually there is. GCC warns that they're not compatible

I would be nice if you told us what the warning said. It is probably
a complaint about loss of "const" so remove that qualifier from your
code.
On the other hand, if you need a true pointer that can be reassigned
to point to different sets of strings, then something of the form:

const astr1[] = "Alpha";
...
const astrx[] = "Omega";
const bstr1[] = "Alef";
...
const bstrx[] = "Tav";
const char* A[] = {astr1,...,astrx};
const char* B[] = {bstr1,...,bstrx};
const char **a = A;
...
a = B;
and anywhere in your code you can say
strings = a;

that's precisely what I'm going to do.

You will still have the const problem.
 
A

Andrea Taverna (Tavs)

I would be nice if you told us what the warning said. It is probably
>a complaint about loss of "const" so remove that qualifier from your
>code.
Sure.
I don't have the code at hand. More or less it says:
"Warning: assignment between incompatible types"

We fixed it by adjusting the declaration of the 'strings' field.
Now they're both declared as const char**, and the problem would be solved.
However I had to change the C standard from gnu89 (gcc's default) to
c99, and with the following piece of code

const char * a [] =
{
"alpha", "beta","charlie"
};

const struct my_structure my_s = (const struct my_structure)
{
.x = 10;
.strings = a; // <- Error
};

and the latter standard it complains with the following message:
"Error: initializer element is not constant"
With gnu89 it compiles fine.
 
B

Barry Schwarz

I would be nice if you told us what the warning said. It is probably
a complaint about loss of "const" so remove that qualifier from your
code.
Sure.
I don't have the code at hand. More or less it says:
"Warning: assignment between incompatible types"

We fixed it by adjusting the declaration of the 'strings' field.
Now they're both declared as const char**, and the problem would be solved.
However I had to change the C standard from gnu89 (gcc's default) to
c99, and with the following piece of code

const char * a [] =
{
"alpha", "beta","charlie"
};

const struct my_structure my_s = (const struct my_structure)
{
.x = 10;
.strings = a; // <- Error
};

and the latter standard it complains with the following message:
"Error: initializer element is not constant"
With gnu89 it compiles fine.

If this code is outside of a function then my_s has static duration.
Initialization of a static object must be a compile time constant
since it occurs before any code is executed. The same restriction
applies to compound literals outside of a function body (6.5.2.5-3).

I can't help with gnu89 except to suggest that you specify the
appropriate options to prohibit extensions.
 
A

Andrea Taverna (Tavs)

Barry said:
If this code is outside of a function then my_s has static duration.
Initialization of a static object must be a compile time constant
since it occurs before any code is executed. The same restriction
applies to compound literals outside of a function body (6.5.2.5-3).
Uh, yes. I forgot to specify that the initialization takes place in the
global scope (in a source file).

Ehm, could I ask you how I should change the code to have the error
fixed? I couldn't figure it out.


thanks

Andrea
 
B

Barry Schwarz

Uh, yes. I forgot to specify that the initialization takes place in the
global scope (in a source file).

Ehm, could I ask you how I should change the code to have the error
fixed? I couldn't figure it out.

To answer your question:

If you can limit yourself to C99 systems, you might be able to
use a compound literal.

Otherwise, I think you need to cheat. Create an
initialization function which you will call early in main. In this
function, access the const structures using non-const pointers and
initialize everything. Mark the structures volatile so the compiler
doesn't optimize away any accesses thinking it knows what values are
in the structures.

The real question is why are the structures at file scope. If the
answer is that they are used so frequently that you cannot afford to
pass them (or their addresses) to all the different functions that
need them then rethink how you access them. If you define the
structures in main, you can create global pointers to const, assign
values to the pointers in main, and have all the other functions
access the structures through the pointers. Since the structures are
no longer static, you have more flexibility in how you initialize
them.
 
A

Andrea Taverna (Tavs)

Barry Schwarz ha scritto:
To answer your question:

If you can limit yourself to C99 systems, you might be able to
use a compound literal.

If you mean this:
my_s {
.x = 10,
.strings = {"alpha","beta","gamma"}
}

it doesn't work.
it shows the same error
"Error: initializer is not constant"
And warns that the second assignment is between incompatible pointer
types, I'd say that's because the braced expression in the example is
expected to be a char [][3]...
The real question is why are the structures at file scope.
The structure I have to initialize is needed as a global constant. Much
like macro ones.
What I want from the compiler is to have an array of strings, organized
as an array of pointers to each string, stored in the ".rodata" segment
of the program, together with the 'int x' field of the structure
my_structure.
I can't define the strings field as a matrix of chars because I may need
to use the same structure to store a different number of string each time.
I didn't put the initialization in main, and I'd like to keep it so, in
order to avoid any dependency between this initialization code, which is
used in a low-level module, and the rest of the code, which is higher-level.

thanks for the attention

Andrea
 
B

Ben Bacarisse

Andrea Taverna (Tavs) said:
Barry Schwarz ha scritto:


If you mean this:
my_s {

Did you have "my_s = {"? What you have posted look like a syntax error.
.x = 10,
.strings = {"alpha","beta","gamma"}
}

No, a compound literal would look like this:

.strings = (const char *[]){"alpha","beta","gamma"}

(again, this is C99).
 
A

Andrea Taverna (Tavs)

Ben Bacarisse ha scritto:
Did you have "my_s = {"? What you have posted look like a syntax error.
Oh, yes, I meant

const struct my_structure my_s = (const struct my_structure)
{
.x = 10;
.strings = {"alpha","beta","gamma"}
};

No, a compound literal would look like this:

.strings = (const char *[]){"alpha","beta","gamma"}

Gotcha. now it accept the assignment, but there's still that error:
initializer element is not constant

Andrea
 
B

Ben Bacarisse

Andrea Taverna (Tavs) said:
Ben Bacarisse ha scritto:
No, a compound literal would look like this:

.strings = (const char *[]){"alpha","beta","gamma"}

Gotcha. now it accept the assignment, but there's still that error:
initializer element is not constant

You'd better post a small example that shows the problem. On my
system the above compiles silently. (BTW, that line is not,
technically, an assignment -- it is part of an initialisation.)
 
A

Andrea Taverna (Tavs)

Ben Bacarisse ha scritto:
You'd better post a small example that shows the problem. On my
system the above compiles silently.

struct my_structure
{
int x;
const char ** strings;
};

const struct my_structure my_s = (const struct my_structure)
{
.x = 10,
.strings = (const char *[]){"alpha","beta","charlie"}
}; // <-error

The compiler (gcc4.3.0-8 for Red Hat) says the error is on the last line
> (BTW, that line is not,
> technically, an assignment -- it is part of an initialisation.)
Gotcha

thanks

Andrea
 
B

Ben Bacarisse

Andrea Taverna (Tavs) said:
Ben Bacarisse ha scritto:

struct my_structure
{
int x;
const char ** strings;
};

const struct my_structure my_s = (const struct my_structure)

Just leave out this "top-level" compound literal.
{
.x = 10,
.strings = (const char *[]){"alpha","beta","charlie"}
}; // <-error

I.e. write:

const struct my_structure my_s = {
.x = 10,
.strings = (const char *[]){"alpha","beta","charlie"}
};
 

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,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top