Почему нет необходимости использовать realloc при выделении нового элемента для указателя на символ? - PullRequest
2 голосов
/ 22 сентября 2010

Я пытаюсь создать указатель на указатель на символ, к которому я могу легко добавить новые элементы (строки). Я использую malloc для создания первых двух измерений и realloc , когда я хочу добавить новые элементы. Я написал код с и без realloc , и я получаю одинаковые результаты. Это ожидаемое / нормальное поведение?


С realloc :

    char **p; // create pointer to char pointer
    p = malloc(sizeof(char*) * 2); // allocate 2 dimensions
    p[0] = "ab";
    p[1] = "cd";

    void* resizedP = (void*)realloc(p, sizeof(char*) * 4); // resize array
    p = (char**)resizedP;
    p[2] = "ef";
    p[3] = "gh";

    printf("%s \n", p[0]); // prints ab
    printf("%s \n", p[1]); // prints cd
    printf("%s \n", p[2]); // prints ef
    printf("%s \n", p[3]); // prints gh

    free(p);


Без перераспределения :

    char **p;
    p = malloc(sizeof(char*) * 2);

    p[0] = "ab";
    p[1] = "cd";
    p[2] = "ef";
    p[3] = "gh";

    printf("%s \n", p[0]); // prints ab
    printf("%s \n", p[1]); // prints cd
    printf("%s \n", p[2]); // prints ef
    printf("%s \n", p[3]); // prints gh

    free(p);

Ответы [ 5 ]

8 голосов
/ 22 сентября 2010

Абсолютно необходимо вызвать realloc(), чтобы изменить размер блока памяти, полученного с помощью malloc(). Вы вызываете неопределенное поведение , получая доступ за пределами блока памяти, выделенного вашей программе.

Сразу после выделенного блока памяти могут быть некоторые важные данные, через которые будет переброшен ваш второй фрагмент кода. И если программа даже пытается получить доступ к блокам памяти в другом процессе из-за ошибки, выходящей за границы, операционная система будет громко жаловаться (в результате ошибки сегментации ).

Нет никакой гарантии, что ваш второй фрагмент кода (без realloc()) не будет аварийно завершен или в противном случае потерпит неудачу. Это случилось на этот раз.

4 голосов
/ 22 сентября 2010

В вашем случае это может работать по стечению обстоятельств. Например, malloc() вашей библиотеки C может решить дополнить ваш размер выделения некоторым числом, которое оказывается >= 2*sizeof(void*).

Однако, теоретически, ничто не может помешать последующему malloc вернуть байты, которые находятся в p[2]. Также ничто не мешает malloc возвращать значение, при котором после запрошенного вами размера ОС не отображает ни одной страницы в адресное пространство. Поэтому вы не должны полагаться на это поведение. Не пишите p[2] без выделения правильной суммы, потому что это может привести к любому из следующего:

  • Вы перезаписываете значение, которое написала какая-то другая функция.
  • Некоторые другие функции могут перезаписать вас позже.
  • Вы можете потерпеть крах при попытке получить к нему доступ.
3 голосов
/ 22 сентября 2010

Попробуйте запустить второй вариант через valgrind или с помощью ключа компилятора -fstack-protector . Вы можете получить больше информации о том, почему In silico ответ правильный.

2 голосов
/ 22 сентября 2010

Предположим, ваш p - это автобус на 20 человек.

И вы пытаетесь поместить 30 человек внутрь (не увеличивая его). Иногда это работает, , если они не толстые ...

В отношении C, это Неопределенное поведение . Все может случиться. Тебе просто не повезло, и это "сработало".

Короче: не делай этого!

1 голос
/ 22 сентября 2010

Ого, не делай этого человека!Есть причина, по которой он работает, и это не что иное, как проблема.

Во-первых, p - это не массив, это указатель, вы можете убедиться в этом, проверив sizeof(p), что он всегда вернет 4 или 8, в зависимостиот того, используете ли вы 32- или 64-разрядную систему.

Указатель относится к позиции в памяти, поэтому, если вы попытаетесь напечатать указатель, вы получите адрес памяти, такой как 0x0B32D2F1 на 32-Бит-машина. Когда вы вызываете одну из функций *alloc, процессор находит свободный блок памяти того размера, который вы запрашиваете, и выделяет его для вашей программы.

То, что вы делаете, - это доступ и редактирование памяти, котораяне был выделен вам, хотя это легко возможно, ничто не мешает вам сделать это, но это плохо, потому что эта память может быть выделена для другой программы, и тогда пользователь будет ненавидеть вас, когда вы завершите работу других его программ.Короче говоря, ВСЕГДА ВЫДЕЛИТЕ ПАМЯТЬ, которую ВЫ ИСПОЛЬЗУЕТЕ!ВСЕГДА!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...