Почему это делает то, что делает?

#c

#c

Вопрос:

 #include <stdio.h>
void littledot(){}//must use C, not C  

int main() {
   littledot(568,76,105,84,116,76,101,68,111,84);
   printf("%c%c%c%c%c%c%c%c%cn");

   getchar();
   return 0;
}
  

Приведенный выше код выдает результат «LiTtLeDoT». Почему он это делает? Почему 568 имеет решающее значение?

Комментарии:

1. arrrgggghhhhhhh!!!!

2. Ваш код генерирует разные результаты в разных компиляторах (GCC 4.1.2 и Clang-167 выдают разные выходные данные).

Ответ №1:

Это зависит от платформы и является UB (реализация может делать все, что захочет *), но, вероятно, аргументы littledot() все еще находятся в стеке после того, как littledot() возвращает и printf печатает эти аргументы из стека.

НИКОГДА НЕ ПОЛАГАЙТЕСЬ НА ЭТО!


* действительно что угодно. Afaik древняя версия GCC запустила видеоигру, когда столкнулась с чем-то, что вело бы себя неопределенным образом.

Ответ №2:

Вам повезло. Это неопределенное поведение, в частности, вызов printf . Программа может делать что угодно. Случается, что ваша реализация пишет «LiTtLeDoT».

На самом деле это природа неопределенного поведения. Компилятор может делать все, что захочет. Если вы действительно хотите знать, почему он делает то, что делает, вам нужно будет посмотреть на исходящий объектный код. Просмотр кода C ничего не даст из-за вышеупомянутого неопределенного поведения.

Ответ №3:

Это то, что надежно работает для меня с Open Watcom 1.9 в Windows:

 //must use C, not C  
#include <stdio.h>
#include <stdlib.h>

void
__stdcall // callee cleans up
littledot()
{
}

int main(void)
{
   littledot(/*568,*/'L','i','T','t','L','e','D','o','T');
   printf("%c%c%c%c%c%c%c%c%cn");
   getchar();
   exit(0);
   return 0;
}
  

littledot() вызывается с несколькими параметрами, которые передаются в стеке.
Если соглашение о вызове для littledot() равно __stdcall (или __fastcall) , ему придется удалить свои параметры из стека.
Если это __cdecl , то main() должен будет их удалить, но у нас это не сработает.

Однако littledot() ничего не делает и не может сделать с параметрами, потому что они не указаны, что вы можете сделать на C, но не на C .

Итак, что происходит, так это то, что не только параметры littledot() остаются в стеке после его вызова, но и указатель стека не восстанавливается (потому что ни littledot(), ни main() не удаляют параметры, что обычно делается путем настройки указателя стека) и указатель стекауказывает на ‘L’. Затем есть вызов printf(), который сначала помещает в стек адрес строки формата «%c%c%c%c%c%c%c%c%c%c% c n», таким образом формируя все ожидаемые параметры для функции в стеке. printf() с радостью печатает текст и возвращает.

После этого стек неправильно сбалансирован, и выполнение return в main() рискованно, так как приложение может аварийно завершить работу. Возврат с помощью exit(0) исправляет это.

Как уже говорили все остальные, ничто из этого не гарантированно сработает. Это может работать только с определенными компиляторами для определенных ОС, и то только иногда.

Ответ №4:

http://codepad.org/tfRLaCB5

Извините, вы утверждаете, что программа печатает не то, что происходит на моем поле, а не то, что происходит на поле codepad.

И причина в том, что программа имеет неопределенное поведение. printf ожидает один дополнительный аргумент (an int ) для каждого %c , который у вас есть в строке формата. Вы не даете ему эти аргументы, следовательно, может случиться все, что угодно.

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