Подтвердить что ты не робот

Это поведение undefined для переопределения стандартного имени?

Легко понять, как будет работать такой код:

#include <string.h>

#define strcmp my_strcmp

int my_strcmp(const char *, const char *)

...
strcmp(str1, str2);
...

Но этот вопрос заключается в том, является ли это технически правильным или нет.

Из C11:

7.1.3.1 (для зарезервированных имен):

...

  • Каждое имя макроса в любом из следующих подпунктов (включая будущую библиотеку направления) зарезервировано для использования, как указано, если включен какой-либо из его связанных заголовков; если явно не указано иное (см. 7.1.4).
  • Все идентификаторы с внешней связью в любом из следующих подпунктов (включая будущие направления библиотек) и errno всегда зарезервированы для использования в качестве идентификаторов с внешняя связь. 184)
  • Каждый идентификатор с областью файлов, указанный в любом из следующих подпунктов (включая будущие направления библиотек) зарезервирован для использования в качестве имени макроса и в качестве идентификатора с область файла в том же пространстве имен, если включен какой-либо из связанных заголовков.

184 Список зарезервированных идентификаторов с внешней связью включает в себя math_errhandling, setjmp, va_copy и va_end.

Таким образом, это означает, что strcmp является зарезервированным словом, потому что string.h включен.

7.1.3.2:

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

Теперь это говорит о переопределении strcmp поведения undefined, за исключением того, что это разрешено в 7.1.4.

Возможно, релевантное содержание 7.1.4:

7.1.4.1:

... Любая функция, объявленная в заголовке, может быть дополнительно реализована как функционально подобный макрос, определенный в заголовке, поэтому, если функция библиотеки объявлена ​​явно при включении заголовка, можно использовать один из приведенных ниже методов чтобы макрос не влиял на декларацию. Любое макроопределение функции может быть подавлено локально путем включения имени функции в круглые скобки, потому что вместо этого имени не следует левая скобка, которая указывает на расширение имени функции макрофункции. По той же синтаксической причине разрешается брать адрес библиотечной функции, даже если он также определен как макрос. 185) Использование #undef для удаления любого определения макроса также гарантирует, что ссылается на фактическую функцию....

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

7.1.4.2:

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

Остальные пункты не имеют значения. Я не вижу, что 7.1.3.2 называется "разрешенным по 7.1.4", за исключением определения функции библиотеки в том же заголовке, что и функция, т.е. Стандартный заголовок, в качестве макроса.

В целом, это код выше techincally undefined поведения? Как насчет того, если string.h не был включен?

4b9b3361

Ответ 1

По крайней мере, одна из причин, почему UB заключается в том, что string.h может вводить макросы. По соображениям внутренней реализации эти макросы могли быть написаны в предположении, что strcmp является "реальной" функцией strcmp. Если вы определяете strcmp как нечто другое, а затем используете эти макросы, strcmp будет расширяться до my_strcmp в макросах с неожиданными последствиями.

Вместо того, чтобы пытаться точно определить, какой код будет ОК в ..., а что нет, стандарт ставит перед вашими махинациями раннюю остановку.

Также обратите внимание, что кроме того, что стандартная выкладка запрещает ее, ваш #define strcmp my_strcmp может быть перекрестным преобразованием макроса, потому что string.h разрешено делать #define strcmp __strcmp или что-то еще. Итак, на некоторых соответствующих реализациях ваш код плохо сформирован.

Ответ 2

Программа, объявляющая или определяющая зарезервированные идентификаторы, не является строго соответствующей (C 2011 4 5), но может быть соответствующей (C 2011 4 7).

спор, из которого возник этот вопрос, заключался не в том, является ли объявление или определение зарезервированного идентификатора поведением undefined на C, но может ли поведение определенных другими средствами, такими как документация для конкретной реализации С, и может ли автор программы сделать это.

Некоторые люди относятся к "undefined поведению" как к значению "Вы не можете этого делать". Это неправильная интерпретация поведения "undefined". undefined поведение не является стандартом, требующим от вас избегать; это то, что вам не помогает стандарт C.

В стандарте C явно указано, что он накладывает без требований на поведение undefined. В частности, это означает, что нет необходимости, чтобы вы этого не делали и не требовали, чтобы другая спецификация не могла определить поведение. Практически любая практическая программа использует поведение, которое не определено стандартом C, когда оно делает системные вызовы, определенные документацией операционной системы или вызовами библиотеки, определенными библиотечной документацией, или зависит от форматов типов данных, определенных конкретной реализацией C, она разработана для.

В C поведение undefined - это просто конец правил, установленных стандартом C. Это открытое поле, которое вы можете перемещать, используя другие средства, а не стену, которая блокирует ваш прогресс.

Ответ 3

Да, это поведение undefined. Оправдано, что это сработает, но вы не можете точно знать.

Я считаю, что причина этого заключается в том, чтобы позволить компилятору иметь встроенные знания о функциях str. И, конечно, strcmp может уже быть макросом memcmp(s1, s2, strlen(s1)) или что-то вроде этого [я не говорю, что это так, просто МОЖЕТ быть.

Я не верю, что "не включая string.h" действительно помогает.

Я предлагаю вам выполнить поиск и замену strcmp my_strcmp в источнике. Вы всегда можете идти в обратном направлении и использовать #define my_strcmp(s1, s2) strcmp(s1, s2), если хотите "вернуться".