LINUX.ORG.RU

[C] Статические переменные

 


0

1
unsigned long xxx_read(void) 
{
        static unsigned long old = 0;
        unsigned long tmp;

        .....
        while(...) {
                ...
                tmp = old;
                ...

        }
        .....
}

-------------------------

Какое значение записывается в данном случае в переменную tmp? Возможные варианты: значение old на момент входа в функцию, значение old в момент присваивания, значение в какой-то промежуточный момент. Что говорит по этому поводу стандарт? И что на деле генерируют компиляторы?

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

*Ясен пень, функция может вызываться параллельно

**Вопрос не про блокировки. Но если в комментарии очень хочется про них написать, запретить, конечно, я не могу :)

★★★★★

На момент присваивания или на какой-то промежуточный момент. С volatile - на момент присваивания.

tailgunner ★★★★★
()

Статические переменные внутри функций — зло. Тем более в многопоточных программах.

anonymous
()

Разумеется,

значение old в момент присваивания


И, разумеется, это значение не обязано быть тем значением, которое ты одновременно пытаешься записать в old.

Что говорит по этому поводу стандарт?


Not your personal text to speech engine.

И что на деле генерируют компиляторы?


Not your personal assembler listing viewer.

LamerOk ★★★★★
()
Ответ на: комментарий от tailgunner

>На момент присваивания или на какой-то промежуточный момент.

То бишь неопределенность в явном виде. Это следует из того, что в стандарте ничего по этому поводу не написано, или там что-то есть?

ttnl ★★★★★
() автор топика
Ответ на: комментарий от anonymous

>Статические переменные внутри функций — зло. Тем более в многопоточных программах.

Если очень хочется, и ты осторожен, то можно.

ttnl ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

>И, разумеется, это значение не обязано быть тем значением, которое ты одновременно пытаешься записать в old.

На чем основывается твое утверждение?

ttnl ★★★★★
() автор топика
Ответ на: комментарий от ttnl

> Это следует из того, что в стандарте ничего по этому поводу не написано

Вообще не припомню, чтобы стандарт Си заморачивался вещами вроде параллельного исполнения и модели памяти. Компилятор свободен в выборе, когда именно ему загрузить значение в регистр.

tailgunner ★★★★★
()
Ответ на: комментарий от ttnl

вместо теоретизирования и догадок лучше читать стандарт C99

5.1.2/1

Two execution environments are defined: freestanding and hosted. In both cases, program startup occurs when a designated C function is called by the execution environment. All objects with static storage duration shall be initialized (set to their initial values) before program startup. The manner and timing of such initialization are otherwise unspecified. Program termination returns control to the execution environment.

6.2.4/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.

anonymous
()
Ответ на: комментарий от Eddy_Em

>Добавляем мьютекс - и проблем нет :)

Это да. Но конкретно в моей функии можно обойтись без него — даже, если значение old слегка протухшее, то оно все равно годится.

ttnl ★★★★★
() автор топика
Ответ на: комментарий от LamerOk

>PS. Гугли по «atomic operations», а не валяй дурака на лоре.

Какое отношение к стандартному поведению static имеют атомарные операции? Ты действительно не видишь между ними разницы?

ttnl ★★★★★
() автор топика

> значение old в момент присваивания

//КО

redixin ★★★★
()
Ответ на: комментарий от ttnl
int xxx_read(int a) 
{
    static int old = 12345;
    int tmp = 2;
    if(a > 3) 
    {
        tmp = old;
    }
    return tmp;
}
int main(int c, char** s)
{	
    return xxx_read(c);
}

после gcc a.c -S -O2 в функции main эта переменная old исчезает вообще и вместе с функцией xxx_read(), превращаясь в прямой параметр команды процессора

        cmpl	$4, %edi
        movl	$2, %edx
        movl	$12345, %eax
        cmovl	%edx, %eax
        ret
anonymous
()
Ответ на: комментарий от ttnl

> Какое отношение к стандартному поведению static имеют атомарные операции? Ты действительно не видишь между ними разницы?

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

LamerOk ★★★★★
()
Ответ на: комментарий от anonymous

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

Вставь перед if

old+=a;

anonymous
()
Ответ на: комментарий от LamerOk

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

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

Конкретно в моем коде используется блокировка чтения/записи, но она здесь не приведена, т.к. вопрос о другом.

ttnl ★★★★★
() автор топика
Ответ на: комментарий от ttnl

Какая разница, что используется для защиты от коллизий?

Все равно в конечном итоге все сводится к временной переменной-флагу, операции с которым производятся атомарно.

А без атомарных операций вы никаких мьютексов и блокировок не реализуете.

Eddy_Em ☆☆☆☆☆
()

Ясен пень, функция может вызываться параллельно

Как раз проблема в том, что С «однопоточен» by design. Потоки - дело всяких там низкоуровневых вызовов и библиотек. Стандарт не читал, но подозреваю, что там про потоки мало что есть.

Не удивлюсь, если этот код будет работать по разному в дебаге и релизе.

mi_estas
()

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

unsigned long xxx_read(void) 
{
        static unsigned long old = 0;
        volatile unsigned long qwerty;
        unsigned long tmp;

        .....
        
        qwerty = old;
        while(...) {
                ...
                tmp = querty;
                ...

        }
        .....
}
anonymous
()
Ответ на: комментарий от deterok

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

anonymous
()

в многопоточной программе значение не определено, вне зависимости от volatile. А какой еще ответ ты хотел услышать? Тут вроде и вариантов-то нет.

gaga
()
Ответ на: комментарий от ttnl

> В том-то и дело, что слишком толсто. Мьютексы годятся не для всякой задачи. Часто бывает, что их использование — это как идиоматическая стрельба из пушки по воробьям.

Конкретно в моем коде используется блокировка чтения/записи, но она здесь не приведена, т.к. вопрос о другом.

Блокировка чтения/записи и мьютексы — и где ж всё-таки пушка, а где воробьи?

unnamed
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.