2016-04-28 2 views
1

У меня есть вопрос о функции realloc. Будет ли изменен контент старого указателя после применения функции realloc? КодЧто будет realloc делать со старой стрелкой

main() { 
    int *a, *b, i; 

    a = calloc(5, sizeof(int)); 
    for (i = 0; i < 5; i++) 
      a[i] = 1; 
    for (i = 0; i < 5; i++) 
      printf("%d", a[i]); 
    printf("\n%p\n", a); 

    b = realloc(a, 200000 * sizeof(int)); 
    if(b == NULL) 
      printf("error\n"); 
    for (i = 0; i < 5; i++) 
      printf("%d", a[i]); 
    printf("\n"); 
    for (i = 0; i < 10; i++) 
      printf("%d", b[i]); 

    printf("\n%p %p\n", a, b); 
} 

Выход

11111 
0x2558010 
00111 
1111100000 
0x2558010 0x7f29627e6010 

Указатель неподвижной точкой на тот же адрес, но содержание изменилось.

+4

чтение 'перераспределить()' спецификации и, возможно, Linux вручную или любое руководство. Поведение не совсем последовательное, это зависит. Кроме того, не делайте 'main()' вроде этого без возвращаемого типа 'int', это действительно старый и устаревший [tag: c]. –

+4

C строго передается по значению! Как «realloc» даже сможет изменить указатель? – Olaf

+2

@iharob: Я чувствую, что это непротиворечиво? Спецификация C очень понятна в отношении поведения. – Olaf

ответ

5

Указатель неподвижной точкой на тот же адрес, но содержание изменилось.

Это потому, что realloc() может сначала попытаться увеличить размер блока, который a указывает. Тем не менее, он может вместо этого выделить новый блок, скопировать данные (или столько, сколько будет поместиться) в новый блок и освободить старый блок. Вы действительно не должны использовать a после вызова b = realloc(a, 200000 * sizeof(int)), так как вызов realloc может переместить блок в новое место, оставив a, указывая на память, которая больше не выделяется. Вместо этого используйте b.

+0

И поскольку новый указатель на другой блок должен быть выделен до того, как 'realloc' освободит старую память (данные должны быть скопированы), пример OP может все еще иметь возможность доступа к предыдущим данным, но это * неопределенное поведение *. –

0

Чтение страницы руководства здесь ключевое, но TLDR, если на заднем конце предыдущего блока недостаточно памяти для увеличения, он получит новый блок памяти, скопирует в него старые данные, и вернуть адрес нового блока. Старый адрес не должен использоваться, и наиболее типичное высказывание перераспределить выглядит следующим образом

a = realloc(a, 200000 * sizeof(int)); 

Таким образом, вы случайно не использовать, возможно, неправильно старое значение.

Он не может изменить адрес в указателе, поскольку он передается по значению, поэтому его изменение в функции изменяет только локальную копию.

EDIT: На абсолютно правильный комментарий флюгером, тем безопаснее маршрут будет

void * b = realloc(a, 200000 * sizeof(int)); 
    if (b) { 
     a = b; 
    } else { 
     ;;; /* error handler here */ 
    } 
+2

Это против обычного совета, который должен назначить новый указатель на другую переменную. Тогда, если это «NULL», предыдущий указатель все еще действителен, и любое серьезное приложение, которое не просто «выйдет» в этой точке, может реализовать стратегию восстановления - например, для хранения важных данных, которые вы действительно не хотите потерять , Если распределение было хорошим, вы заменили исходный указатель. –

0

Простой realloc реализация должна ответить на ваши вопросы:

void * realloc(void * ptr, size_t desired_size) { 
    size_t allocated_size = _allocated_size_of(ptr); 
    if (allocated_size < desired_size) { 
     void * new_ptr = malloc(desired_size); 
     memcpy(new_ptr, ptr, allocated_size); 
     free(ptr); 
     ptr = new_ptr; 
    } 
    return ptr; 
} 

malloc и связанные с ней функции не всегда выделяют именно нужный размер. Очень часто они выделяют больше, чем желаемый размер. Есть некоторые скрытые данные, поддерживаемые функциями выделения памяти, которые позволяют указать указатель, который был выделен malloc или связанными функциями, которые будут использоваться для поиска размера выделенного блока памяти. Как это следует учитывать, не нужно понимать, но некоторые очень простые реализации просто сохраняют размер в пространстве перед возвратом указателя *(((size_t)ptr)-1).

+1

Я думал, что 'realloc' также может уменьшить память. –

+1

