#c #compilation #strtok
#c #Сборник #strtok
Вопрос:
Я обнаружил странную проблему, когда изучал функцию «strtok». Сначала я пропустил файл заголовка при написании демонстрационной программы следующим образом:
/* strtok example */
#include <stdio.h>
//#include <string.h> // the header file I've missed at first
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string "%s" into tokens:n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%sn",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
Компилятор не выдал никакого сообщения об ошибке и успешно скомпилировал программу. Но это приводит к ошибке сегментации при запуске. И когда я добавил отсутствующий файл заголовка, все прошло хорошо.
Мой вопрос в том, почему компилятор не диагностировал никаких ошибок при первой компиляции. Я скомпилировал ее под Mac OS X с помощью gcc4.2.1.
Комментарии:
1. ошибочное прямое объявление приводит к умолчанию …. определение who ускользает от меня atm
Ответ №1:
В C функциям разрешалось не иметь прототипов (объявлений). При вызове таких функций преобразования параметров не будет. Например.:
f(0);
вызовет функцию с именем f
с параметром (int)0
, даже если f
он не был объявлен. Это приводит к неопределенному поведению (… segfaults …), когда фактическое определение f
(в другом .c
файле или в библиотеке) было, например. int f(char*)
или int f(long)
. Это не очень хорошая практика, но она сохраняется для обеспечения обратной совместимости с исходным C.
Когда прототип присутствует, компилятор автоматически преобразует параметр в требуемые типы (возможно, выдает ошибку) на сайте вызова.
PS: не поймите меня неправильно, int
это значение по умолчанию. То, что на самом деле вызывает компилятор, полностью зависит от параметров вызова. Например. f(1.1)
будет соответствовать с void f(double)
, f("string")
с f(char*)
.
Комментарии:
1. 1. прошло так много времени, что я не мог вспомнить, что было по умолчанию! 🙂
2. Убедительный ответ, но компилятор C может фактически вывести правильную сигнатуру strtok() в данном фрагменте кода. Никаких преобразований не требуется.
3. @HansPassant: нет, если
NULL
определено как0
(что является законным в соответствии со стандартом), а указатели имеют ширину 64 бита.
Ответ №2:
Мой вопрос в том, почему компилятор не выдал никакого сообщения об ошибке при первой компиляции.
Потому что вы не скомпилировали с -Wall -Wextra
. Для недавно написанного современного кода вы должны делать это как само собой разумеющееся.
Комментарии:
1.
-W
Флаги (за исключением-Werror
и, возможно, еще 1 или 2) не генерируют ошибок. Они генерируют предупреждение, и компиляция может быть успешной в любом случае 🙂