2017-01-19 4 views
12

Нечаянно, я написал следующий интересный отрывок:Weird поведения с Определённым STRLEN

#include <iostream> 
#include <cstring> 

size_t strlen(const char* str) { 
    std::cout << "hello"; 
    return 0; 
} 

int main() { 
    return std::strlen("sdf"); 
} 

Неожиданно для меня, выхода «привет» в GCC 5.1, что означает, что мой strlen вызывается. Еще более интересно, если я удалю return, т. Е. Заменим main только вызовом std::strlen("sdf");, ничего не будет напечатано!

Я также пробовал Clang, для которого std::strlen вызывает реальную функцию, которая вычисляет длину строки (и ничего не печатается). Это то, что я ожидал увидеть.

Как это можно объяснить? Определяет ли моя собственная функция strlen считаться неопределенным?

+3

См. [Reserved.names] и [des.c.headers]. –

+0

На моей машине ваш пример segfaults. (linux, gcc-версия 6.2.1 20160830) Я этого не ожидал. Я ожидал, что код напечатает «привет» и выйдет с кодом 0 в операционную систему. – DusteD

+0

Я также получаю segfault с вашим примером (gcc 5.4). Я бы предположил, что чрезвычайно необычно и «рискованно» предоставлять функцию «strlen» в глобальном пространстве имен, поскольку это переопределит версию libc (с привязкой «C»), если вы сначала включили '', так как это естественно переопределяет 'std :: strlen', который может использоваться в другом месте в библиотеке. Например. 'std :: cout', похоже, вызывает вызов' strlen'. – davmac

ответ

12

Здесь нет ничего интересного, просто перегрузка функции и немного неопределенного поведения. Вы перегрузили библиотечную функцию strlen() своей собственной версией. Поскольку в GCC реализация std::strlen - это не что иное, как вызов функции библиотеки внутри пространства имен std, вы получаете результат, который видите.

Вот соответствующая выдержка из cstring:

namespace std _GLIBCXX_VISIBILITY(default) 
{ 
_GLIBCXX_BEGIN_NAMESPACE_VERSION 

    using ::strlen; 
    ... 

И когда вы удалите оператор возврата, GCC оптимизирует далеко вызов вообще, так как она знает, что strlen является функцией без побочных эффектов, и это на самом деле зарезервированное имя, которое не должно быть перегружено. Я предполагаю, что компилятор может дать вам предупреждение здесь, но, увы, этого не произошло, поскольку это не требуется.

+1

Можете ли вы объяснить поведение при удалении 'return'? Является ли вызов просто оптимизированным (что я предполагаю, что gcc может сделать, потому что это стандартная библиотечная функция)? – Kevin

+0

@ Кевин, да, но я уточню в ответе. – SergeyA

+0

Это не перегрузка функции. 'std :: strlen' имеет параметры' (char const *) '; функция не может быть перегружена одним и тем же списком параметров. –

0

Вы определили strlen в пространстве имен std по умолчанию, тем самым перезаписав стандартный.

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

Если это макрос, стандартный будет запущен. Кроме того, при удалении возврата вызов функции может быть удален оптимизатором. Вы можете сравнить с -O0.

4

В соответствии с С ++ 14 [extern.names]/3, ::strlen зарезервирован:

Каждое имя из библиотеки Стандарт C объявленной с внешним связыванием зарезервирован для осуществления для использования в качестве имени с extern «C», как в пространстве имен std, так и в глобальном пространстве имен.

и эффект от использования зарезервированного имени, [reserved.names]/2:

Если программа объявляет или определяет имя в контексте, где он зарезервирован, иная явным образом разрешено по этому пункту его поведение не определено.

Таким образом, ваша программа имеет неопределенное поведение.

Смежные вопросы