Возможно, это сообщение SO может внести свой вклад в тему - [здесь ссылка] (http://stackoverflow.com/questions/7078019/using-realloc-to-shrink-the-allocated-memory) – user3078414

+0

@WeatherVane: Он может, но в этой упрощенной версии фактический размер блока памяти не уменьшается. Попытка сделать это в этом коде сделала бы его гораздо менее кратким и слишком сильно зависеть от реализации распределения памяти. Если этого недостаточно для моего вопроса, мои извинения. – nategoose

0

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

4

Значение, возвращаемое realloc, сообщает вам, удалось ли это или не удалось.

b = realloc(a, 200000 * sizeof(int)); 

Если это не удается, он возвращает пустой указатель, и a все еще указывает на исходный неизмененном кусок памяти (и, конечно, b пустой указатель).

Если это удастся, то b указывает на (возможно, вновь выделенный) кусок памяти, а значение a является неопределенным . Если бы он смог выделить новый кусок в том же месте, что и старый (увеличив или сократив кусок на месте), то b будет равен a - но, проверяя это или даже ссылаясь на значение a, имеет неопределенное поведение. Если он должен переместить кусок, то после копирования данных realloc выполнит эквивалент free(a). В любом случае, вероятно, лучше всего установить a в NULL, чтобы избежать случайного обращения к его (теперь неопределенному) значению.

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

+0

Realloc определенно освобождает исходный указатель, если он был успешным? У вас есть документация для этого? Я верю тебе, но я хотел бы иметь доказательства, если кто-то спросит. – velocirabbit

0

Если «а» точек действительный блок памяти (от предыдущего таНос/перераспределить/calloc), то перераспределить вызов будет пытаться обеспечить блок памяти с новым размером запрошенной
Вызов перераспределить должен быть формы *tmp = realloc (a ...

возвращаемое значение перераспределить должны быть проверены
Если NULL, перераспределить не смог выделить требуемую память, и это оставляет «а» в качестве действительного указателя
Вы тогда ответственны за обработку любые данные, на которые указывает «a» (сохранить его/отменить), и вы несете ответственность за free в памяти, на которую указывает «a»

Если вызов realloc был успешным, сделайте b = tmp, и теперь «b» - новый указатель на блок памяти - не имеет значения, совпадает ли начальное местоположение с «a» или другим. «a» больше не является допустимым указателем на выделение памяти, хотя дальнейшие ошибки будут зависеть от того, указывает ли «a» на память, принадлежащую вашей программе, или нет - в основном, если a == b, 'a' можно получить без очевидных ошибок.

После действительного *tmp = realloc(a ... & b = tmp;:
1) Если начальное местоположение перераспределяются памяти осталась неизменной: (а == б)
выделит требуемую память
, но запустить его под Valgrind и вы сообщения об ошибках:
Invalid свободный()/удалить/удалить []/перераспределить()
Адрес 0x51fc040 составляет 0 байт внутри блока размером 256 free'd
в этом случае перераспределить не может освободить память, на которую указывает 'a'
и снова в этом случае «а» по-прежнему можно получить, как это указатель на память, которая выделяется на вашу проножку

2) Если начальное местоположение перераспределяются памяти было изменено: (а = Ь!)
она не будет выполнена, и Valgrind показывает вывод так:
адрес: 0x1e89010
адрес Ъ: 0x7f2c5893c010
A после перераспределить: 0x1e89010
ошибок в `./ test15 ': realloc(): недействительный старый размер: 0x0000000001e89010

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

Другими словами, использование «a» после b = realloc(a ... - это неопределенное поведение.
выше комментарий был основан на использование следующего кода:

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

int main(void) 
{ 
    int *a = NULL, *b = NULL, *c = NULL; 

    /* initial allocation */ 
    a = malloc(256); 
    if(a == NULL) return (1); 
    printf("address of a: %p\n", a); 

    /* reallocation 'b' MAY be same as 'a' - try much larger allocations */ 
    void *tmp = realloc(a, 512); 
    if (!tmp) { 
     free(a); 
     return (1); 
    } else { 
     b = tmp; 
    } 
    printf("address of b: %p\n", b); 

    /* see what 'a' is now - this MAY crash the program*/ 
    printf("a after realloc: %p\n", a); 

    /* 'a' may not be a valid pointer - try using it for another realloc */ 
    c = realloc(a, 256); 
    /* Valgrind shows that memory could not be free'd or 'a' was not valid allocated memory */ 
    printf("return value of c: %p\n", c); 
    if (c != NULL) { 
     free(c); 
     printf("'c' allocated\n"); 
    } else { 
     free(b); 
     printf("'c' not allocated\n"); 
    } 

    return 0; 
} 
Смежные вопросы