Может ли printf быть заменен автоматически помещается в программу C?
#include <stdio.h>
int puts(const char* str)
{
return printf("Hiya!\n");
}
int main()
{
printf("Hello world.\n");
return 0;
}
Этот код выводит "Hiya!" при запуске. Может кто-нибудь объяснить, почему?
Линия компиляции:
gcc main.c
РЕДАКТИРОВАТЬ: теперь он чистый C, и любые другие вещи были удалены из строки компиляции.
Ответы
Ответ 1
Да, компилятор может заменить вызов printf
эквивалентным вызовом puts
.
Поскольку вы определили свою собственную функцию puts
с тем же именем, что и стандартная библиотечная функция, ваше поведение программы undefined.
Ссылка: N1570 7.1.3:
Все идентификаторы с внешней связью в любом из следующих подклассов [это включает в себя puts
] всегда зарезервированы для использования в качестве идентификаторов с внешней связью.
...
Если программа объявляет или определяет идентификатор в контекст, в котором он зарезервирован (кроме как разрешено в соответствии с пунктом 7.1.4), или определяет зарезервированный идентификатор в качестве имени макроса, поведение undefined.
Если вы удалите свою собственную функцию puts
и просмотрите список сборок, вы можете найти вызов puts
в сгенерированном коде, где вы вызывали printf
в исходном коде. (Я видел, что gcc выполняет эту оптимизацию.)
Ответ 2
Это зависит от компилятора и уровня оптимизации. Самые последние версии GCC, в некоторых общих системах с некоторыми оптимизациями могут делать такую оптимизацию (заменяя простой printf
с puts
, который AFAIU является законным по стандартам, например C99)
Вы должны включить предупреждения при компиляции (например, попробуйте сначала скомпилировать с gcc -Wall -g
, затем отладить с помощью gdb
, а затем, когда вы уверены, что ваш код скомпилирует его с помощью gcc -Wall -O2
)
BTW, переопределение puts
действительно действительно уродливое, если вы не делаете это специально (то есть кодируете свою собственную библиотеку C, а затем вы должны подчиняться стандартам). Вы получаете некоторое undefined поведение (см. Также этот ответ о возможных последствиях UB). На самом деле вам следует избегать переопределения имен, упомянутых в стандарте, если вы действительно не знаете, что вы делаете и что происходит внутри компилятора.
Кроме того, если вы скомпилировали статическую ссылку, например gcc -Wall -static -O main.c -o yourprog
, я буду держать пари, что компоновщик пожаловался бы (о множественном определении puts
).
Но IMNSHO ваш код не прав, и вы это знаете.
Кроме того, вы можете скомпилировать, чтобы получить ассемблер, например. с gcc -fverbose-asm -O -S
; и вы даже можете попросить gcc
разлить много файлов "дампа" с помощью 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 <stdlib.h>
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
(на самом деле не выдается машинная инструкция no call
), и снова gcc
прав на оптимизацию (и так далее clang
)!
PS: Gnu/Linux/Debian/Sid/x86-64; gcc
- версия 4.9.1, clang
- версия 3.4.2
Ответ 3
Попробуйте ltrace
на вашем исполняемом файле. Вы увидите, что printf
заменяется вызовом puts
компилятором. Это зависит от того, как вы назвали printf
Интересное чтение по этому вопросу здесь
Ответ 4
Предположительно, ваша библиотека printf() вызывает puts().
Ваш puts() заменяет версию библиотеки.