Как получить доступ к локальной переменной из другой функции, используя указатели?

Могу ли я получить доступ к локальной переменной в другой функции? Если так, то как?

void replaceNumberAndPrint(int array[3]) {
    printf("%i\n", array[1]);
    printf("%i\n", array[1]);
}

int * getArray() {
    int myArray[3] = {4, 65, 23};
    return myArray;
}

int main() {
    replaceNumberAndPrint(getArray());
}

Вывод фрагмента кода выше:

65
4202656

Что я делаю неправильно? Что означает "4202656"?

Должен ли я скопировать весь массив в replaceNumberAndPrint() функция, чтобы иметь возможность получить к нему доступ больше, чем в первый раз?

9 ответов

Решение

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

На практике происходит то, что printf перезаписывает часть стека, используемую myArray и тогда он содержит некоторые другие данные.

Чтобы исправить ваш код, вы должны либо объявить массив в области, которая живет достаточно долго (main функция в вашем примере) или выделить его в куче. Если вы размещаете его в куче, вам нужно освободить его вручную или в C++ с помощью RAII.

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

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

Когда вы обращаетесь к массиву в функции replaceNumberAndPrint, результат не определен. Тот факт, что он работает впервые, - просто счастливое совпадение. Возможно, область памяти, на которую вы указываете, не выделена в стеке и все еще правильно установлена ​​для первого вызова, но вызов printf затем перезаписывает это, помещая значения в стек во время его работы, поэтому второй вызов printf отображает что-то разные.

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

Попробуйте что-нибудь подобное. То, как вы это делаете, "убивает" myArray причина, если это определено локально.

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

void replaceNumberAndPrint(int * array) {
 printf("%i\n", array[0]);
 printf("%i\n", array[1]);
 printf("%i\n" , array[2]);
 free(array);
}

int * getArray() {
 int * myArray = malloc(sizeof(int) * 3);
 myArray[0] = 4;
 myArray[1] = 64;
 myArray[2] = 23;
 //{4, 65, 23};
 return myArray;
}

int main() {
 replaceNumberAndPrint(getArray());
}

Больше: http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/

Редактировать: Как правильно указали в комментариях: лучший способ сделать это будет так:

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

void replaceNumberAndPrint(int * array) {
    if(!array)
        return;

    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n" , array[2]);
}

int * createArray() {
    int * myArray = malloc(sizeof(int) * 3);

    if(!myArray)
        return 0;

    myArray[0] = 4;
    myArray[1] = 64;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int * array = createArray();
    if(array)
    {
        replaceNumberAndPrint(array);
        free(array);
    }
    return 0;
}

myArray выходит из области видимости, как только вы покидаете getArray. Вместо этого вам нужно выделить для него место в куче.

Локальные переменные выходят из области видимости по возвращении, поэтому вы не можете вернуть указатель на локальную переменную.

Вы должны выделить его динамически (в куче), используя malloc или же new, Пример:

int *create_array(void) {
    int *array = malloc(3 * sizeof(int));
    assert(array != NULL);
    array[0] = 4;
    array[1] = 65;
    array[2] = 23;
    return array;
 }
 void destroy_array(int *array) {
     free(array);
 }
 int main(int argc, char **argv) {
     int *array = create_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     destroy_array(array);
     return 0;
 }

В качестве альтернативы, вы можете объявить массив как статический, помня семантику. Пример:

int *get_array(void) {
    static int array[] = { 4, 65, 23 };
    return array;
 }
 int main(int argc, char **argv) {
     int *array = get_array();
     for (size_t i = 0; i < 3; ++i)
         printf("%d\n", array[i]);
     return 0;
 }

Если вы не знаете, что static значит, прочитайте этот вопрос и ответ.

Правильный способ сделать это заключается в следующем:

struct Arr {
   int array[3];
};
Arr get_array() {
   Arr a;
   a.array[0] = 4;
   a.array[1] = 65;
   a.array[2] = 23;
   return a;
}
int main(int argc, char **argv) {
   Arr a = get_array();
   for(size_t i=0; i<3; i++)
       printf("%d\n", a.array[i]);
   return 0;
}

Чтобы понять, почему вам нужно это сделать, вам нужно знать, как работает sizeof(массив). C (и, следовательно, C++) изо всех сил пытается избежать копирования массива, и вам нужна структура, чтобы пройти мимо этого. Почему копирование необходимо из-за областей действия - область действия функции get_array() исчезает, и все значения, все еще необходимые из этой области, необходимо будет скопировать в вызывающую область.

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

C++ решение:

"Могу ли я получить доступ к локальной переменной в другой функции? Если да, то как?"

Ответ - нет, не после завершения функции. Локальные переменные уничтожаются в этой точке.

В C++ способ иметь дело с возвращаемыми массивами - это управлять ими в контейнере, таком как std:: array (фиксированный размер) или std:: vector (динамический размер).

Например:

void replaceNumberAndPrint(const std::array<int, 3>& array) {
    printf("%i\n", array[0]);
    printf("%i\n", array[1]);
    printf("%i\n", array[2]);
}

std::array<int, 3> getArray() {
    std::array<int, 3> myArray = {4, 65, 23};
    return myArray;
}

Во второй функции возвращаемое значение оптимизируется компилятором, поэтому вы не платите цену за фактическое копирование массива.

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

int* getArray(int size) {
    int *myArray = (int*)malloc(size*sizeof(int));
    myArray[0] = 4;
    myArray[1] = 65;
    myArray[2] = 23;
    return myArray;
}

int main() {
    int i;
    int *vector = getArray(3);
    for(i=0;i<3;i++)
    {
        printf("%i\n",vector[i]);
    }
    getch();
    return 0;
}

Этот код напечатает все элементы массива, и перезаписи не произойдет.

Статический.. или.. Глобальный в вашем.c сделает свое дело;)

Однако все время программа будет занимать эти 3 байта, НО вы избегаете выполнения malloc для таких простых вещей, как это (malloc рекомендуется для больших массивов)

С другой стороны, если внешняя функция изменяет указатель, тогда внутренний myArray будет изменен, потому что он указывает на него, что это

int myArray[3];
int * getArray() {
    myArray[0] = 4;
    myArray[1] = 65;
    myArray[2] = 23;
    return myArray;
}
Другие вопросы по тегам