|
Использование static переменных |
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 40)
|
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-флага, информирующего прерывание о том, что запись произведена. В результате прерывание может начать читать буфер до того, как в него реально произведена запись.
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
|
Oct 8 2010, 14:10
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(rezident @ Oct 8 2010, 17:54)  Не его (компилятора) собачье дело!  Пускай warning выдаст, а я уж сам решу нужна мне там переменная или не нужна. Насчет выбрасывания неиспользуемых функций - согласен, а вот насчет выбрасывания переменных типа static ...ммм... можно привести реальные примеры такого поведения с указанием конкретных компиляторов и параметров оптимизации? Код static int foo;
int main (void){ return 0; } На любом ненулевом уровне оптимизации gcc выкидывает foo
|
|
|
|
|
Oct 8 2010, 14:51
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(rezident @ Oct 8 2010, 17:54)  Не его (компилятора) собачье дело!  По закону имеет право выкидывать... В смысле, по стандарту. Какой компилятор реализует такую оптимизацию? Возможно, и никакой, так как это может требовать специальных усилий разработчиков компилятора без существенного выигрыша. Искать нужно среди компиляторов, занимающихся глобальной оптимизацией программы в целом. Они могут легко вычислить, что переменная не используется, и выкинуть её в ходе обычной оптимизации потоков данных. Поэтому крутые компиляторы для однокристаллок с их небольшими программами и широкими возможностями для глобальной оптимизации наиболее вероятные кандидаты. Вот только С с его адресной арифметикой существенно тормозит подобные оптимизации. Цитата(Petka @ Oct 8 2010, 18:10)  На любом ненулевом уровне оптимизации gcc выкидывает foo Скорее всего выкидывает линкер, как объект, на который нет ни одной ссылки из программы.
--------------------
Пишите в личку.
|
|
|
|
|
Oct 8 2010, 15:01
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Petka @ Oct 8 2010, 20:10)  На любом ненулевом уровне оптимизации gcc выкидывает foo Хм. IAR тоже выбрасывает. А вот так уже нет. Но warning формирует в обоих случаях. Код static int foo;
int main (void) { foo = 1; return 0; } Цитата(Oldring @ Oct 8 2010, 20:51)  Скорее всего выкидывает линкер, как объект, на который нет ни одной ссылки из программы. Нет, именно компилятор. Эта строка с объявлением переменной уже в asm-файле просто закоментарена компилятором.
|
|
|
|
|
Oct 8 2010, 23:03
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(777777 @ Oct 8 2010, 12:46)  Понравилос  По таким постам легко отличать верующих от атеистов. Для верующих сила авторитета сильнее стандарта Си.  Вера здесь не при чём. Просто я, в отличее от Сергея, работаю только в компиляторе IAR. В связи с этим не берусь обобщать. Кроме того, Сергей не раз достаточно аргументировано отвечал приводя выдержки из стандарта. Все остальные аргументы здесь уже привели. Они совпадают с моим подходом. Так, к примеру, я отлаживаю сразу на максимальной оптимизации. 2 причины. 1 - чтобы по 2 раза не отлаживать. 2 - сама отладка - это не пошаговое хождение, а точечная остановка в нужном месте.
|
|
|
|
|
Oct 9 2010, 22:02
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Код #include <avr/io.h> #include <avr/interrupt.h>
extern void foo (void); volatile int volatile * p_a;
int main() { foo (); foo ();
return 0; }
void foo (void) { static int a = 1;
p_a = &a;
a = 2;
cli ();
// предположим что в этот момент управление // передаеться обработчику прерывания и в нем // происходит чтение локальной переменной 'a' // через указарель 'p_a'.
sei (); a = 3; return; } Вопрос: Какое значение получает обработчик прерывания через указарель 'p_a', если код скомпилирован с -Os? Кто считает что 2? Код .global foo .type foo, @function foo: ldi r24,lo8(a.1233) ldi r25,hi8(a.1233) sts p_a+1,r25 sts p_a,r24 /* #APP */ ; 23 "main.c" 1 cli ; 0 "" 2 ; 30 "main.c" 1 sei ; 0 "" 2 /* #NOAPP */ ldi r24,lo8(3) ldi r25,hi8(3) sts a.1233+1,r25 sts a.1233,r24 ret .size foo, .-foo .global main .type main, @function main: rcall foo rcall foo ldi r24,lo8(0) ldi r25,hi8(0) ret .size main, .-main .comm p_a,2,1 .data .type a.1233, @object .size a.1233, 2 a.1233: .word 1 Правильный ответ: при первом вызове foo () 1, при последующих 3. Анатолий.
Сообщение отредактировал aesok - Oct 9 2010, 22:02
|
|
|
|
|
Oct 9 2010, 22:32
|
Знающий
   
