Редактировать 1
На ваш оригинальный вопрос:
Краткий ответ: Да, вы можете переназначить указатель на меньший массив в C.
Длинный ответ: короткий ответ вводит в заблуждение. Массивы также просто указатели на C. И для лучшего понимания этой концепции, вы можете прочитать эту ветку , особенно взгляните на этот ответ .
Проблема в том, что вы пытаетесь освободить память на stack
.
Всякий раз, когда вы создаете локальную переменную, например, int a = 5
, она помещается в стек, то есть эта переменная получает память, выделенную в стеке, и после завершения вызова функции (main()
) память освобождается - все это происходит автоматически!
В вашем случае вот что происходит:
// memory is allocated automatically on stack for the local variable "s"
double s[3]={1,2,3};
// assign the stack address of "s" to "ptr"
ptr = s;
// de-allocate the managed stack address of "s", which is NOT allowed!
free(ptr);
Создание динамической памяти, например через malloc
, с другой стороны, происходит в памяти heap
, которая должна управляться программистом, то есть выделение (malloc()
) и удаление (free()
) памяти разрешено и должно выполняться с большой осторожностью.
Таким образом, в вашей программе, когда он достигает этой строки free(ptr);
, утверждение не выполняется, поскольку целевой адрес памяти не соответствует требованию -> IsValidHeapPointer
: Нет ptr
не является действительным указателем кучи, потому что в этот момент он хранит адрес стека переменной s
. :)
Редактировать 2
Я получаю значения мусора, такие как -1456815990147 .... 1, когда я печатаю значения ptr после вызова changePtr. Почему это?
Это потому, что вы сначала освобождаете память ptr
в changePtr(ptr)
, то есть содержимое ptr
записывается с некоторым значением «мусора».
А затем вы выделяете новую память и присваиваете ей ptr
:
ptr = (double *) malloc(sizeof(double) * 2);
После этого изменения, которые вы вносите с помощью ptr
(т.е. ptr[i] = 12
), не влияют на предыдущий адрес, который удерживался ptr
. Итак, просто удалите free(ptr)
и вышеприведенную строку, они вам не нужны.
Редактировать 3
Так что ptr
теперь содержит только 2 значения, верно? После того, как я удалил ту строку, которую вы упомянули, а затем распечатал значения в ptr, он показывает правильные значения для ptr [0] и ptr [1], а затем значение мусора для ptr [2].
Правильно. ptr[2]
освобождается раньше, поэтому, если вы не назначите этому адресу новое значение, оно останется мусором. Также будьте осторожны после освобождения памяти кучи. Вы не должны использовать указатель для доступа к памяти или присвоения ей нового значения. Считайте ваш указатель «void» после того, как вы назовете free
.
Когда я пытался напечатать его размер с помощью printf ("% d", sizeof (ptr)), он показывает 8, а printf ("% d", sizeof (ptr) / sizeof (ptr [0]) показывает 1.
ptr
- указатель. sizeof(ptr)
возвращает 8
байтов, это размер адреса, который содержит указатель в вашей системе. Таким образом, это не размер массива ... Кроме того, определение размера указателя (который указывает на массив и передается функции в качестве ссылки - как вы сделали changePtr(ptr)
) во время выполнения - программно не возможно , вы должны явно передать размер массива функции, если вы хотите выполнить некоторые операции цикла.