2016-12-27 2 views
0

Я использую C/C++ и выключается, но сегодня я видел в определенной книге то, что я никогда раньше не видел:Автоматическое выделение памяти для C структуры

struct item { 
    int code; 
    float prize; 
}; 

void main() { 

    struct item a,*b; 

    a.code = 123; 
    a.prize = 150.75; 
    printf ("Code: %d, Prize %d", a.code, a.prize); 

    b->code = 124; 
    b->prize = 200.75; 

    printf ("Code: %d, Prize %d", a->code, a->prize); 
} 

Приведенные выше принтами значения, как обычно, которые был сюрпризом относительно части *b. Так как Ь является указателем, память, выделенная для него должно быть в стеке с размером size_t в то время как данные должны быть доступны только путем выделения его отдельно в куче (ех 64 бита.):

b = (item*)malloc(sizeof(struct item)); 

По-видимому хотя, нет необходимости в этом. Как это возможно?

+1

Это неопределенное поведение. Кажется, он работает в одном случае и может произойти с малейшим изменением кода или другим прогоном. –

+4

Я голосую, чтобы закрыть этот вопрос как вне темы, потому что спрашивать о поведении неопределенного поведения бесполезно. –

+0

В книге была опечатка/ошибка, так как сейчас это UB. – Jarod42

ответ

1

Как вы правильно поставили диагноз, b - неинициализированный указатель.Доступ к элементам для чтения и записи вызывает неопределенное поведение, без памяти автоматически выделено для неинициализированных указателей. Неинициализированный указатель имеет неопределенное значение, которое не должно использоваться.

В вашем случае, программа, кажется, ведут себя нормально, но это просто совпадение, и вряд ли один на современных системах: если b случается иметь значение, которое указывает на какой-то доступной памяти, поэтому нет ошибки сегментации , но кто знает, какой объект был изменен, и какие дальнейшие последствия это может иметь ...

Не делайте этого и не компилируйте с дополнительными предупреждениями, поскольку текущие компиляторы могут обнаруживать такие ошибки:или clang -Weverything.

Есть несколько других проблем в вашем коде:

  • Вы не включают стандартный заголовок <stdio.h>;
  • вы передаете float для printf формат %d;
  • вы определяете main как возвращающийся void, он должен вернуть int;
  • вы не заканчиваете вывод на stdout с помощью новой строки.

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

Вот исправленный вариант:

#include <stdio.h> 
#include <stdlib.h> 

struct item { 
    int code; 
    float prize; 
}; 

int main(void) { 
    struct item a, *b; 

    a.code = 123; 
    a.prize = 150.75; 
    printf("Code: %d, Prize %f\n", a.code, a.prize); 

    b = malloc(sizeof(*b)); 
    if (b != NULL) { 
     b->code = 124; 
     b->prize = 200.75; 
     printf ("Code: %d, Prize %f\n", b->code, b->prize); 
    } 
    return 0; 
} 
+0

Оставляя '#include ' не делает - сам по себе - этот код имеет неопределенное поведение. В стандарте 1989/90 C предусмотрено, что, если вызывается неоткрытая функция, предполагается, что он возвращает 'int' и может иметь произвольное количество аргументов. Единственный способ, который приводит к неопределенному поведению, заключается в том, что компилятор предполагает нечто отличное от фактической функции. Более поздние стандарты C и все стандарты C++ требуют отклонения кода (хотя некоторые компиляторы C и некоторые более старые [предварительные стандартные] компиляторы C++ этого не делают). – Peter

+0

@Peter: У вас есть действительная точка. у нас была дискуссия юриста по этой теме, и это спорный вопрос, касающийся точных последствий в реальной жизни и в соответствии с различными версиями Стандартов C, я соответствующим образом внеся поправки в ответ. – chqrlie

5

В вашем случае, b->code и b->prize обращаются к неинициализированной памяти, и результат undefined behavior. Не делайте этого.

Разрабатывать, нет магической автоматического распределения памяти происходит, чтобы сделать эти указатели указуют на любую действительную память. Просто потому, что C позволяет вам написать код, доступ к неинициализированному указателю не означает, что это действительный код. Вы, , должны указать, чтобы указатели указывали на какое-то действительное место в памяти, прежде чем вы сможете получить доступ (чтение или запись).

Любой достойный компилятор, скорее всего, (, хотя и не обязателен для), чтобы создать предупреждение против такого кода. Для gcc, позволяя -Wuninitialized (включен путем добавления -Wall) должен показать предупреждение, как

предупреждение: 'б' используется неинициализированным в этой функции [-Wuninitialized]

Тем не менее, у вас есть другой главный проблема. Вы передаете float как аргумент спецификатора формата %d, который снова вызывает UB на своем собственном.

printf ("Code: %d, Prize %d", a.code, a.prize); 
         ^^^   ^^^^^^^^ 

Связанные со ссылкой C11, глава §7.21.6.1

[...] Если какой-либо аргумент не правильный тип для соответствующей спецификации преобразования, поведение не определено.

Вы должны использовать %f для печати float.

Наконец, для размещаемой среды подпись main() должна быть int main(void), по крайней мере, в соответствии со стандартом.

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