|
Использование static переменных |
|
|
|
Oct 7 2010, 16:32
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(rezident @ Oct 7 2010, 20:14)  Не совсем понятно из какого топика вырвана эта цитата, Отсюда. Модераторы мудро решили ее закрыть. Цитата(rezident @ Oct 7 2010, 20:14)  но вы исказили ее смысл, попустив слово "запись". Я не пропустил слово "запись" Цитата(rezident @ Oct 7 2010, 20:14)  По смыслу у Сергея Борща "выкинуть" не саму переменную, а выкинуть обращение к ней. И обращение к ней (запись в нее) он не может выкинуть, так как должен знать, что результат может понадобиться при следующем обращении к ней. Если она static.
|
|
|
|
|
Oct 7 2010, 16:43
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(777777 @ Oct 7 2010, 22:32)  И обращение к ней (запись в нее) он не может выкинуть, так как должен знать, что результат может понадобиться при следующем обращении к ней. Если она static. static относится к storage-class specifier, т.е. влияет лишь на размещение и область видимости объекта, но не влияет на оптимизацию обращения к данному объекту. Для предотвращения оптимизации обращения к объекту применяется квалификатор volatile, который наряду с const и restrict относится к квалификаторам типа ( type qualifiers). См. стандарт C99 (ISO/IEC 9899:1999).
|
|
|
|
|
Oct 7 2010, 17:11
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(777777 @ Oct 7 2010, 18:41)  Насчет любой переменной вы погорячились - значения static переменных сохраняются даже после выхода из функции и будут иметь его при повторном входе в эту функцию. Ключевой момент - "Если результат такой записи не используется". Т.е. если вы не читате из такой переменной, а только пишете, то такая запись - бесполезная трата тактов процессора. Я не уверен, является ли присвоение адреса переменной указателю сигналом о том, что результат записи где-то потребуется и не смог найти упоминания об этом в стандарте. В вашем случае я могу предположить еще один сценарий, который также сделает вашу программу неработоспособной - компилятор имеет право сделать запись в вашу переменную непосредственно перед выходом из функции. А вы пытаетесь использовать содержимое этой переменной в прерывании до выхода из функции.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Oct 7 2010, 18:19
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
В целом я согласен с Сергей Борщ. Особенно зная его квалификацию. На практике, тем не менее, по крайней мере IAR AVR не выбрасывает массивы в которые идёт запись, но нет чтения. Я использовал такие массивы для отладки.Например, когда мне хотелось просмотреть результаты обработки аналоговых значений в окне watch. Интересно также что массив не выкидывается также и в случае если обращение осуществляется путём работы с указателем.
Думаю, что причина следующая. Массив размещается в принудительном порядке за счёт static, как писал rezident. А обращение к нему не выбрасывается так как на этапе линковки может быть прилинкован модуль ассемблерный или из другого языка, короче тот, где оптимизация затруднена.
PS: Тем не менее не вижу смысла в "хитрых" приёмах, если всё это можно сделать в рамках стандарта. Зачем передавать ссылку на массив размещённый на стэке? Это же ни в какие ворота! Вы этот массив даже в отладчике не увидите сразу после завершения его использования. А ваш указатель формально будет указывать на несуществующие данные. Это для примера. Надо применять такие приёмы, которые обеспечивают железобетонный результат. А не "от компиляции к компиляции". Чисто моё мнение конечно.
|
|
|
|
|
Oct 8 2010, 01:01
|
Местный
  
Группа: Свой
Сообщений: 358
Регистрация: 27-06-06
Из: Новосибирск
Пользователь №: 18 410

