Почему я могу успешно скомпилировать программу, даже если отсутствует заголовочный файл? - PullRequest
0 голосов
/ 09 октября 2011

Я обнаружил странную проблему, когда изучал функцию "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 ("%s\n",pch);
        pch = strtok (NULL, " ,.-");
    }   
    return 0;
}

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

У меня вопрос, почему компилятор не диагностировал ошибки для первой компиляции. Я скомпилировал его под Mac OS X с помощью gcc4.2.1.

Ответы [ 2 ]

4 голосов
/ 09 октября 2011

У меня вопрос, почему компилятор не выдал сообщение об ошибке при первой компиляции.

Поскольку вы не компилировали с -Wall -Wextra.Для недавно написанного современного кода вы должны делать это как нечто само собой разумеющееся.

4 голосов
/ 09 октября 2011

В C было разрешено, чтобы функции не имели прототипов (объявлений). При вызове таких функций не будет преобразований параметров. Eg.:

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*).

...