Есть ли способ инициализировать `const`` struct` с помощью переменных `const`? - PullRequest
0 голосов
/ 18 мая 2018

Я хотел бы создать struct в C99, который инкапсулирует строковый буфер с нулевым символом в конце вместе с фактической длиной строки:

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, и передать ее инициализатором с константными членами, компилятор кричит на меня.

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 ...:

предупреждение: инициализация отбрасывает квалификатор const из целевого типа указателя

... и в Visual Studio 2015:

предупреждение C4090: «инициализация»: разные квалификаторы «const»

Видимо, я что-то делаю не такВот.Единственный способ обойти это - объявить:

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

Но теперь у меня есть два типа вместо одного, поэтому мне нужно создать удобные способы преобразования между ними, и я 'Я создаю декларативный сахар вместо использования языковых возможностей, поэтому код менее читабелен.

Поэтому мой вопрос таков, как гласит заголовок: есть ли способ инициализировать const struct с использованием constпеременные для членов?Есть ли альтернативный способ достижения результатов, которых я хочу достичь?Если нет, то почему (пожалуйста, включите ссылки на официальную документацию)?

Буду признателен за любую помощь.

Ответы [ 5 ]

0 голосов
/ 18 мая 2018

это просто неправильная постоянность:

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 }; 
} 
0 голосов
/ 18 мая 2018

Как писали другие люди, проблема связана с тем, как квалификатор типа const влияет на тип агрегата struct.Все значения элементов struct сами становятся const.Для членов-указателей это означает лишь то, что вы не можете изменять переменную-указатель, чтобы она указала на что-либо еще, но на что бы указатель ни указывал, он остается изменчивым.Это похоже на то, что произошло бы, если бы вы объявили переменную int * const.

Кажется, в C99 нет никакого способа наделить const -ness целевыми объектами косвенных (указательных) типов, которыечасть так называемых агрегатных типов.Поэтому невозможно прикрепить ключевое слово к переменной struct -типа, чтобы указать, что его члены-указатели следует рассматривать как указатели на типы const, такие как int const *.Это упоминается только в качестве примера в стандартах C99 и C11 , в конце раздела 6.7.3 (Спецификаторы типов).

Этот ответ основан напереписка в ответ на ответ Жан-Батиста Юнеса, между Ювалом и PSkocik.

0 голосов
/ 18 мая 2018

Проблема не на уровне структуры.Проблема в инициализации .buffer (unsigned char*) с char const*.Если вы сделаете .buffer char const*, он будет работать без приведения, хотя вы все равно будете получать предупреждения с -Wall из-за разной подписи.

0 голосов
/ 18 мая 2018

Constness о типах указателей следует читать «справа налево» (а "const T * buffer" означает «указатель на содержимое const» на самом деле, а не «указатель const на содержимое» или «указатель 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);
}

Надеюсь, это поможет.

0 голосов
/ 18 мая 2018

В вашей структуре есть член, объявленный как uchar_t *buffer, и вы попытались инициализировать его параметром, объявленным как const char *buffer.Таким образом, компилятор жалуется, потому что удалит предполагаемую константность памяти, на которую указывает buffer (такое нарушение является логической ошибкой).В зависимости от того, что вам нужно / нужно, вы можете:

  1. передать параметр как неконстантный,
  2. передать как const, но затем объявить член как const в структуре,
  3. использовать семантику копирования и выделить часть памяти для члена и скопировать значения, указанные параметром, в память, указанную элементом.

Также будьте осторожны, что uchar_t и char могут не бытьтого же типа ...

...