Группа: Участник
Сообщений: 596
Регистрация: 26-05-06
Из: Москва
Пользователь №: 17 484

|
Цитата(GetSmart @ Oct 10 2010, 02:17)  Чтобы прерывания правильно обрабатывали глобальные переменные, меняющиеся вне прерывания, эти переменные должны быть волатильными. Есть такое правило. Это я и пытаюсь показать на примере 777777Цитата Формально, p_a является волатильной и даже указывает (должна) на волатильную переменную, но в реале указывает на неволатильную. При присваивании p_a = &a должен был появиться варнинг. Никаких варингов нет. Цитата А в остальном, компилятор работая с "а" всё делает верно. Если бы между cli/sti был вызов функции, то перед вызовом в "а" была бы записана двойка. Да, с небольшим уточнением, если эта дополнительная функция проинлайнилась в foo () и указатель p_a в ней не используеться, то записи двойки в 'a' не будет. Анатолий.
|
|
|
|
|
Oct 10 2010, 08:57
|

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

|
Цитата(aesok @ Oct 10 2010, 02:02)  Правильный ответ: при первом вызове foo () 1, при последующих 3. Разумеется, потому что с точки зрения компилятора у тебя идут две подряд строки a = 2; a = 3; Результат выполнения этой программы - занесения 3 в переменную a. Но это последнее присваивание компилятор не имеет права выбрасывать потому что a - static! И этот результат может понадобиться при последующих вызовах этой функции. В моем случае последовательность была такая: Код uint8_t cmd[3] = { 8, mn, 0 };
pTWI = cmd; SendSLA(); Здесь компилятор не имеет права выбрасывать ничего, даже если бы pTWI было не volatile потому что: а) адрес cmd присваивается глобальной переменной, сделовательно cmd используется и он должен быть инициализирован; б) pTWI - глобальная переменная, следовательно она может использоваться в других функциях, а значит по выходу из функции в ней должен быть адрес cmd; в) более того, следом вызывается некая глобальная функция, поэтому компилятор обязан предположить, что pTWI может использоваться в этой функции, следовательно адрес cmd должен быть присвое ей до вызова SendSLA После того как я сделал массив static ограничений стало еще больше.
|
|
|
|
|
Oct 10 2010, 10:03
|

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

|
Цитата(GetSmart @ Oct 10 2010, 02:17)  Чтобы прерывания правильно обрабатывали глобальные переменные, меняющиеся вне прерывания, эти переменные должны быть волатильными. Есть такое правило. Обалденное определение, мне нравится. А что значит "правильно обрабатывали"? А как можно обрабатывать неправильно? Вообще-то у него есть строго определенное значение: оно заставляет компилятор предполагать, что значение этой переменной может меняться само по себе, помимо хода выполнения программы: Цитата An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Therefore any expression referring to such an object shall be evaluated strictly according to the rules of the abstract machine, as described in 5.1.2.3. Furthermore, at every sequence point the value last stored in the object shall agree with that prescribed by the abstract machine, except as modified by the unknown factors mentioned previously. What constitutes an access to an object that has volatile-qualified type is implementation-defined. Таким образом, volatile должны быть объявлены только те переменные, которые с точки зрения компилятора не меняются исходя из транслироемого кода. Цитата(GetSmart @ Oct 10 2010, 02:17)  Если бы между cli/sti был вызов функции, то перед вызовом в "а" была бы записана двойка. Вообще говоря да, компилятор обязан предположить, что 'a' может потребоваться в той функции, но в данном случае она static и поэтому из той функции недоступна.
|
|
|
|
|
Oct 10 2010, 12:31
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(777777 @ Oct 10 2010, 12:57)  Разумеется... ...последнее присваивание компилятор не имеет права выбрасывать потому что a - static! Вы не правы. Привязались к статику уак банный лист:-) Он не для этого задуман. И все ваши домыслы можно поставить с ног на голову путём изменения опций компилятора и линкера. Остыньте и сделайте два-три глубоких вдоха. В этом споре вы уже проиграли. Сэ ля ви  PS aesok, с возвращением!
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Oct 11 2010, 04:30
|

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