|
Цитата(SasaVitebsk @ Oct 8 2010, 01:19)  На практике, тем не менее, по крайней мере IAR AVR не выбрасывает массивы в которые идёт запись, но нет чтения. Я использовал такие массивы для отладки.Например, когда мне хотелось просмотреть результаты обработки аналоговых значений в окне watch. Интересно также что массив не выкидывается также и в случае если обращение осуществляется путём работы с указателем. это смотря как накрутить ему уровень оптимизации. в режиме отладки обычно оптимизацию вообще отключают, тогда все переменные считаются как volatile.
|
|
|
|
|
Oct 8 2010, 09:46
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Ну раз пошла такая пьянка (с привлечением стандартов) то читаем: Цитата 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 Цитата(Сергей Борщ @ Oct 7 2010, 21:11)  Ключевой момент - "Если результат такой записи не используется". Т.е. если вы не читате из такой переменной, а только пишете, то такая запись - бесполезная трата тактов процессора. Я не уверен, является ли присвоение адреса переменной указателю сигналом о том, что результат записи где-то потребуется и не смог найти упоминания об этом в стандарте. А что по-вашему означает "использование" переменной? Как раз присвоение ее значения другой или передача как параметра в функцию. И выкидывать запись в нее компилятор тоже не может, так как это значение может понадобиться функции при последующих входах, она ведь полагается на то, что оно сохранится поскольку Its lifetime is the entire execution of the program. Цитата(Itch @ Oct 8 2010, 05:01)  это смотря как накрутить ему уровень оптимизации. Оптимизация здесь совершенно ни при чем. Поведение static переменных регламентируются стандартом. Цитата(Itch @ Oct 8 2010, 05:01)  в режиме отладки обычно оптимизацию вообще отключают Оптимизацию отключают только... альтернативно одаренные люди. Мне кажется что сама возможность ее отключения оставлена для того, чтобы пользователь мог отключив ее убедиться что виноват не оптимизатор, а его программа. ЗЫ. Тут недавно проскакивал пост в котором один такой юзер удивлялся почему AVR-ка не выполняет запись в EEPROM хотя у него все написано правильно: EECR |= _BV(EEMPE); EECR |= _BV(EEPE); правда у него зачем-то оптимизация была отключена в результате для этого кода генерировался десяток команд, а EEMPE действительно лишь на протяжении четырех клоков... Цитата(SasaVitebsk @ Oct 7 2010, 22:19)  В целом я согласен с Сергей Борщ. Особенно зная его квалификацию. Понравилос  По таким постам легко отличать верующих от атеистов. Для верующих сила авторитета сильнее стандарта Си.
|
|
|
|
|
Oct 8 2010, 10:02
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(777777 @ Oct 8 2010, 13:46)  Таким образом, выкинуть компилятор ее не может при всем желании, так как Its lifetime is the entire execution of the program Проверте сами, может ли компилятор выкинуть static перемную. Код void foo (void) { static int a; a = 2;
return; } Скомпилируйте код. Я бы на месте оптимизатора ее выкинул. Или по крайней мере запись в нее. Анатолий.
|
|
|
|
|
Oct 8 2010, 10:16
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(aesok @ Oct 8 2010, 14:02)  Проверте сами, может ли компилятор выкинуть 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 Цитата(aesok @ Oct 8 2010, 14:02)  Скомпилируйте код. Я бы на месте оптимизатора ее выкинул. Или по крайней мере запись в нее. Как хорошо, что вы не на месте компилятора!
|
|
|
|
|
Oct 8 2010, 10:22
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
ну и еще один пример тогда, конечно с -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; } Анатолий.
Сообщение отредактировал aesok - Oct 8 2010, 10:47
|
|
|
|
|
Oct 8 2010, 11:52
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(aesok @ Oct 8 2010, 16:02)  Проверте сами, может ли компилятор выкинуть 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(); }
|
|
|
|
|
Oct 8 2010, 12:28
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(777777 @ Oct 8 2010, 14:16)  Код #include <avr/io.h>
int main() { }
void foo (void) { static int a; a = 2;
return; } Даже в этой абсолютно бессмысленной программе он ее не выкидывает: ...... И даже выполняет в нее запись. Оптимизация -Os Как хорошо, что вы не на месте компилятора!  добавьте ключик "-ffunction-sections -fdata-sections -Wl,--gc-sections" и от вашей статической переменной абсолютно ничего не останется.
|
|
|
|
|
Oct 8 2010, 12:46
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417

|
Цитата(777777 @ Oct 8 2010, 12:46)  Понравилос  По таким постам легко отличать верующих от атеистов. Для верующих сила авторитета сильнее стандарта Си.  Ну так вот стандрат С говорит, что компилятор в процессе оптимизации обязан обеспечить 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-флага, информирующего прерывание о том, что запись произведена. В результате прерывание может начать читать буфер до того, как в него реально произведена запись.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|