Jump to content

    
Sign in to follow this  
777777

Использование static переменных

Recommended Posts

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

 

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

 

Share this post


Link to post
Share on other sites

Не совсем понятно из какого топика вырвана эта цитата, но вы исказили ее смысл, попустив слово "запись". По смыслу у Сергея Борща "выкинуть" не саму переменную, а выкинуть обращение к ней.

Share this post


Link to post
Share on other sites
Не совсем понятно из какого топика вырвана эта цитата,

Отсюда. Модераторы мудро решили ее закрыть.

но вы исказили ее смысл, попустив слово "запись".

Я не пропустил слово "запись"

По смыслу у Сергея Борща "выкинуть" не саму переменную, а выкинуть обращение к ней.

И обращение к ней (запись в нее) он не может выкинуть, так как должен знать, что результат может понадобиться при следующем обращении к ней. Если она static.

Share this post


Link to post
Share on other sites
И обращение к ней (запись в нее) он не может выкинуть, так как должен знать, что результат может понадобиться при следующем обращении к ней. Если она static.
static относится к storage-class specifier, т.е. влияет лишь на размещение и область видимости объекта, но не влияет на оптимизацию обращения к данному объекту. Для предотвращения оптимизации обращения к объекту применяется квалификатор volatile, который наряду с const и restrict относится к квалификаторам типа (type qualifiers). См. стандарт C99 (ISO/IEC 9899:1999).

C99_iso_iec_9899_1999.pdf

Share this post


Link to post
Share on other sites
Насчет любой переменной вы погорячились - значения static переменных сохраняются даже после выхода из функции и будут иметь его при повторном входе в эту функцию.
Ключевой момент - "Если результат такой записи не используется". Т.е. если вы не читате из такой переменной, а только пишете, то такая запись - бесполезная трата тактов процессора. Я не уверен, является ли присвоение адреса переменной указателю сигналом о том, что результат записи где-то потребуется и не смог найти упоминания об этом в стандарте.

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

 

Share this post


Link to post
Share on other sites

В целом я согласен с Сергей Борщ. Особенно зная его квалификацию. На практике, тем не менее, по крайней мере IAR AVR не выбрасывает массивы в которые идёт запись, но нет чтения. Я использовал такие массивы для отладки.Например, когда мне хотелось просмотреть результаты обработки аналоговых значений в окне watch. Интересно также что массив не выкидывается также и в случае если обращение осуществляется путём работы с указателем.

 

Думаю, что причина следующая. Массив размещается в принудительном порядке за счёт static, как писал rezident. А обращение к нему не выбрасывается так как на этапе линковки может быть прилинкован модуль ассемблерный или из другого языка, короче тот, где оптимизация затруднена.

 

 

PS: Тем не менее не вижу смысла в "хитрых" приёмах, если всё это можно сделать в рамках стандарта. Зачем передавать ссылку на массив размещённый на стэке? Это же ни в какие ворота! Вы этот массив даже в отладчике не увидите сразу после завершения его использования. А ваш указатель формально будет указывать на несуществующие данные. Это для примера.

Надо применять такие приёмы, которые обеспечивают железобетонный результат. А не "от компиляции к компиляции".

Чисто моё мнение конечно.

Share this post


Link to post
Share on other sites
На практике, тем не менее, по крайней мере IAR AVR не выбрасывает массивы в которые идёт запись, но нет чтения. Я использовал такие массивы для отладки.Например, когда мне хотелось просмотреть результаты обработки аналоговых значений в окне watch. Интересно также что массив не выкидывается также и в случае если обращение осуществляется путём работы с указателем.

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

Share this post


Link to post
Share on other sites

Ну раз пошла такая пьянка (с привлечением стандартов) то читаем:

6.2.4 Storage durations of objects

...

3 An object whose identifier is declared with external or internal linkage, or with the storage-class specifier static has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.

Таким образом, выкинуть компилятор ее не может при всем желании, так как Its lifetime is the entire execution of the program

 

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

А что по-вашему означает "использование" переменной? Как раз присвоение ее значения другой или передача как параметра в функцию. И выкидывать запись в нее компилятор тоже не может, так как это значение может понадобиться функции при последующих входах, она ведь полагается на то, что оно сохранится поскольку Its lifetime is the entire execution of the program.

 

это смотря как накрутить ему уровень оптимизации.

Оптимизация здесь совершенно ни при чем. Поведение static переменных регламентируются стандартом.

 

в режиме отладки обычно оптимизацию вообще отключают

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

 

ЗЫ. Тут недавно проскакивал пост в котором один такой юзер удивлялся почему AVR-ка не выполняет запись в EEPROM хотя у него все написано правильно:

EECR |= _BV(EEMPE);

EECR |= _BV(EEPE);

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

 

 

В целом я согласен с Сергей Борщ. Особенно зная его квалификацию.

Понравилос :)

По таким постам легко отличать верующих от атеистов. Для верующих сила авторитета сильнее стандарта Си. :)

Share this post


Link to post
Share on other sites
Таким образом, выкинуть компилятор ее не может при всем желании, так как Its lifetime is the entire execution of the program

 

Проверте сами, может ли компилятор выкинуть static перемную.

 

void
foo (void)
{
  static int a;
  a = 2;

  return;
}

 

Скомпилируйте код. Я бы на месте оптимизатора ее выкинул. Или по крайней мере запись в нее.

 

Анатолий.

Share this post


