Может ли 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() заменяет версию библиотеки.