Как переназначить указатель на меньший массив в C? - PullRequest
0 голосов
/ 22 мая 2019

Есть ли способ иметь указатель в C, который указывает на массив, переназначать его так, чтобы он указывал на массив меньшего размера?Например, если у меня есть указатель ptr, который первоначально указывает на массив s размера 3, а также существует массив s1 размера 2, могу ли я переназначить ptr, чтобы он указывал на s и, таким образом, указывает на меньший массив?

Я попробовал следующий код:

void changePtr(double* ptr);

int main()
{
    double *ptr;
    ptr = (double*)malloc(sizeof(double)*3);
    double s[3]={1,2,3};
    ptr=s;
    changePtr(ptr);

    return 0;
}

void changePtr(double* ptr) {

    double s1[2]={4,5};
    free(ptr);
    ptr = (double*)malloc(sizeof(double)*2);
    ptr=s1;
}

Но когда я запускаю это в VIsual Studio, я получаю ошибку в free(ptr), которая говорит Debug Assertion failed! Expression: _CrtIsValidHeapPointer(block)

Почему это так?

РЕДАКТИРОВАТЬ

Новый код:

void changePtr(double* ptr);

int main()
{
    double *ptr;
    ptr = (double*)malloc(sizeof(double)*3);
    double *s;
    s = (double*)malloc(sizeof(double)*3);
    s[0]=1; s[1]=2; s[0]=3; 
    for (int i=0; i<3; i++){
        ptr[i]=s[i];
    }

    changePtr(ptr);

    for (int i=0; i<2; i++){
        printf("%f\t", ptr[i]);
    }


    free(ptr);
    free(s);

    return 0;
}

void changePtr(double* ptr) {
    free(ptr);
    double *s1;
    s1 = (double*)malloc(sizeof(double)*2);
    s1[0]=11; s1[1]=12;

    ptr = (double*)malloc(sizeof(double)*2);

    for (int i=0; i<2; i++){
        ptr[i]=s1[i];
    }
}

Но я получаю такие значения мусора, как-1456815990147....1 когда я печатаю значения ptr после вызова changePtr.Почему это?

Ответы [ 2 ]

2 голосов
/ 22 мая 2019

Редактировать 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)) во время выполнения - программно не возможно , вы должны явно передать размер массива функции, если вы хотите выполнить некоторые операции цикла.

1 голос

Ну, когда вы делаете

double s[3]={1,2,3};

Эта память не управляется вами, поэтому вы не можете ее освободить. Вы можете использовать free только тогда, когда вы выделяете память, используя malloc или calloc (то есть динамически выделяемая память). Кроме того, у вас есть утечка памяти, потому что

ptr=(double*)malloc(sizeof(double)*3); // here you allocate memory 
...
ptr = s; // here you change the pointer address but allocated memory is never freed

решение было бы использовать цикл for для копирования значений из s в ptr и сделать то же самое в changePtr для копирования значений из s1 в ptr.

...