Зачем мне нужно объявлять эту функцию extern. Без него работает

Я новичок в концепции extern. Сегодня на работе я столкнулся с большим количеством внешних функций, которые были объявлены внутри заголовочного файла; фу.ч. Где-то в беспорядке папок я нашел файл foo.c, который содержал определение указанных функций, но он не #include foo.h. Вернувшись домой, я решил поиграться с примерами класса внешнего хранилища. После некоторого чтения в "Книге C" это то, что я придумал.

Вот то, что я НЕ ожидал, чтобы работать. Но это произошло.

main.c

#include <stdio.h> 

int data;

int main()
{

  data = 6;

  printf("%d\n", getData());

  data = 8;

  printf("%d\n", getData());
  return 0;
} 

externs.c

int getData()
{
  extern int data;
  return data;
}

баш

gcc main.c externs.c -o externs

Я не думал, что это сработает, потому что функция технически (или, по крайней мере, подробно) не определена перед main. Это работает, потому что класс хранения по умолчанию для int getData()extern? Если да, то зачем вообще заморачиваться со следующим примером (похожим на то, что я видел на работе)?

main2.c

#include <stdio.h> 
#include "externs.h"

int data;

int main()
{

  data = 6;

  printf("%d\n", getData());

  data = 8;

  printf("%d\n", getData());
  return 0;
} 

externs.h

#ifndef _EXTERNSH_
#define _EXTERNSH_

extern int getData();

#endif

externs.c

int getData()
{
  extern int data;
  return data;
}

баш

gcc main2.c externs.c -o externs

person atomSmasher    schedule 29.01.2016    source источник
comment
Можете ли вы добавить или прокомментировать вывод в каждом случае, пожалуйста?   -  person Grantly    schedule 29.01.2016


Ответы (2)


Функции неявно extern. Вы можете изменить это, объявив их как static.

Вы правы, говорить extern int getData(); по сравнению с int getData(); излишне, но некоторые люди считают, что это улучшает ясность кода.

Обратите внимание, что начиная с C99 ваш первый пример имеет неправильный формат. Он вызывает getData без объявления этой функции. В C89 выполнялось неявное объявление int getData();, но начиная с C99 это было удалено. Используйте переключатели компилятора -std=c99 -pedantic, чтобы проверить это.

person M.M    schedule 29.01.2016

По умолчанию GCC версии 4.x или более ранней компилируется в режиме совместимости C89/C90, который не требует объявления функций перед использованием. Предполагается, что необъявленные функции имеют возвращаемый тип int. По умолчанию GCC 5.x или новее компилируется в режиме совместимости с C11, который требует объявления функций перед использованием — как и C99.

Учитывая, что код скомпилирован без нареканий, можно сделать вывод, что вы используете GCC 4.x.

Тем не менее, вы не обязаны предоставлять полные прототипы, хотя было бы глупо этого не делать. Я обычно компилирую код с параметрами:

gcc -std=c11 -O3 -g -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
    -Wold-style-definition -Wold-style-declaration …

Я подозреваю, что там есть какая-то избыточность, но эти опции обнаруживают много проблем, прежде чем я приступаю к запуску кода.

person Jonathan Leffler    schedule 29.01.2016
comment
Спасибо за ответ. +1 за теги gcc. М.М. опередил тебя на 1 минуту :x - person atomSmasher; 29.01.2016