Как мы можем получить доступ к автоматическим и статическим переменным вне их области видимости в C? - PullRequest
1 голос
/ 27 марта 2019

Авто и статические переменные имеют область действия, ограниченную блоком, в котором они определены.Поскольку в стеке определены автоматические переменные, при выходе из функции стек уничтожается и освобождается память для автоматической переменной.Но я где-то читал: «Тем не менее, к ним можно получить доступ и вне их области применения, используя приведенную здесь концепцию указателей, указывая на очень точное место в памяти, где находятся переменные».Это правильно?

Кроме того, статические переменные определены в разделе данных, поэтому он сохраняет свое существование до конца программы.Область действия находится внутри блока, в котором она определена.Есть ли способ, с помощью которого мы можем получить доступ к статической переменной из любой другой функции?Кроме того, есть ли способ получить доступ к статической переменной из любого другого файла?

Ответы [ 3 ]

3 голосов
/ 27 марта 2019

Вот очень простой пример:

void print_msg(const char* msg) {
  printf("The message is: %s\n", msg);
}

int main(void) {
  char m[] = "Hello, world!";
  print_msg(m);
}

Здесь m - это автоматическая переменная, которая не входит в область действия print_msg. Но print_msg явно имеет доступ к его значению.

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

2 голосов
/ 27 марта 2019

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

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

int* func()
{
        static int a = 0;
        a++;
        printf("a in func = %d\n", a);
        return &a;
}

int main()
{
        int *p;
        p = func();
        printf("a in main from ptr : %d\n", *p);
        *p++;
        p = func();
        return 0;
}

Как видно из примера, func() возвращает указатель на объявленную им статическую переменную и любого, кто хочет получить доступ к переменной a, можете использовать этот указатель.ПРИМЕЧАНИЕ: мы можем сделать это только потому, что срок действия статической переменной ограничен программой.Теперь, независимо от того, находится ли статическая переменная в другой функции или в другом файле, пока вы можете каким-то образом получить указатель на эту статическую переменную, вы можете использовать ее.

Теперь перейдем к случаюавтоматическая переменная.

Что произойдет, если вы запустите вышеуказанную программу, изменив a с static на auto?вы увидите, что при компиляции выдается предупреждение warning: function returns address of local variable [-Wreturn-local-addr], а при выполнении мы получаем segmentation fault.Это объясняется тем, что автоматическая переменная существует только в своей области видимости, то есть, пока выполняется функция func(), переменная a имеет выделенную память для себя.Как только функция завершается, память, выделенная для переменной a, освобождается, и поэтому значение, на которое указывает указатель p, находится в каком-то нераспределенном месте памяти (что приводит к ошибке сегментации).

0 голосов
/ 27 марта 2019

Обратите внимание, как правильно отмечают комментарии, я делаю здесь допущение, предполагая, что простейший случай вызова другой функции не в том, о чем идет речь. Это предположение не было (пока) подтверждено или отклонено ОП. Этот случай обсуждается, например, в ответе Ричи.

Существование автоматических переменных существует не только для того, чтобы «в пределах» их области действия (упрощенно: только код между одним и тем же включением {} может использовать их идентификатор), они также ограничены «во время» своей «хронологической области», то есть их время жизни (упрощается после начала выполнения кода в функции и завершения его выполнения). Доступ к ячейке памяти переменной можно получить через указатель, для которого был задан их адрес (что возможно только в пределах их области действия, поскольку необходим доступ через их идентификатор), если это происходит в течение срока их службы, да.

Но как найти этот указатель где-нибудь еще?
Возможно, будучи записанным (изнутри их области видимости и в течение их жизни) в глобальную переменную.

Но какой «другой» код должен использовать это значение? (помните, что здесь я ставлю вызов функций)
Это требует многопоточности / многозадачности / многозадачности. Допустим, существует процедура обработки прерывания. Он должен видеть то же адресное пространство, что и область действия переменных, т. Е. Никакие блоки управления памятью не мешают использовать магию виртуальной памяти. Это не так для многих реализаций нескольких типов, но, разумеется, для некоторых из них, поэтому давайте продолжим.

Этот воображаемый ISR должен был бы обеспечить доступ к автоматической переменной только в то время, когда она действительно существует (то есть в течение ее времени жизни), в противном случае он в значительной степени получал бы доступ к тому, что фактически является бессмысленной случайной ячейкой памяти. И это предполагает, что ISR фактически разрешен / может получить доступ к этой памяти. Даже без MMU есть реализации, которые могут / будут иметь исключения.
Это вводит необходимость в механизмах синхронизации, например, семафоры.

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

Аналогично для статических переменных.

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

В случае «статических» переменных, понимаемых как переменные, ограниченные областью видимости файла, указатель все равно должен быть перенесен из области видимости файла.
Это просто победило бы то, что, вероятно, является точкой ограниченной области видимости файла. Но я мог бы представить какую-то схему привилегий доступа, как в «Вот ключ к хранилищу. Обращайтесь осторожно».

Как уже упоминалось в начале этого ответа, я откладываю вызов других функций. Да, самый простой способ выйти из области действия функции - вызвать другую. Если эта другая функция имеет параметр указателя, она может использовать ее для доступа к чтению и записи для автоматической переменной вызывающей функции. Это обычный случай параметров вызова по ссылке, поддерживаемых C.
Вызов функции также предоставляет другой, еще более простой способ доступа к чтению значения автоматической переменной вызывающей функции, хотя не к записи и к фактическому доступу к самой автоматической переменной, только с использованием ее значения.Этот способ является тривиальным механизмом параметра вызова по значению, он даже не требует указателя.Оба способа (параметр call-by-reference и параметр call-by-value) удобно гарантируют, что значение не изменится во время выполнения вызываемой функции.(на этот раз я отложил в сторону многопоточный случай, потому что это обсуждается в основной части этого ответа).

...