Приветствую Всех заглянувших надеюсь вам будет интересно похоливарить ))) у меня есть вопрос к знатокам Си
утром после бессонной ночи попивая кофе и затягиваясь смачно сигаретой я наткнулся на такую врезку из книги
Искусство программирования на С авторы Ричард Хэзфилд Лоуренс Кирби
предупреждаю первый сабж у меня хватило лени не компилировать порассуждаем логически
(уже скомпилил) а вот второй сабж я скомпилил и обломался но обо всем по порядку и так врезка
/*
* Если уж мы говорим о sprintf может быть стоит заметить что следующая обычная конструкция
* sprintf(mystring, "%s%d%s%f", mystring, j, otherstring, d);
* приводит к неопределенному поведению программы поскольку компилятор
* может проводить запись в mystring в таком порядке в каком хочет возможно начиная с конца выражения
* а может сделать и по другому
* Если вы дейсвительно хотите сделать это то в качестве области для временного хранения
* используйте другую строку
* sprintf(thirdstring, "%s%d%s%f", mystring, j, otherstring, d);
* strcpy(mystring, thirdstring);
*/
и как всегда на этом объяснения закончились какой бред подумал я начал и обдумывать
что вообще он имел ввиду непонятно ясно что аргументы он вычислит рандомно в худшем случае но у нас в аргументах нет побочных эффектов на стеке (поcле двух обязательных аргументов) в кадре предыдущей функции первым будет копия указателя mystring выше копия j выше копия othersttring выше копия d
я все пониманию вычисления рандомно аргументов может быть но все побочные эффеты заканчиваются при вызове
функции ладно sprintf начнет парсить строку первый найдет %s и возмет из стека указатель на mystring потом на j
и так далее где тут неопределенное поведение я не пониманию все законно несколько смущает
что из mystring будет читатся байт и писаться сразу же в нее тоесть мы проосто получим копию mystring а остальные аргументы вообще в примере ни о чем не говорят
что имел автор ввиду говоря эту фразу
«поскольку компилятор
может проводить запись в mystring в таком порядке в каком хочет возможно начиная с конца выражения
а может сделать и по другому»
жути нагоняет ))) при чем тут компилятор вообще ? это какой то бред или я чего то непонимаю
если уж автор пишет что это баг надо показать баг
людям будет неинтересно почему плохо что думаете по этому поводу и почему это плохо вообщем после этой врезки я решил набросать маленькую функцию и проверить в каком порядке мой компилятор будет вычислять аргументы она имеет такой вид
/*
* int main(void)
* {
* int dec = 0;
* int a, b, c, d;
*
* show((++dec,a=dec), (++dec,b=dec), (++dec,c=dec), (++dec,d=dec));
* }
*/
как можно заметить я хотел в вызываемой функции посмотреть какой аргумент каким по счету вызывался но как же я удивился когда увидел следующую картину
4 4 4 4
снова куча вопросов я всегда считал что оператор запятая устанавливает порядок вычисленния строго слева направо с вычислением всех побочных эффетов но только в тех местах где запятая не играет другую роль например вызов функций и инициализация поэтому я каждый аргумент заключил в скобки сразу перед компиляцией в этом случае запятая задает порядок вычисления получилось каждый аргумент это первичное выражение в котором сначало делается инкремент со всеми побочными эфыектами а потом аргумент который и пойдет в стек получает значение
типом и значением этого выражения является тип и значение операнда слева от знака присваивания тоесть я должен был получить номера в каком порядке вычислялись аргументы
но получилась на выходе в точности до наоборот посмотрел код сгенерированный компилятором вот он
00401030 >/$ 55 PUSH EBP /* сохраняем стэковый кадр предыдущей функции */
00401031 |. 8BEC MOV EBP,ESP /* настраиваем на новый */
00401033 |. 83EC 14 SUB ESP,14 *. ./* выделяем немного стека */
00401036 |. C745 FC 000000>MOV DWORD PTR SS:[EBP-4],0 /* инициализируем dec *.
0040103D |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] /*
00401040 |. 83C0 01 ADD EAX,1 *
00401043 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX *
00401046 |. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4] *
00401049 |. 83C1 01 ADD ECX,1 *
0040104C |. 894D FC MOV DWORD PTR SS:[EBP-4],ECX * прибавляем к dec 4
0040104F |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4] *
00401052 |. 83C2 01 ADD EDX,1 *
00401055 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX *
00401058 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] *
0040105B |. 83C0 01 ADD EAX,1 *
0040105E |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX */
00401061 |. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4]
00401064 |. 894D F0 MOV DWORD PTR SS:[EBP-10],ECX /* инициализиуем a *.
00401067 |. 8B55 FC MOV EDX,DWORD PTR SS:[EBP-4]
0040106A |. 8955 EC MOV DWORD PTR SS:[EBP-14],EDX /* инициализируем b */
0040106D |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00401070 |. 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX /* инициализируем c */
00401073 |. 8B4D FC MOV ECX,DWORD PTR SS:[EBP-4]
00401076 |. 894D F8 MOV DWORD PTR SS:[EBP-8],ECX /* инициализируем d */
00401079 |. 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-10]
0040107C |. 52 PUSH EDX ; /Arg4 /* пушим a */
0040107D |. 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14] ; |
00401080 |. 50 PUSH EAX ; |Arg3 /* пушим b */
00401081 |. 8B4D F4 MOV ECX,DWORD PTR SS:[EBP-C] ; |
00401084 |. 51 PUSH ECX ; |Arg2 /* пушим c */
00401085 |. 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8] ; |
00401088 |. 52 PUSH EDX ; |Arg1 /* пушим d */
00401089 |. E8 72FFFFFF CALL move.show ; \show
0040108E |. 83C4 10 ADD ESP,10 /* прибавляем к указателю 16 байт занятые копиями локальных переменных */
00401091 |. 33C0 XOR EAX,EAX /* устанавливаем код возврата функции */
00401093 |. 8BE5 MOV ESP,EBP ./* настраиваем указатель что бы он указывал на адрес с адресом предыдущего кадра */
00401095 |. 5D POP EBP /* восстанавливаем предыдущий кадр */
00401096 \. C3 RETN ./* снимаем адрес возврата и выходим /*
что мы видим ?
приведу вызов функции чтоб не терять ниточки рассуждения
show((++dec,a=dec), (++dec,b=dec), (++dec,c=dec), (++dec,d=dec));
он сначало увеличил четыре раза значение dec потом скопировал 4 в каждый аргумент который и запушил в стэк теперь вопрос почему это так
я себе видел это так выбирается рандомно(рандомно не в прямом смысле))) первичное выражение делается инкремент а потом присваивание и так для всех аргументов потому что запятая в первичном выражениии задает порядок жесткий на вычисление в итоге вызываемая функция должна была получить номера кто в каком порядке вычилялся а не четыре 4 но все с точностью до наоборот получилось
он сначало выдрал из каждого выражения dec увеличив его на 4 а потом всем скопом присвоил значение 4
вообщем загадка если есть люди знающие расскажите все подробно по первому и по второму сабжу чтоб заполнить этот пробел в знаниях тем кто дочитал до конца респект )))