2010-02-08 2 views
33

Я хотел бы выделить структуру в куче, инициализировать ее и вернуть указатель на нее из функции. Я интересно, если есть способ для меня, чтобы инициализировать константные члены структуры в этом сценарии:Как инициализировать const члены structs на куче

#include <stdlib.h> 

typedef struct { 
    const int x; 
    const int y; 
} ImmutablePoint; 

ImmutablePoint * make_immutable_point(int x, int y) 
{ 
    ImmutablePoint *p = (ImmutablePoint *)malloc(sizeof(ImmutablePoint)); 
    if (p == NULL) abort(); 
    // How to initialize members x and y? 
    return p; 
} 

Должен ли я заключить из этого, что невозможно выделить и инициализировать-структуру в куче, которая содержит константные члены ?

ответ

48

Как так:

ImmutablePoint *make_immutable_point(int x, int y) 
{ 
    ImmutablePoint init = { .x = x, .y = y }; 
    ImmutablePoint *p = malloc(sizeof *p); 

    if (p == NULL) abort(); 
    memcpy(p, &init, sizeof *p); 

    return p; 
} 

(Обратите внимание, что в отличие от C++, нет необходимости подавать возвращаемое значение malloc в C, и это часто считается плохим тоном, потому что он может скрыть другие ошибки).

+6

Обратите внимание, что использование инициализации .x и .y в инициализации инициализации - C99, вы, конечно, можете использовать неименованные записи, если ваш компилятор не поддерживает его. – Trent

+0

Блестящий, кафе - спасибо! – user268344

+4

+1 Для уточнения на 'malloc' между C и C++. –

10

Если это C, а не C++, я не вижу решения, кроме как подорвать систему типов.

ImmutablePoint * make_immutable_point(int x, int y) 
{ 
    ImmutablePoint *p = malloc(sizeof(ImmutablePoint)); 
    if (p == NULL) abort(); 

    // this 
    ImmutablePoint temp = {x, y}; 
    memcpy(p, &temp, sizeof(temp)); 

    // or this 
    *(int*)&p->x = x; 
    *(int*)&p->y = y; 

    return p; 
} 
+1

Если это C, то отличное отбрасывание возвращаемого значения 'malloc' является ненужным и избыточным. – dreamlax

+1

@dreamlax: хороший улов. Я действительно скучаю по поводу C

2

Если вы настаиваете на сохранении сопзЬ в структуре, вы будете иметь, чтобы сделать некоторые кастинг, чтобы обойти это:

int *cheat_x = (int *)&p->x; 
*cheat_x = 3; 
+2

Не вызывает ли это неопределенное поведение? – dreamlax

+2

Если вы выделили память, я думаю, что все в порядке. – NateS

0

Мне нравится caf's approach, но это произошло со мной слишком

ImmutablePoint* newImmutablePoint(int x, int y){ 
    struct unconstpoint { 
     int x; 
     int y; 
    } *p = malloc(sizeof(struct unconstpoint)); 
    if (p) { // guard against malloc failure 
     *p.x = x; 
     *p.y = y; 
    } 
    return (ImmutablePoint*)p; 
} 
+0

Извините, я не вижу здесь const. Не могли бы вы объяснить? – kevinarpe

+1

@KCArpe: Я выделяю и присваиваю 'struct', который по структуре идентичен' ImmutablePoint', но является изменяемым, затем отбрасывает указатель в оператор return, чтобы измененная версия никогда не была видна пользователю.Это довольно стандартное злоупотребление кастингом, чтобы «разбить» систему типов. Недостаток решения этого относительного кафе заключается в том, что он не [DRY] (http://en.wikipedia.org/wiki/Don%27t%5Frepeat%5Fyourself): если кто-то редактирует 'ImmutablePoint', мне нужно отредактировать' unconstpoint' также. – dmckee

+3

это нарушает строгий указатель-сглаживание. –

Смежные вопросы