Почему язык C позволяет пользователям создавать макросы, имена которых совпадают с именами уже существующих библиотечных функций? - PullRequest
2 голосов
/ 09 мая 2020
# include <stdio.h> 
# define scanf  "%s Hello World" 
int main (void) 
{ 
   printf(scanf, scanf); 
   getchar(); 
   return 0; 
}

В приведенном выше фрагменте кода перед выполнением кода происходит фаза раскрытия макроса. Здесь каждое вхождение 'scanf' заменяется "%s Hello World".

Следовательно, printf(scanf,scanf) превратится в printf(“%s Hello World” , “%s Hello World”).

Окончательный результат будет:

%s Hello World Hello World

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

Однако, если мы используем функцию scanf для какой-либо цели

# include <stdio.h> 
# define scanf  "%s Hello World" 
int main(void) 
{ 
   int x;
   printf(scanf, scanf); 
   scanf("%d",&x);
   getchar(); 
   return 0; 

}

Мы обнаруживаем ошибку:

main.c: In function ‘main’:
main.c:2:17: error: called object is not a function or function pointer
 # define scanf  "%s Hello World" 
                 ^
main.c:7:4: note: in expansion of macro ‘scanf’
    scanf("%d",&x);
    ^~~~~

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

1 Ответ

5 голосов
/ 09 мая 2020

C 2018 7.1.3 1 говорит:

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

scanf - идентификатор с областью действия файла, указанной в следующем подпункте (7.21), и вы включить его заголовок, поэтому он зарезервирован для использования в качестве имени макроса.

7.1.3 2 говорит:

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

В совокупности эти правила говорят, что если вы определите scanf в качестве имени макроса и включать <stdio.h>, стандарт C не предъявляет никаких требований к тому, что происходит - это не указано как ошибка в вашей программе, и компилятор не обязан отклонять ваш программа как некорректная. Компилятор также не требуется, чтобы не отклонять вашу программу.

Существует альтернатива, позволяющая избежать этой проблемы: вы можете опустить #include <stdio.h> и вместо этого объявить printf (и любые другие функции из <stdio.h>, которые вы используете) сами. Стандартные расширения C позволяют это, а затем вы можете свободно определять макрос для scanf.

Это типично для большей части стандарта C: он не предотвращает вы делаете то, что может вызвать проблемы. В частности, не требуется, чтобы компилятор предупреждал вас об этом. Существует как минимум три причины (хорошие или плохие), что стандарт C разработан таким образом:

  • Он допускает гибкость и расширения. C был разработан как переносимый язык не в том смысле, что программа работает одинаково везде, а в том смысле, что C может быть относительно легко реализован для множества вычислительных платформ. Стандарт C обеспечивает гибкость, позволяющую адаптировать язык к платформе, и позволяет реализациям предоставлять расширения языка сверх того, что указано в стандарте.
  • Это снижает нагрузку на компиляторы. Не предъявляя много требований к компиляторам, это упрощает их написание. (Тогда необходимость дополнительных проверок программ становится вопросом качества, а не соблюдения стандарта C. Эта политика хорошо себя зарекомендовала. Даже без требований стандарта современные широко доступные компиляторы имеют гораздо более высокое качество, чем десятилетия a go.)
  • Это позволило некоторому коду, написанному до публикации стандарта C, продолжить работу со стандартом. До того, как появились правила использования библиотечных функций в качестве имен макросов, некоторые программы могли быть написаны, которые делали это (что могли бы сделать программисты, потому что они хотели несколько изменить библиотечную функцию, по крайней мере, по внешнему виду). Требование, чтобы компиляторы отклоняли эти программы, потребовало бы работы, чтобы заставить эти программы работать с новым языковым стандартом.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...