it-swarm.com.ru

Как инициализировать const в структуре в C (с помощью malloc)

Я пытался;

void *malloc(unsigned int);
struct deneme {
    const int a = 15;
    const int b = 16;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    return 0;
}

И это ошибка компилятора:

gereksiz.c:3:17: error: expected ':', ',', ';', '}' or '__attribute__' before '=' token

И также это;

void *malloc(unsigned int);
struct deneme {
    const int a;
    const int b;
};

int main(int argc, const char *argv[])
{
    struct deneme *mydeneme = malloc(sizeof(struct deneme));
    mydeneme->a = 15;
    mydeneme->b = 20;
    return 0;
}

И это ошибка компилятора:

gereksiz.c:10:5: error: assignment of read-only member 'a'
gereksiz.c:11:5: error: assignment of read-only member 'b'

И ни один не был скомпилирован. Есть ли способ инициализировать переменную const внутри структуры при выделении памяти с помощью malloc?

20
yasar

Вам нужно отбросить const для инициализации полей структуры malloc:

struct deneme *mydeneme = malloc(sizeof(struct deneme));
*(int *)&mydeneme->a = 15;
*(int *)&mydeneme->b = 20;

Кроме того, вы можете создать инициализированную версию структуры и написать ее:

struct deneme deneme_init = { 15, 20 };
struct deneme *mydeneme = malloc(sizeof(struct deneme));
memcpy(mydeneme, &deneme_init, sizeof(struct deneme));

Вы можете сделать deneme_init статическим и/или глобальным, если вы делаете это много (поэтому его нужно создать только один раз).


Объяснение того, почему этот код не является неопределенным поведением, как предлагается в некоторых комментариях, с использованием стандартных ссылок C11:

  • Этот код не нарушает 6.7.3/6, потому что пространство, возвращаемое malloc, не является "объектом определенным с типом, определенным константой". Выражение mydeneme->a не является объектом, это выражение. Хотя он имеет const- уточненный тип, он обозначает объект, который не был определен с помощью const-уточненного типа (фактически, вообще не определен ни с каким типом).

  • Строгое правило псевдонимов никогда не нарушается записью в пространство, выделенное malloc, потому что тип ffective (6.5/6) обновляется при каждой записи.

(Строгое правило псевдонимов может быть нарушено чтением из пространства, выделенного malloc как бы то ни было).

В примерах кода Криса первый устанавливает эффективный тип целочисленных значений на int, а второй устанавливает эффективный тип на const int, однако в обоих случаях чтение этих значений через *mydeneme корректно, поскольку правило строгого псевдонима (6.5/7 bullet 2) позволяет считывать объект через выражение, которое в равной степени или более квалифицировано, чем эффективный тип объекта. Поскольку выражение mydeneme->a имеет тип const int, его можно использовать для чтения объектов эффективного типа int и const int.

22
Chris Dodd

Вы пытались сделать так:

int main(int argc, const char *argv[])
{
    struct deneme mydeneme = { 15, 20 };
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    memcpy(pmydeneme, &mydeneme , sizeof(mydeneme));
    return 0;
}

Я не проверял, но код кажется правильным

10
FBruynbroeck

Я не согласен с ответом Христа Додда , поскольку я думаю, что его решение дает неопределенное поведение в соответствии со стандартами, как говорили другие. 

Чтобы «обойти» квалификатор const таким образом, чтобы не вызывать неопределенное поведение, я предлагаю следующее решение: 

  1. Определите переменную void*, инициализированную вызовом malloc()
  2. Определите и возьмите объект нужного типа, в данном случае struct deneme, и инициализируйте его таким образом, чтобы квалификатор const не жаловался (то есть в самой строке объявления). 
  3. Используйте memcpy() для копирования битов объекта struct deneme в объект void*
  4. Объявите указатель на объект struct deneme и инициализируйте его переменной (void*), ранее приведенной к (struct deneme *)

Итак, мой код будет: 

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct deneme {
    const int a;
    const int b;
};
struct deneme* deneme_init(struct deneme data) {
    void *x = malloc(sizeof(struct deneme));
    memcpy(x, &data, sizeof(struct deneme));
    return (struct deneme*) x;
}
int main(void) {
    struct deneme *obj = deneme_init((struct deneme) { 15, 20, } );
    printf("obj->a: %d, obj->b: %d.\n", obj->a, obj->b);
    return 0;
}
2
pablo1977

Интересно, я нашел этот способ C99 работает в Clang, но не в GCC

int main(int argc, const char *argv[])
{
    struct deneme *pmydeneme = malloc(sizeof(struct deneme));
    *pmydeneme = (struct deneme) {15, 20};
    return 0;
}
1
Andrey