|
Цитата(demiurg_spb @ Oct 10 2010, 16:31)  Вы не правы. Привязались к статику уак банный лист:-) Он не для этого задуман. И все ваши домыслы можно поставить с ног на голову путём изменения опций компилятора и линкера. Остыньте и сделайте два-три глубоких вдоха. В этом споре вы уже проиграли. А для чего же он создан? Приведите раздел стандарта который это объясняет. В крайнем случае, приведите пример программы с набором опций компилятора и линкера. Вы ничего этого не сделали, но почему-то объявляете, что я проиграл. Статик создан именно для того, чтобы его значение, установленное в функции, сохранялось при выходе из нее и было таким же при повторном входе в эту функцию. Компилятор имеет большую свободу действий, но такое поведение переменной он обязан обеспечивать. Если у вас есть другие мнения на этот счет - объясните, желательно с убедительной аргументацией.
|
|
|
|
|
Oct 11 2010, 04:41
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(777777 @ Oct 11 2010, 09:30)  Статик создан именно для того, чтобы его значение, установленное в функции, сохранялось при выходе из нее и было таким же при повторном входе в эту функцию. Это такая же голимая интерпретация как и я писал об volatille  Я хотя бы для себя ввёл то правило, являющееся следствием оптимизации компиляторов. То есть "моё" правило, напрямую не касалось определения volatille, а просто юзало его. Статик просто размещает переменную в каком-то там сегменте (bss или др.). И всё. Остальное на совести компилятора.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Oct 11 2010, 07:36
|

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

|
Цитата(GetSmart @ Oct 11 2010, 08:41)  Статик просто размещает переменную в каком-то там сегменте (bss или др.). И всё. Остальное на совести компилятора. Что именно "всё остальное"? Стандарт четко определяет время жизни (lifetime) переменной. Что по-вашему означает этот термин? Когда компилятор выкидывает какие-либо присвоения, то он это делает в том случае, когда видит, что они не потребуются до конца жизни переменной. Если же ее время жизни - до конца программы, то как он может их выкинуть? Он обязан полагаться на то, что где-то в программе она потребуется. Или вы считаете, что он может проанализировать программу и выяснить, что функция больше вызываться не будет и отменить присваивание? Это даже программист не может сделать, для этого он и выполняет отладку. Вы даете компилятору слишком много воли. Если бы это было так, то программы было бы невозможно писать. Наслушавшись таких разговоров некоторые предпочитают асм, думая, что компилятор действительно может сгенерировать не то, что написал программист. Но к счастью это не так.
|
|
|
|
|
Oct 11 2010, 07:55
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Я уже потерял суть спора. Чё не нравится? В смысле, что компилятор (и какой?) сделал не так, как хотелось? Цитата(777777 @ Oct 11 2010, 12:36)  Что именно "всё остальное"? Стандарт четко определяет время жизни (lifetime) переменной. Что по-вашему означает этот термин? В сегменте bss эта переменная будет жить вечно. Довольны? Вопрос только в том, выкинет ли компилятор какое-нибудь присваивание в эту переменную.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Oct 11 2010, 08:10
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(rezident @ Oct 8 2010, 17:54)  Не его (компилятора) собачье дело!  Пускай warning выдаст, а я уж сам решу нужна мне там переменная или не нужна. Насчет выбрасывания неиспользуемых функций - согласен, а вот насчет выбрасывания переменных типа static ...ммм... можно привести реальные примеры такого поведения с указанием конкретных компиляторов и параметров оптимизации? Это уж как вы ему (компилятору) укажете. Выбрасывать-не выбрасывать, предупреждать-не отвлекать на всякую ботву и тд. По крайней мере GCC так работает. ключ -fdata-sections позволяет выкидывать неиспользуемые секции данных.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Oct 11 2010, 08:58
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(777777 @ Oct 11 2010, 08:30)  А для чего же он создан? Приведите раздел стандарта который это объясняет. В крайнем случае, приведите пример программы с набором опций компилятора и линкера. Вы ничего этого не сделали... Для себя я уже давно всё сделал, а Вам народ неоднократно писал про это: Код CFLAGS += --combine CFLAGS += -fwhole-program CFLAGS += -ffunction-sections CFLAGS += -fdata-sections С такими опциями со статической переменной может быть вообще всё что угодно (всё будет сильно зависеть от контекста Вашей программы).
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
|
Oct 11 2010, 10:20
|

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

