Может ли printf быть заменен автоматически помещается в программу C?

#include  int puts(const char* str) { return printf("Hiya!\n"); } int main() { printf("Hello world.\n"); return 0; } 

Этот код выводит «Hiya!» при запуске. Может кто-нибудь объяснить, почему?

Линия компиляции: gcc main.c

EDIT: теперь это чистая C, и любые посторонние вещи были удалены из строки компиляции.

Да, компилятор может заменить вызов printf эквивалентным вызовом puts .

Поскольку вы определили свою собственную функцию с тем же именем, что и стандартная библиотечная функция, поведение вашей программы не определено.

Ссылка: N1570 7.1.3:

Все идентификаторы с внешней связью в любом из следующих подclassов [это включает в себя puts ] всегда зарезервированы для использования в качестве идентификаторов с внешней связью.
...
Если программа объявляет или определяет идентификатор в контексте, в котором он зарезервирован (кроме как разрешено в соответствии с 7.1.4), или определяет зарезервированный идентификатор в качестве имени макроса, поведение не определено.

Если вы удалите свою собственную функцию puts и просмотрите список сборок, вы можете найти вызов, который puts в сгенерированный код, где вы назвали printf в исходном коде. (Я видел, что gcc выполняет эту оптимизацию).

Это зависит от компилятора и уровня оптимизации. Самые последние версии GCC , в некоторых общих системах с некоторыми оптимизациями, могут делать такую ​​оптимизацию (заменяя простой printf на puts , который AFAIU является законным по сравнению с C99)

Вы должны включать предупреждения при компиляции (например, сначала попытайтесь скомпилировать gcc -Wall -g , затем отладить с помощью gdb , а затем, когда вы уверены, что ваш код скомпилирует его с помощью gcc -Wall -O2 )

BTW, переопределение puts действительно действительно уродливое, если вы не делаете это нарочно (т.е. кодируете свою собственную библиотеку C, а затем вы должны подчиняться стандартам). Вы получаете некоторое неопределенное поведение (см. Также этот ответ о возможных последствиях UB). На самом деле вам следует избегать переопределения имен, упомянутых в стандарте, если вы действительно не знаете, что вы делаете и что происходит внутри компилятора.

Кроме того, если вы скомпилировали статическую gcc -Wall -static -O main.c -o yourprog например gcc -Wall -static -O main.c -o yourprog я буду gcc -Wall -static -O main.c -o yourprog пари, что компоновщик пожаловался бы (о множественном определении puts ).

Но IMNSHO ваш код явно ошибается, и вы это знаете.

Кроме того, вы можете скомпилировать, чтобы получить ассемблер, например, с gcc -fverbose-asm -O -S ; и вы даже можете попросить gcc gcc -fdump-tree-all -O много файлов «дампа» с gcc -fdump-tree-all -O которые могут помочь вам понять, что делает gcc .

Опять же, эта конкретная оптимизация действительна и очень полезна : программа printf любого libc должна «интерпретировать» во время выполнения строку формата печати (особенно для обработки %s т. Д.); это на практике довольно медленно. Хороший компилятор прав, избегая при необходимости вызывать printf (и заменяя puts ).

BTW gcc – не единственный компилятор, который делает эту оптимизацию. clang также делает это.

Кроме того, если вы скомпилируете

 gcc -ffreestanding -O2 almo.c -o almo 

программа almo показывает Hello world.


Если вам нужна еще одна фантастическая и неожиданная оптимизация, попробуйте скомпилировать

 // file bas.c #include  int f (int x, int y) { int r; int* p = malloc(2*sizeof(int)); p[0] = x; p[1] = y; r = p[0]+p[1]; free (p); return r; } 

с gcc -O2 -fverbose-asm -S bas.c затем посмотрите на bas.s ; вы не увидите никакого вызова в malloc или free (на самом деле, команда call не call ), и снова gcc прав, чтобы оптимизировать (а также clang )!

PS: Gnu / Linux / Debian / Sid / x86-64; gcc – версия 4.9.1, clang – версия 3.4.2

Попробуйте ltrace в вашем исполняемом файле. Вы увидите, что printf заменяется вызовом puts компилятором. Это зависит от того, как вы назвали printf

Интересное чтение об этом здесь

Предположительно, функция printf () вашей библиотеки puts ().

Ваш puts () заменяет версию библиотеки.

  • Почему я не должен использовать atoi ()?
  • интеграция пружин + cron + кварц в кластере?
  • Можно ли использовать несколько (двух) постоянных хранилищ с одной объектной моделью, поддерживая отношения друг от друга?
  • Оценка короткого замыкания и побочные эффекты
  • Почему мой оператор мощности (^) не работает?
  • Есть ли функция для округления поплавка на C или мне нужно написать собственное?
  • Что такое пакеты -devel?
  • что такое использование fflush (stdin) в программировании c
  • Массивы, распадающиеся на указатели
  • Декодирование и проверка токена JWT с использованием System.IdentityModel.Tokens.Jwt
  • Что значит ? в С означает?
  • Interesting Posts
    Давайте будем гением компьютера.