[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Can I put constant string arrays in a header file

Tobias Blass

4/8/2011 10:43:00 PM

Hi,
I'm currently implementing a string lookup table, mapping error numbers to
human readable strings. So my question is: Can I put the strings in a header
file, or will I end up with N exact copies of this array (N being the number
of inclusions of this file). I use gcc on Linux, so if anyone can tell me that
gcc will optimise N-1 copies of the array out I'm happy with that, too.

/* error.h */
#ifndef ERROR_H
#define ERROR_H

const char *errstrs[]={
"first error",
"second error",
...
};
#endif
22 Answers

Ian Collins

4/8/2011 11:38:00 PM

0

On 04/ 9/11 10:43 AM, Tobias Blass wrote:
> Hi,
> I'm currently implementing a string lookup table, mapping error numbers to
> human readable strings. So my question is: Can I put the strings in a header
> file, or will I end up with N exact copies of this array (N being the number
> of inclusions of this file).

There will only be one copy.

--
Ian Collins

Keith Thompson

4/9/2011 12:18:00 AM

0

Ian Collins <ian-news@hotmail.com> writes:
> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>> Hi,
>> I'm currently implementing a string lookup table, mapping error numbers to
>> human readable strings. So my question is: Can I put the strings in a header
>> file, or will I end up with N exact copies of this array (N being the number
>> of inclusions of this file).
>
> There will only be one copy.

The code in question is:

/* error.h */
#ifndef ERROR_H
#define ERROR_H

const char *errstrs[]={
"first error",
"second error",
...
};
#endif

There won't be one copy per inclusion, because of the header guards,
but you don't want a *definition* in a header file. If the header
is included in two or more translation units that are linked into
a single program, you might get multiple copies of the strings,
or you might just get a link error. (I'm not quite sure what the
language permits or requires here, but don't do it.)

Here's what I came up with. Note that it makes errstrs a pointer
rather than an array, but you can still refer to errstrs[0],
errstrs[1], etc. If you make it an array, it means that client code
can figure out how many elements there are, but it also means that
you have to specify the length in the declaration and recompile if
it changes.

Note that identifiers starting with 'E' are reserved, so I changed
the include guard macro name.

/* error.h */
#ifndef H_ERROR
#define H_ERROR

const char *const *const errstrs;

#endif

/* error.c */
#include "error.h"

static const char *const error_strings[] = {
"first error",
"second error"
};

const char *const *const errstrs = error_strings;

Yes, that's a lot of "const"s. It means that client code
can't modify the top-level pointer, any of the pointers in the
error_strings array, or any of the error strings themselves.

It might be simpler for error.h to declare a function that takes an int
and returns a pointer to the error message.

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Ian Collins

4/9/2011 12:47:00 AM

0

On 04/ 9/11 12:18 PM, Keith Thompson wrote:
> Ian Collins<ian-news@hotmail.com> writes:
>> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>>> Hi,
>>> I'm currently implementing a string lookup table, mapping error numbers to
>>> human readable strings. So my question is: Can I put the strings in a header
>>> file, or will I end up with N exact copies of this array (N being the number
>>> of inclusions of this file).
>>
>> There will only be one copy.
>
> The code in question is:
>
> /* error.h */
> #ifndef ERROR_H
> #define ERROR_H
>
> const char *errstrs[]={
> "first error",
> "second error",
> ...
> };
> #endif
>
> There won't be one copy per inclusion, because of the header guards,
> but you don't want a *definition* in a header file. If the header
> is included in two or more translation units that are linked into
> a single program, you might get multiple copies of the strings,
> or you might just get a link error. (I'm not quite sure what the
> language permits or requires here, but don't do it.)

Bugger, I didn't notice there was one to few consts! As you say, one
good reason not to do this!

--
Ian Collins

Ben Bacarisse

4/9/2011 1:39:00 AM

0

Ian Collins <ian-news@hotmail.com> writes:

> On 04/ 9/11 12:18 PM, Keith Thompson wrote:
>> Ian Collins<ian-news@hotmail.com> writes:
>>> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>>>> Hi,
>>>> I'm currently implementing a string lookup table, mapping error numbers to
>>>> human readable strings. So my question is: Can I put the strings in a header
>>>> file, or will I end up with N exact copies of this array (N being the number
>>>> of inclusions of this file).
>>>
>>> There will only be one copy.
>>
>> The code in question is:
>>
>> /* error.h */
>> #ifndef ERROR_H
>> #define ERROR_H
>>
>> const char *errstrs[]={
>> "first error",
>> "second error",
>> ...
>> };
>> #endif
>>
>> There won't be one copy per inclusion, because of the header guards,
>> but you don't want a *definition* in a header file. If the header
>> is included in two or more translation units that are linked into
>> a single program, you might get multiple copies of the strings,
>> or you might just get a link error. (I'm not quite sure what the
>> language permits or requires here, but don't do it.)
>
> Bugger, I didn't notice there was one to few consts! As you say, one
> good reason not to do this!

I think you may be confusing C and C++ here. The presence of a "top
level" const makes all the difference in C++ (well it did that time I
read a C++ reference manual) but it does not make any difference to how
the objects may or may not get merged in a C program.

I can't find any text that describes what it means in C to have more
that one external definition for an object. I thought there was a "one
definition rule" but if so I can't find it at the moment.

--
Ben.

Keith Thompson

4/9/2011 1:54:00 AM

0

Ian Collins <ian-news@hotmail.com> writes:
> On 04/ 9/11 12:18 PM, Keith Thompson wrote:
>> Ian Collins<ian-news@hotmail.com> writes:
>>> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>>>> I'm currently implementing a string lookup table, mapping error
>>>> numbers to human readable strings. So my question is: Can I put the
>>>> strings in a header file, or will I end up with N exact copies of
>>>> this array (N being the number of inclusions of this file).
>>>
>>> There will only be one copy.
>>
>> The code in question is:
>>
>> /* error.h */
>> #ifndef ERROR_H
>> #define ERROR_H
>>
>> const char *errstrs[]={
>> "first error",
>> "second error",
>> ...
>> };
>> #endif
>>
>> There won't be one copy per inclusion, because of the header guards,
>> but you don't want a *definition* in a header file. If the header
>> is included in two or more translation units that are linked into
>> a single program, you might get multiple copies of the strings,
>> or you might just get a link error. (I'm not quite sure what the
>> language permits or requires here, but don't do it.)
>
> Bugger, I didn't notice there was one to few consts! As you say, one
> good reason not to do this!

Hmm? I wasn't referring to the consts, just that defining objects
in header files is a bad idea. It's better to *declare* the
object in the header and *define* it in the corresponding .c file.
(Or, perhaps even better, to declare a function that gives you the
same information.)

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Ian Collins

4/9/2011 2:28:00 AM

0

On 04/ 9/11 01:38 PM, Ben Bacarisse wrote:
> Ian Collins<ian-news@hotmail.com> writes:
>> On 04/ 9/11 12:18 PM, Keith Thompson wrote:
>>>
>>> The code in question is:
>>>
>>> /* error.h */
>>> #ifndef ERROR_H
>>> #define ERROR_H
>>>
>>> const char *errstrs[]={
>>> "first error",
>>> "second error",
>>> ...
>>> };
>>> #endif
>>>
>>> There won't be one copy per inclusion, because of the header guards,
>>> but you don't want a *definition* in a header file. If the header
>>> is included in two or more translation units that are linked into
>>> a single program, you might get multiple copies of the strings,
>>> or you might just get a link error. (I'm not quite sure what the
>>> language permits or requires here, but don't do it.)
>>
>> Bugger, I didn't notice there was one to few consts! As you say, one
>> good reason not to do this!
>
> I think you may be confusing C and C++ here. The presence of a "top
> level" const makes all the difference in C++ (well it did that time I
> read a C++ reference manual) but it does not make any difference to how
> the objects may or may not get merged in a C program.

Um, right again. I think I should confine myself to something
productive like unclogging more drains this weekend.

const char* const errstrs[]={
"first error",
"second error"
};

Will result in one and only one instance of errstrs in C++. I wonder
why this was never added to C? I can't imagine it breaking any existing
code.

--
Ian Collins

Morris Keesan

4/9/2011 4:05:00 AM

0

On Fri, 08 Apr 2011 20:18:03 -0400, Keith Thompson <kst-u@mib.org> wrote:

> Ian Collins <ian-news@hotmail.com> writes:
>> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>>> Hi,
>>> I'm currently implementing a string lookup table, mapping error
>>> numbers to
>>> human readable strings. So my question is: Can I put the strings in a
>>> header
>>> file, or will I end up with N exact copies of this array (N being the
>>> number
>>> of inclusions of this file).
>>
>> There will only be one copy.
>
> The code in question is:
>
> /* error.h */
> #ifndef ERROR_H
> #define ERROR_H
>
> const char *errstrs[]={
> "first error",
> "second error",
> ...
> };
> #endif
>
> There won't be one copy per inclusion, because of the header guards,
> but you don't want a *definition* in a header file. If the header
> is included in two or more translation units that are linked into
> a single program, you might get multiple copies of the strings,
> or you might just get a link error. (I'm not quite sure what the
> language permits or requires here, but don't do it.)
>
> Here's what I came up with. Note that it makes errstrs a pointer
> rather than an array, but you can still refer to errstrs[0],
> errstrs[1], etc. If you make it an array, it means that client code
> can figure out how many elements there are, but it also means that
> you have to specify the length in the declaration and recompile if
> it changes.
>
> Note that identifiers starting with 'E' are reserved, so I changed
> the include guard macro name.
>
> /* error.h */
> #ifndef H_ERROR
> #define H_ERROR
>
> const char *const *const errstrs;
>
> #endif
>
> /* error.c */
> #include "error.h"
>
> static const char *const error_strings[] = {
> "first error",
> "second error"
> };
>
> const char *const *const errstrs = error_strings;

This seems overly complex. Is there any good reason not to have

/* error.c */
const char *const error_strings[] = { "first error", "second error" };

/* error.h */
extern const char *const error_strings[];

I.e. why not just make error_strings a global array, and expose the
array name as an extern? Why go to the trouble of declaring a separate
pointer?

(Or, as was suggested elsewhere in this thread, make the whole thing
static, and provide a function to access the members of the array).



--
Morris Keesan -- mkeesan@post.harvard.edu

Keith Thompson

4/9/2011 4:29:00 AM

0

"Morris Keesan" <mkeesan@post.harvard.edu> writes:
> On Fri, 08 Apr 2011 20:18:03 -0400, Keith Thompson <kst-u@mib.org> wrote:
[snip]
> This seems overly complex. Is there any good reason not to have
>
> /* error.c */
> const char *const error_strings[] = { "first error", "second error" };
>
> /* error.h */
> extern const char *const error_strings[];
>
> I.e. why not just make error_strings a global array, and expose the
> array name as an extern? Why go to the trouble of declaring a separate
> pointer?

I actually tried that. The problem is that any code that includes
"error.h" has no way of knowing how big the array is. (More precisely,
the compiler has no way of knowing this when it's compiling a source
file that includes "error.h".)

#include "error.h"
....
printf("error_strings has %d elements\n",
(int)sizeof error_strings / sizeof error_strings[0]);

gcc gave me a warning that it was assuming the size of the array is 1.
Other compilers may behave differently.

You can avoid that by declaring the size of the array, but then you have
to recompile whenever you add a new error string.

> (Or, as was suggested elsewhere in this thread, make the whole thing
> static, and provide a function to access the members of the array).

Yes.

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Barry Schwarz

4/9/2011 6:27:00 AM

0

On Fri, 8 Apr 2011 22:43:12 +0000 (UTC), Tobias Blass
<tobiasblass@gmx.net> wrote:

>Hi,
>I'm currently implementing a string lookup table, mapping error numbers to
>human readable strings. So my question is: Can I put the strings in a header
>file, or will I end up with N exact copies of this array (N being the number
>of inclusions of this file). I use gcc on Linux, so if anyone can tell me that
>gcc will optimise N-1 copies of the array out I'm happy with that, too.
>
>/* error.h */
>#ifndef ERROR_H
>#define ERROR_H
>
>const char *errstrs[]={
> "first error",
> "second error",
> ...
> };
>#endif

If the header is included at file scope, then per 6.9.2 the
declaration of errstrs is also an "external definition".

If the header is included in multiple translation units, errstrs will
be defined in each object module.

Paragraph 6.9-5 requires that there be only one such definition.

Chances are the linker will complain when you attempt to combine all
the object modules into a single program.

--
Remove del for email

Tobias Blass

4/9/2011 9:37:00 AM

0

On 2011-04-09, Keith Thompson <kst-u@mib.org> wrote:
> Ian Collins <ian-news@hotmail.com> writes:
>> On 04/ 9/11 10:43 AM, Tobias Blass wrote:
>>> Hi,
>>> I'm currently implementing a string lookup table, mapping error numbers to
>>> human readable strings. So my question is: Can I put the strings in a header
>>> file, or will I end up with N exact copies of this array (N being the number
>>> of inclusions of this file).
>>
>> There will only be one copy.
>
> The code in question is:
>
> /* error.h */
> #ifndef ERROR_H
> #define ERROR_H
>
> const char *errstrs[]={
> "first error",
> "second error",
> ...
> };
> #endif
>
> There won't be one copy per inclusion, because of the header guards,
> but you don't want a *definition* in a header file. If the header
> is included in two or more translation units that are linked into
> a single program, you might get multiple copies of the strings,
> or you might just get a link error. (I'm not quite sure what the
> language permits or requires here, but don't do it.)
That's what I meant by "inclusion". I've got some .c files, each of them
includes error.h, but I don't want to have 5 Instances of the same table.
> Note that identifiers starting with 'E' are reserved, so I changed
> the include guard macro name.
Thanks for the note, I didn't know that
> /* error.h */
> #ifndef H_ERROR
> #define H_ERROR
>
> const char *const *const errstrs;
>
> #endif
>
> /* error.c */
> #include "error.h"
>
> static const char *const error_strings[] = {
> "first error",
> "second error"
> };
>
> const char *const *const errstrs = error_strings;
>
I wanted to do that first, but I think it's quite ugly to have a separate C file
just for the table, so I wanted to ask whether it's possible to do that in the
header file instead.
(The const thing is a good hint, I find it rather difficult to remember which
const has which effect */
>
> It might be simpler for error.h to declare a function that takes an int
> and returns a pointer to the error message.
>
That's possibly true, but I think it's easier to change an entry in a table
than to change it in a function.