Reputation: 3433
I would like to create a struct
in C99 that encapsulates a null-terminated string buffer together with the actual string length:
typedef unsigned int uint_t;
typedef unsigned char uchar_t;
typedef struct {
uchar_t * buffer;
uint_t length; // excluding null-terminating character
} string_t;
However, I am encountering several difficulties revolving the const
-ness of the struct
's members. Specifically, when I want to consume a function that accepts such a const struct string_t
, and feed it with an initializer with const members, the compiler yells at me.
void show_string_internal(const string_t str) {
printf("%s", str.buffer);
}
void show_string(const uchar_t * buffer, uint_t length) {
const string_t str = // <== tricky assignment here
(const string_t){ buffer, length };
show_string_internal(str);
}
int main() {
uchar_t message[] = "Hello, world.";
show_string(message, sizeof(message) - 1);
return 0;
}
This produces a warning over the highlighted line both in GCC...:
warning: initialization discards 'const' qualifier from pointer target type
... and in Visual Studio 2015:
warning C4090: 'initializing': different 'const' qualifiers
Apparently, I am doing something wrong here. The only way I found to get around this is by declaring:
typedef struct {
const uchar_t * buffer;
const uint_t length;
} const_string_t;
But now I have two types instead of one, so I'll need to create handy ways to convert between the two, and I'm creating declarative sugar instead of using language features, so the code is less readable.
So my question is, as the title reads: is there a way to initialize a const
struct
using const
variables for the members? Is there an alternative manner to reach the results I wish to achieve? If not, why not (please include references to official documentation)?
Any help would be appreciated.
Upvotes: 1
Views: 2461
Reputation: 67476
it is just the wrong constantness:
typedef unsigned int uint_t; typedef unsigned char uchar_t;
use the correct type for size. In C it is the size_t
typedef struct {
const uchar_t * buffer;
size_t length; // excluding null-terminating character
} string_t;
void show_string(const uchar_t * buffer, size_t length) {
const string_t str = // <== tricky assignment here
(string_t){ buffer, length };
}
Upvotes: 0
Reputation: 3433
As other people have written, the problem has to do with the way the const
type qualifier affects the struct
aggregate type. All of the struct
member values become const
themselves. For pointer members, it only means that you may not change the pointer variable so that it would point at anything else, but whatever the pointer points to remains mutable. This is similar to what would happen if you'd declare an int * const
variable.
There seems to be no way in C99 to endow const
-ness on the targets of indirect (pointer) types that are part of so-called aggregate types. So there is no way to attach a keyword to a struct
-type variable to signify that its pointer members should be considered pointers to const
types, such as int const *
. This is only mentioned by example in both the C99 and C11 standards, at the end of section 6.7.3 (Type Qualifiers).
This answer is based on correspondence in reply to Jean-Baptiste Yunès' answer, between Yuval and PSkocik.
Upvotes: 0
Reputation: 5369
Constness about pointer types should be read "from right to left" (and "const T * buffer"
means "pointer to const content" actually, not "const pointer to content" nor "const pointer to const content).
So, you was about to use mutable pointer to const char buffer and use it for const version of structure (asking your const struct having mutable field - this is a reason for the warning shown). In case you plan to get rid of the warning you have to move "const" thing like that:
void show_string(char * const buffer, const int length) { // <== move const to the right of "*"
const string_t str = // <== tricky assignment here
(const string_t) {
buffer, length
};
show_string_internal(str);
}
In case you plan to have const buffer itself as well, you have to redefine your string_t structure (or introduce as separate type):
typedef struct {
const uchar_t * buffer; // <== add "const" here
uint_t length; // excluding null-terminating character
} string_t;
...
void show_string(const char * const buffer, const int length) { // two "const" clauses there
const string_t str = // <== tricky assignment here
(const string_t) {
buffer, length
};
show_string_internal(str);
}
Hope this helps.
Upvotes: 2
Reputation: 60068
The problem is not at the struct level.
The problem is in the initialization of .buffer
(unsigned char*
) with
char const*
. If you make .buffer
char const*
, it'll work without casts, although you will still get warnings with -Wall
due to different signedness.
Upvotes: 1
Reputation: 36401
Your struct has a member declared as uchar_t *buffer
and you tried to initialize it with a parameter declared as const char *buffer
. So the compiler complains because such would remove the presupposed constness of the memory pointed by buffer
(such a violation is a logical error). Depending on what you need/want you may:
Also be carefull that uchar_t
and char
may not be the same type...
Upvotes: 0