Массив не стирается после завершения вызова функции - PullRequest
0 голосов
/ 04 марта 2020

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

int * secret()
{
    int arr[10]={0};
    arr[0]=9999;
    return arr;
}

int main() {
    printf("%d",secret()[0]);
    return 0;
}

, и результат составил 9999, что не соответствует тому, чему меня учили.

Ответы [ 4 ]

3 голосов
/ 04 марта 2020

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

Это вводящая в заблуждение характеристика. Если ваш инструктор использовал эту конкретную формулировку c, то он оказал вам медвежью услугу. Однако это не совсем неправильно, в зависимости от того, как его интерпретируют.

В спецификации языка сказано, что время жизни объекта, объявленного без спецификатора класса хранения внутри блока (такого как блок) служит в качестве тела функции в определении функции), когда заканчивается выполнение самого внутреннего содержащего блока. Вы можете охарактеризовать это как такие объекты, которые «стираются» в смысле стирания из существования , но не в смысле очистки их содержимого.

результат был 9999, что не соответствует тому, чему меня учили.

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

В любом случае попытка получить доступ к объекту, срок жизни которого истек, приводит к неопределенному поведению . Более того, когда время жизни объекта заканчивается, значения любых указателей на этот объект становятся неопределенными. Это означает, что вывод вашей программы, на самом деле не может , противоречит тому, что спецификация языка говорит о ситуации, потому что вывод программы не определен. Любые выходные данные или их отсутствие будут одинаково соответствовать семантике языка C. Если мы предполагаем, что ваш инструктор пытался передать характеристику, соответствующую спецификации, то они также не противоречат.

2 голосов
/ 04 марта 2020

В вашем примере локальная переменная "arr" размещается в стеке. Когда вы возвращаетесь из secret (), программа освобождает эту часть стека для использования следующей вызываемой функцией, но возвращаемый указатель по-прежнему указывает на эту область памяти. Он все еще существует, как и до тех пор, пока не появится другая функция, которая использует эту часть стека.

Ваш учитель был прав, если вы предполагали, что они подразумевали, что память не должна использоваться после возврата, но, в частности, стек не стирается как часть возврата. Это связано с тем, что по определению локальные переменные больше не находятся в области видимости и не могут находиться в каком-либо конкретном состоянии. Нет смысла использовать циклы процессора для его удаления.

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

0 голосов
/ 04 марта 2020

В большинстве реализаций функция epilogue просто меняет указатель стека, но не очищает фактические данные. Если вам нужно очистить данные, вам нужно сделать это самостоятельно.

void foo()
{
    char verySecret[5000];
    char verySecret2[5000];
    char verySecret4[5000];

    /* do something */

    /* now purge the data */
    purge(verySecret,0, sizeof(verySecret));
    purge(verySecret2,0, sizeof(verySecret2));
    purge(verySecret4,0, sizeof(verySecret4));
}

0 голосов
/ 04 марта 2020

не стирается. Это просто освобождено. В нем говорится, что данные бесплатны для использования, но они остаются там до тех пор, пока другая программа не перезапишет их. Когда данные больше не используются, система освобождает память от программы. Это только делает его свободным для использования другими программами, но не обязательно освобождает его. Вы можете увидеть этот эффект:

char *String = malloc(100);
puts(String); // It won't always print nothing. Memory sometimes has "garbage" from processes that used it before.
free(String);

В зависимости от реализации, вышеприведенный код печатает «мусор» из других программ, которые использовали память раньше вашей. Хотя не всегда печатается ничего, если «мусор» начинается с нуля. Это связано с тем, что нет необходимости стирать его, как только он будет освобожден, если он, вероятно, будет перезаписан следующей программой, которая использует его в любом случае. Если вы хотите стереть его, вам нужно перезаписать его самостоятельно:

int * secret()
{
    int *arr = calloc(10, sizeof int);
    arr[0]=9999;
    return arr;
}

int main() {
    int *secretptr = secret();
    printf("%d",secretptr[0]);
    memset(secretptr, 0, 10*sizeof(int));//overrite it with zeros
    free(secretptr);
    return 0;
}

ПРИМЕЧАНИЕ. Разыменование возвращенного локального указателя из функции - НЕПРЕДЕЛЕННОЕ ПОВЕДЕНИЕ

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