Это определение «статических» переменных неверно, вводит в заблуждение или нет? - PullRequest
0 голосов
/ 16 ноября 2018

Согласно https://www.learn -c.org / ru / Static :

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

Приведены два примера кода.
Во-первых, пример, когда локальная переменная int count удаляется из памяти после того, как функция runner() возвращает:

#include<stdio.h>
int runner() {
    int count = 0;
    count++;
    return count;
}

int main()
{
    printf("%d ", runner());
    printf("%d ", runner());
    return 0;
}

Вывод: 1 1.

В следующем примере count static:

#include<stdio.h>
int runner()
{
    static int count = 0;
    count++;
    return count;
}

int main()
{
    printf("%d ", runner());
    printf("%d ", runner());
    return 0;
}

Выходные данные на этот раз: 1 2.

count увеличено до 2 в этом примере, потому что оно не было удалено из памяти при возврате runner().
... НО, похоже, это не относится к утверждению в начале страницы о доступе к статическим переменным в любом месте файла. Два примера показывают только, что static позволял count оставаться в памяти при нескольких вызовах до runner() (и что он не установлен в 0 для каждого вызова). Они не показывают, можно ли получить доступ к count « в любом месте файла », потому что main() просто печатает все, что возвращает runner().

Чтобы проиллюстрировать свою точку зрения, я сделал пример, показывающий, что static не делает count() доступным где-либо внутри файла:

#include<stdio.h>
void runner()
{
    static int count = 0;
    count++;
    return;
}

int main()
{
    runner();
    printf("%d ", count);
    runner();
    printf("%d ", count);
    return 0;
}

Вывод:

prog.c: In function 'main':
prog.c:12:23: error: 'count' undeclared (first use in this function)
         printf("%d ", count);
                       ^
prog.c:12:23: note: each undeclared identifier is reported only once for each function it appears in

Это то, что я ожидал.

Я сделал еще один пример, в котором используется x, к которому могут обращаться как runner(), так и main():

#include<stdio.h>
int x = 0;
void runner()
{
    static int count = 0;
    count++;
    x = count;
    return;
}

int main()
{
    runner();
    printf("%d ", x);
    runner();
    printf("%d ", x);
    return 0;
}

Вывод: 1 2.

Цитата вверху этого вопроса неверна, вводит в заблуждение или нет Я неправильно понимаю проблему семантики?

Ответы [ 4 ]

0 голосов
/ 16 ноября 2018

Этот учебник очень запутан, и его необходимо удалить из Интернета.


C имеет два разных, но связанных между собой термина: scope и длительность хранения .Область действия определяет, где переменная может быть доступна через ее имя.Срок хранения определяет, как долго переменная будет сохранять свое значение.

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

Область действия остается той же самой: переменная имеет локальную область действия для функции int runner() и не может быть доступна по ее имени за пределами этой функции.Ключевое слово static не влияет на область действия.

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

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

Принимая во внимание, что переменные с внешней связью явно доступны по всей программе, используя ключевое слово extern.Вы не можете объединить внутреннюю и внешнюю связь - и все переменные, объявленные как static, получат внутреннюю связь.Это означает, что вы не сможете получить к ним доступ из других файлов .c.

Объявляя переменную static, вы даете ей статическое время хранения и внутреннюю связь.

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

0 голосов
/ 16 ноября 2018

TL; DR - переменная со статическим хранилищем живет в течение всего выполнения программы, и любой доступ к ней будет действительным.Доступ к нему возможен из любой части или нет, зависит от scope определения.

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


У вас уже был свой ответ, просто чтобы добавить мои два цента (используя авторитетные слова из стандарта):

static - спецификатор класса хранения, он помогает определить продолжительность хранения.

Цитирование C11, глава §6.2.4

Объект имеет срок хранения , который определяет его время жизни .[....]

Тогда

время жизни объекта - это часть выполнения программы, в течение которой хранение гарантированно будетзарезервировано для этого.Объект существует, имеет постоянный адрес 33) и сохраняет свое последнее сохраненное значение в течение всего срока его службы.[...]

Итак, для переменных с статическая продолжительность хранения ,

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


'пустой разговор, покажите мне код:

Итак, для сравнения давайте посмотрим фрагменты кода:

  • Фрагмент один: Неверный код
#include<stdio.h>
char *func()
{
    char arr [ ] = "Hello!";
    return arr;
}

int main()
{        
    printf("%s ", func()); // you're accessing `arr`, some or other way!!
    return 0;
}

Здесь возвращаемый указатель указывает на недопустимую память, как и в конце вызова функции, автоматическая локальная переменная arr уничтожается (время жизни истекло), а возвращенный адрес указывает на недопустимую память.

Таким образом, даже если у вас есть способ доступа к arr, этот доступ больше недействителен.

  • Фрагмент 2: действительный код
#include<stdio.h>
char *func()
{
    static char arr [ ] = "Hello!";
    return arr;
}

int main()
{        
    printf("%s ", func()); // you're accessing `arr`, some or other way!!
    return 0;
}

это совершенно корректный код, поскольку он делает arr «доступным» вне области его определения из-за статической длительности хранения .

0 голосов
/ 16 ноября 2018

Цитата вводит в заблуждение область действия и время жизни .

* область действия переменной описывает, где к переменной можно получить доступ по имени . время жизни переменной или, точнее, объекта , описывает, когда объект действителен.Для нестатической локальной переменной и ее область действия, и время ее жизни - это блок, в котором она определена.

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

#include<stdio.h>
int *runner()
{
    static int count = 0;
    count++;
    return &count;    // ok, count has full program lifetime
}

int main()
{
    int *p = runner();
    printf("%d ", *p);
    p = runner();
    printf("%d ", *p);
    return 0;
}

Вывод:

1 2

Если вы попытаетесь сделать это с нестатической переменной:

#include<stdio.h>
int *runner()
{
    int count = 0;
    count++;
    return &count;    // BAD: count no longer exists when function returns
}

int main()
{
    int *p = runner();
    printf("%d ", *p);   // UNDEFINED BEHAVIOR
    p = runner();
    printf("%d ", *p);   // UNDEFINED BEHAVIOR
    return 0;
}

Вы вызываете неопределенное поведение с помощью указателя на объект, который больше не существует.

0 голосов
/ 16 ноября 2018

Вы правы, а цитата неверна. Объявление переменной в функциональном блоке static увеличивает ее время жизни, а не область видимости. (Область действия - это часть кода, в которой вы можете использовать имя.)

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

Вероятно, пора прекратить использование этого сайта.

...