используя ваш опубликованный код (немного переформатирован для удобства чтения:
#include <stdio.h>
#include <stdlib.h>
struct Box
{
int **value;
};
void nop(void)
{
/* Why does this function have side effects? */
void *a = malloc(sizeof *a);
free(a);
}
struct Box *makeBox(void)
{
int *value = NULL;
struct Box *box = malloc(sizeof *box);
box->value = &value;
return box;
}
int main(void)
{
struct Box *box = makeBox();
printf("Dereferenced: %p\n", *box->value);
nop();
printf("Dereferenced: %p\n", *box->value);
}
всегда включать предупреждения при компиляции. затем исправьте эти предупреждения. (для gcc
, при минимальном использовании:
-Wall -Wextra -Wconversion -pedantic -std=gnu11
В результате компилятор выдаст несколько предупреждающих сообщений.
(то, что в сообщении говорится «предупреждение», а не «ошибка», не означает, что предупреждения можно игнорировать.
Предупреждения:
: 13: 29: предупреждение: недопустимое применение размера sizeof к типу пустоты [-Wpointer-arith]
: 30: 12: предупреждение: формат «% p» ожидает аргумент типа
"Void *", но аргумент 2 имеет тип "int *" [-Wformat =]
: 32: 12: предупреждение: формат «% p» ожидает аргумент типа «void *», но аргумент 2 имеет тип «int *» [-Wformat =]
EDIT:
Функция makebox()
возвращает адрес локальной переменной. локальные переменные выходят из области видимости при выходе из функции. так что это неопределенное поведение.
Вышеуказанное неопределенное поведение является проблемой при вызове функции: nop()
. потому что он использует ту же область стека, что и в функции 'makebox () `.
Локальная переменная: void *a
использует ту же часть стека, что и функциональная переменная makebox()
value
.
, поэтому, когда функция main()
разыменовывает поле в struct Box, which points to the same area on the stack, and that area has been modified by the later call to
nop () `, тогда значение в стеке в этой области было изменено.
, поэтому вызовы на printf()
при первом вызове получат исходное значение, а при втором вызове получит адрес, возвращенный после вызова на malloc()
т.е. изменить код, чтобы он не имел неопределенного поведения. Это можно сделать, либо изменив struct box
, чтобы он содержал фактические данные, либо переместив локальную переменную: value
в некоторую «безопасную область», например, предоставив ей область видимости файла, возможно, добавив модификатор static
перед объявление переменной