Странное поведение с заданной вручную strlen
Случайно, я написал следующий интересный фрагмент:
#include <iostream>
#include <cstring>
size_t strlen(const char* str) {
std::cout << "hello";
return 0;
}
int main() {
return std::strlen("sdf");
}
Неожиданно для меня вывод "hello" в GCC 5.1, что означает, что вызывается мой strlen
. Еще более интересно, если я удалю return
, т.е. Заменим main только вызовом std::strlen("sdf");
, ничего не будет напечатано!
Я также пробовал Clang, для которого std::strlen
вызывает реальную функцию, которая вычисляет длину строки (и ничего не печатается). Это то, что я ожидал увидеть.
Как это можно объяснить? Является ли определение моей собственной функции strlen
рассмотренной поведением undefined?
Ответы
Ответ 1
Здесь нет ничего интересного, просто перегрузка функции и немного поведения undefined. Вы перегрузили библиотечную функцию strlen()
своей собственной версией. Поскольку в реализации GCC std::strlen
есть не что иное, как вызов функции библиотеки внутри пространства имен std
, вы получаете результат, который видите.
Вот соответствующий выдержку из cstring
:
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
using ::strlen;
...
И когда вы удаляете оператор return, GCC полностью отключает вызов, так как он знает, что strlen
является функцией без побочных эффектов, и на самом деле это зарезервированное имя, которое нельзя перегружать. Я предполагаю, что компилятор может дать вам предупреждение здесь, но, увы, этого не произошло, поскольку это не требуется.
Ответ 2
В соответствии с С++ 14 [extern.names]/3, ::strlen
зарезервировано:
Каждое имя из библиотеки Standard C, объявленное с внешней привязкой, зарезервировано для реализации для использования в качестве имени с внешней связью "C", как в пространстве имен std, так и в глобальном пространстве имен.
и эффект использования зарезервированного имени, [reserved.names]/2:
Если программа объявляет или определяет имя в контексте, где оно зарезервировано, кроме как явно разрешено этим разделом, его поведение undefined.
Таким образом, ваша программа имеет поведение undefined.
Ответ 3
Вы определили strlen в пространстве имен std по умолчанию, тем самым заменив стандартный.
Причина, почему иногда вызывается strlen и иногда вызывается стандартный strlen, вероятно, связана с тем, что многие реализации strlen являются макросами вместо функций. Это может быть даже реализовано в ассемблере.
Если это макрос, будет запущен стандартный.
Кроме того, если вы удаляете возврат, вызов функции может быть удален оптимизатором. Вы можете сравнить с -O0.