|
Цитата(demiurg_spb @ Oct 11 2010, 12:58)  Для себя я уже давно всё сделал, а Вам народ неоднократно писал про это: Код CFLAGS += --combine CFLAGS += -fwhole-program CFLAGS += -ffunction-sections CFLAGS += -fdata-sections С такими опциями со статической переменной может быть вообще всё что угодно Ну что ж, попробуем тупо воткнуть их в проект Код int main() { foo(); }
volatile int volatile * p_a;
void foo (void) { static int a;
p_a = &a; a = 2; a = 3; } Получаем на выходе: Код void foo (void) { static int a;
p_a = &a; 56: 80 e0 ldi r24, 0x00; 0 58: 91 e0 ldi r25, 0x01; 1 5a: 90 93 03 01 sts 0x0103, r25 5e: 80 93 02 01 sts 0x0102, r24 a = 2; a = 3; 62: 83 e0 ldi r24, 0x03; 3 64: 90 e0 ldi r25, 0x00; 0 66: 90 93 01 01 sts 0x0101, r25 6a: 80 93 00 01 sts 0x0100, r24 Если между присваиваниями воткнуть обращение к какой-нибудь функции (даже к ней самой), то он и первое присваивание не выкинет: Код void foo (void) { static int a;
p_a = &a; 7e: 80 e0 ldi r24, 0x00; 0 80: 91 e0 ldi r25, 0x01; 1 82: 90 93 03 01 sts 0x0103, r25 86: 80 93 02 01 sts 0x0102, r24 a = 2; 8a: 82 e0 ldi r24, 0x02; 2 8c: 90 e0 ldi r25, 0x00; 0 8e: 90 93 01 01 sts 0x0101, r25 92: 80 93 00 01 sts 0x0100, r24 foo(); 96: df df rcall .-66 ; 0x56 <foo> a = 3; 98: 83 e0 ldi r24, 0x03; 3 9a: 90 e0 ldi r25, 0x00; 0 9c: 90 93 01 01 sts 0x0101, r25 a0: 80 93 00 01 sts 0x0100, r24 Цитата(demiurg_spb @ Oct 11 2010, 12:58)  (всё будет сильно зависеть от контекста Вашей программы). Ну, если например в ней не будет обращений к foo, то он ее выкинет, это отследить не сложно. Но если обращение есть, то никакой самодеятельности не будет. (Если только там нет ключей, заставляющих его нарушать стандарт, но я сомневаюсь и наличии таковых)
|
|
|
|
|
Oct 11 2010, 10:52
|

Профессионал
    
Группа: Свой
Сообщений: 1 003
Регистрация: 20-01-05
Пользователь №: 2 072

|
Цитата(777777 @ Oct 11 2010, 07:30)  Статик создан именно для того, чтобы его значение, установленное в функции, сохранялось при выходе из нее и было таким же при повторном входе в эту функцию. Оригинально. Вообще ключевое слово 'static' определяет область видимости переменной, а в контексте функции к этому добавляется требование ее размещения в области данных, а не стека. Вот и все. Больше компилятор ничего не ограничивает, и если он достаточно умный, то найдя только присваивания данной переменной без передачи (или возврата) ее адреса в другие функции имеет полное право оптимизировать использование этой переменной, вплоть до полного исключения ее из результата компиляции.
|
|
|
|
|
Oct 11 2010, 11:10
|

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

|
Цитата(halfdoom @ Oct 11 2010, 14:52)  Оригинально. Вообще ключевое слово 'static' определяет область видимости переменной, а в контексте функции к этому добавляется требование ее размещения в области данных, а не стека. Вот и все. Больше компилятор ничего не ограничивает, и если он достаточно умный, то найдя только присваивания данной переменной без передачи (или возврата) ее адреса в другие функции имеет полное право оптимизировать использование этой переменной, вплоть до полного исключения ее из результата компиляции. Ключевое слово - lifetime. У static переменной оно бесконечное. Поэтому наличие только присваивания не может служить основанием для того чтобы заявлять о ее неиспользовании. Она может понадобиться при следующем вызове функции. И вообще, если компилятор выкидывает какую-то переменную или часть кода, то это означает что и без оптимизаций эта переменная/код не будет использована. Поэтому не стоит так бояться "самодеятельности" компилятора, если таковая обнаружится, не спешите выключать оптимизацию, лучше повнимательнее посмотрите исходник. Цитата(GetSmart @ Oct 11 2010, 14:58)  ...переменной <==> функции... С самого начала речь шла о static переменных в контексте функций.
|
|
|
|
|
Oct 11 2010, 11:18
|

Профессионал
    
Группа: Свой
Сообщений: 1 003
Регистрация: 20-01-05
Пользователь №: 2 072

|
Цитата(777777 @ Oct 11 2010, 14:10)  Ключевое слово - lifetime. У static переменной оно бесконечное. Поэтому наличие только присваивания не может служить основанием для того чтобы заявлять о ее неиспользовании. Она может понадобиться при следующем вызове функции. Современные методы анализа потока данных и кода позволяют выяснить, что в конструкциях типа приведенной ниже, ни при первом, ни при последующем вызовах необходимости в выделении места под переменную нет. Код int test(int aa) { static int bb = 5;
bb = 3; bb = 2; return aa * bb; /* это будет заменено на aa * 2, с последующим удалением bb */ } В случае с исходником из поста 35, компилятор все делает правильно, т.к. имеется использование адреса переменной вне функции.
|
|
|
|
|
Oct 11 2010, 11:25
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата(777777 @ Oct 11 2010, 16:10)  ... Поэтому наличие только присваивания не может служить основанием для того чтобы заявлять о ее неиспользовании. Она может понадобиться при следующем вызове функции. Отсутствие её чтения - может. Этот факт анализируют (почти) все современные компиляторы. Могут выдавать ремарку или варнинг. Особенно на "статик" (не глобальную/межмодульную). Шипко умные могут даже выкинуть её, если она не волатильная. Цитата(777777 @ Oct 11 2010, 15:20)  Ну, если например в ней не будет обращений к foo, то он ее выкинет, это отследить не сложно. Но если обращение есть, то никакой самодеятельности не будет. Следите за ходом мысли. Если foo() сделать статиком, то она рискует быть удалённой, если внутри модуля она на вызывается и адрес её не используется. Даже на 99% она будет удалена современными компиляторами. То же самое относится и к статик переменным. Если внутри функции (аналог модуля для статик функции) переменная не читается, то не важно есть ли в неё запись, она может быть удалена. Если её адрес используется/записывается в другую переменную, то идёт рекурсия по анализу использования этой второй переменной. Если она не читается, а только прописывается, то сперва удаляется эта вторая переменная, а потом удаляется первая переменная, которая "статик". Всё по-чесноку.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Oct 11 2010, 12:49
|

неотягощённый злом
     
Группа: Свой
Сообщений: 2 746
Регистрация: 31-01-08
Из: Санкт-Петербург
Пользователь №: 34 643

|
Цитата(777777 @ Oct 11 2010, 14:20)  Ну, если например в ней не будет обращений к foo, то он ее выкинет, это отследить не сложно. Но если обращение есть, то никакой самодеятельности не будет. (Если только там нет ключей, заставляющих его нарушать стандарт, но я сомневаюсь и наличии таковых) Как уже говорили, при использовании ключей combine и fwhole-program, все функции становятся static. И в этом случае у компилятора руки развязываются полностью. А со всеми ключикамии для линкера возможно получить кумулятивный эффект, под кодовым названием "без говNа":) И тут единственное, что может сохранить обращения к переменной это volatile и ничто более. Повтарюсь, не стоит сюда притягивать за уши static.
--------------------
“Будьте внимательны к своим мыслям - они начало поступков” (Лао-Цзы)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|