Переопределение функции из стандартной библиотеки - PullRequest
0 голосов
/ 07 июня 2018

Контекст: в недавнем разговоре вопрос "gcc / clang делает strlen("static string") во время компиляции?"подошел.После некоторого тестирования ответ, кажется, да, независимо от уровня оптимизации.Я был немного удивлен, увидев, что это сделано даже в -O0, поэтому я провел некоторое тестирование и в итоге пришел к следующему коду:

#include <stdio.h>

unsigned long strlen(const char* s) {
  return 10;
}

unsigned long f() {
  return strlen("abcd");
}

unsigned long g(const char* s) {
  return strlen(s);
}

int main() {
  printf("%ld %ld\n",f(),g("abcd"));
  return 0;
}

К моему удивлению, он печатает 4 10, а не 10 10.Я попытался скомпилировать с gcc и clang, а также с различными флагами (-pedantic, -O0, -O3, -std=c89, -std=c11, ...), и поведение между тестами было согласованным.

Поскольку я не включил string.h, я ожидал, что будет использовано мое определение strlen.Но код сборки действительно показывает, что strlen("abcd") был в основном заменен на return 4 (что я наблюдаю при запуске программы).

Кроме того, компиляторы не выводят предупреждений с помощью -Wall -Wextra (большеточно, ни один не связан с проблемой: они все еще предупреждают, что параметр s не используется в моем определении strlen).

Возникают два (связанных) вопроса (я думаю, что они достаточно связаны, чтобы быть заданными втот же вопрос):
- разрешено ли переопределять стандартную функцию в C, если заголовок объявляет, что она не включена?
- эта программа ведет себя так, как должна?Если так, что именно происходит?

1 Ответ

0 голосов
/ 07 июня 2018

Согласно C 2011 (черновик N1570) 7.1.3 1 и 2:

Все идентификаторы с внешней связью в любом из следующих подпунктов… всегда зарезервированы для использования в качестве идентификаторов с внешней связью.

Если программа объявляет или определяет идентификатор в контексте, в котором она зарезервирована (кроме как разрешено в 7.1.4), или определяет зарезервированный идентификатор как имя макроса, поведение не определено.

«Следующие подпункты» определяют стандартную библиотеку C, включая strlen.Ваша программа определяет strlen, поэтому ее поведение не определено.

В случае, который вы наблюдаете, происходит следующее:

  • Компилятор знает, как должен вести себя strlen,независимо от вашего определения, поэтому, оптимизируя strlen("abcd") в f, он оценивает strlen во время компиляции, что приводит к четырем.
  • В g("abcd") компилятор не может распознать это из-заопределение g, это эквивалентно strlen("abcd"), поэтому он не оптимизирует его во время компиляции.Вместо этого он компилирует его в вызов g и компилирует g для вызова strlen, а также компилирует ваше определение strlen, в результате чего g("abcd") вызывает g, что вызываетваш strlen, который возвращает 10.

Стандарт C позволит компилятору полностью отказаться от вашего определения strlen, так что g вернул четыре.Однако хороший компилятор должен предупредить, что ваша программа определяет зарезервированный идентификатор.

...