有没有办法使用`const`变量初始化`const``struct`?

时间:2018-05-18 12:33:30

标签: c struct const

我想在C99中创建一个struct,它封装了一个以空字符结尾的字符串缓冲区以及实际的字符串长度:

typedef unsigned int uint_t;
typedef unsigned char uchar_t;

typedef struct {
    uchar_t * buffer;
    uint_t length; // excluding null-terminating character
} string_t;

然而,我遇到了一些困扰const - struct成员的问题。具体来说,当我想使用一个接受这样的const struct string_t的函数,并用一个带有const成员的初始化程序来提供它时,编译器会对我大喊大叫。

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;
}

这会在GCC ......:

中对突出显示的行产生警告
  

警告:初始化丢弃&#39; const&#39;指针目标类型的限定符

...在Visual Studio 2015中:

  

警告C4090:&#39;初始化&#39;:不同的&#39; const&#39;限定符

显然,我在这里做错了。我发现解决这个问题的唯一方法是声明:

typedef struct {
    const uchar_t * buffer;
    const uint_t length;
} const_string_t;

但是现在我有两种类型而不是一种,所以我需要创建方便的方法来转换两者,我创建声明性糖而不是使用语言功能,所以代码少了可读的。

所以我的问题是,正如标题所示:有没有办法使用成员的const变量初始化struct const?有没有其他方式来达到我希望实现的结果?如果没有,为什么不(请参考官方文件)?

任何帮助都将不胜感激。

5 个答案:

答案 0 :(得分:2)

关于指针类型的常量应该“从右到左”读取(并且"const T * buffer"实际上意味着“指向const内容的指针”,而不是“指向内容的const指针”,也不是“指向const内容的const指针”。

因此,您将使用可变指针到const char缓冲区并将其用于const版本的结构(要求您的const结构具有可变字段 - 这是显示警告的原因)。如果您打算摆脱警告,您必须移动“const”这样的事情:

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);
}

如果您计划同时拥有const缓冲区,则必须重新定义string_t结构(或作为单独的类型引入):

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);
}

希望这有帮助。

答案 1 :(得分:1)

问题不在于结构层面。 问题在于.bufferunsigned char*)的初始化 char const*。如果您制作.buffer char const*,它会在没有演员的情况下工作,但由于签名不同,您仍然会收到-Wall的警告。

答案 2 :(得分:0)

您的struct有一个声明为uchar_t *buffer的成员,并且您尝试使用声明为const char *buffer的参数对其进行初始化。所以编译器抱怨是因为这样会删除 buffer所指向的内存的预设常量(这种违规是一个逻辑错误)。根据您的需要/需要,您可以:

  1. 将参数传递为非const,
  2. 以const方式传递,但随后在结构中将成员声明为const,
  3. 使用复制语义并为成员分配一些内存,并将参数指向的值复制到成员指向的内存中。
  4. 另外请注意uchar_tchar可能不是同一类型......

答案 3 :(得分:0)

正如其他人所写,问题与const类型限定符影响struct聚合类型的方式有关。所有struct成员值都会成为const。对于指针成员,它只意味着您可能不会更改指针变量以使其指向其他任何内容,但无论指针指向哪个仍然是可变的。这类似于声明int * const变量时会发生的情况。

在C99中似乎没有办法赋予const - 对作为所谓聚合类型一部分的间接(指针)类型的目标。因此,无法将关键字附加到struct - 类型变量,以表示其指针成员应被视为指向const类型的指针,例如int const *。这仅在C99C11标准中的示例中提及,在第6.7.3节(类型限定符)的末尾。

这个答案是基于回复Jean-BaptisteYuès在Yuval和PSkocik之间回答的信件。

答案 4 :(得分:0)

这只是错误的常数:

typedef unsigned int uint_t; typedef unsigned char uchar_t;

使用正确的大小类型。在C中,它是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 }; 
}