Link to post
Share on other sites
Проверте сами, может ли компилятор выкинуть static перемную.

#include <avr/io.h>

int main()
{
}

void foo (void)
{
  static int a;
  a = 2;

  return;
}

Даже в этой абсолютно бессмысленной программе он ее не выкидывает:

int main()
    {
    }
  56:    80 e0           ldi    r24, 0x00; 0
  58:    90 e0           ldi    r25, 0x00; 0
  5a:    08 95           ret

0000005c <foo>:

void foo (void)
{
  static int a;
  a = 2;
  5c:    82 e0           ldi    r24, 0x02; 2
  5e:    90 e0           ldi    r25, 0x00; 0
  60:    90 93 01 01     sts    0x0101, r25
  64:    80 93 00 01     sts    0x0100, r24

  return;
}

И даже выполняет в нее запись. Оптимизация -Os

Скомпилируйте код. Я бы на месте оптимизатора ее выкинул. Или по крайней мере запись в нее.

Как хорошо, что вы не на месте компилятора! :)

 

Share this post


Link to post
Share on other sites

ну и еще один пример тогда, конечно с -Os:

 

#include <avr/io.h>


int main()
{
}

volatile int volatile * p_a;

void foo (void)
{
  static int a;
  
  p_a = &a

  a = 2;


  sei ();
// переменая 'a'  через указатель 'p_a' используется в обработчике прерываня
// и ожидается что ее значение равно 2.
  cli ();

  a = 3;

  return;
}

 

Анатолий.

Edited by aesok

Share this post


Link to post
Share on other sites
Проверте сами, может ли компилятор выкинуть static перемную.

 

 

Скомпилируйте код. Я бы на месте оптимизатора ее выкинул. Или по крайней мере запись в нее.

"Не болтайте ерундой!" :) Не может и не должен компилятор выкидывать эту переменную. Она же static, хоть и объявлена внутри функции. Под нее обязательно выделяется ОЗУ и она очищается при стартапе, но область видимости этой переменной - только внутри данной функции. Присвоение значения тоже не выкидывается, т.к. по сути это инициализация ее.

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

void foo (void)
{ static int a;
  a = 1;
  a = 2;
  a = 3;
}

И вот в таком случае цикл с проверкой на условие скорее всего заменится на бесконечный цикл. Особенно, если foo1 и foo2 будут в разных единицах (файлах) компиляции.

static unsigned int cntr;

void foo1 (void)
{ 
    cntr--;
}

void foo2 (void)
{ 
  cntr=100;
  while (cntr!=0)
    foo1();
}

 

Share this post


Link to post
Share on other sites
#include <avr/io.h>

int main()
{
}

void foo (void)
{
  static int a;
  a = 2;

  return;
}

Даже в этой абсолютно бессмысленной программе он ее не выкидывает:

......

И даже выполняет в нее запись. Оптимизация -Os

 

Как хорошо, что вы не на месте компилятора! :)

добавьте ключик "-ffunction-sections -fdata-sections -Wl,--gc-sections" и от вашей статической переменной абсолютно ничего не останется.

Share this post


Link to post
Share on other sites
Понравилос :)

По таким постам легко отличать верующих от атеистов. Для верующих сила авторитета сильнее стандарта Си. :)

Ну так вот стандрат С говорит, что компилятор в процессе оптимизации обязан обеспечить observable behaviour такое же, как у абстрактной машины языка. Да, время жизни static-переменной по стандарту совпадает с временем жизни программы. Но и время жизни static-функции совпадает с ним же. А компилятор успешно выбрасывает, если она в пределах этого же файла не вызывается сама и её адрес никуда не передаётся. А всё почему - на всё то же o.b. не виляет - выбрасываем.

 

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

#include <avr/io.h>

void
foo (void)
{
    static int a;
    a = 2;
    return;
}


void
main(void)
{
    for(;;) {
        foo();
        PORTB ^= 0x01;
    }
}

avr-gcc -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -mmcu=atmega8 static.c

avr-objdump -d a.out >static.dump

  ...
0000005e <main>:
  5e:    91 e0           ldi    r25, 0x01; 1
  60:    88 b3           in    r24, 0x18; 24
  62:    89 27           eor    r24, r25
  64:    88 bb           out    0x18, r24; 24
  66:    fc cf           rjmp    .-8     ; 0x60 <main+0x2>
  ...

 

Или так

avr-gcc -Os -fwhole-program -S -mmcu=atmega8 static.c

.global    main
    .type    main, @function
main:
/* prologue: function */
/* frame size = 0 */
    ldi r25,lo8(1)
.L2:
    in r24,56-32
    eor r24,r25
    out 56-32,r24
    rjmp .L2

 

А то, что не выкинул при компиляции одной только функции foo() - ну так есть ещё над чем работать. Поскольку переменная не volatile, внутри функци не считывается и её адрес никуда не передаётся — она может быть выброшена. Не в этой версии компилятора, так в следующей.

 

С массивами, в которые из основного кода ведётся только запись, а из прерывания — только чтение, тоже не всё так просто. Если массив не volatile, то компилятор имеет право переставить запись в него и взведение volatile-флага, информирующего прерывание о том, что запись произведена. В результате прерывание может начать читать буфер до того, как в него реально произведена запись.

Share this post


Link to post
Share on other sites
Она же static, хоть и объявлена внутри функции.
Да и хрен на нее, хоть и static. На то и оптимизатор. Пользы от переменной никакой, накой ее держать?

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this