Встроенная функция, определенная в заголовке & mdash; нужен ли extern-определитель в исходном файле? [Дубликат]

Третье решение, о котором я почти никогда не упоминал, имеет специфику MySQL и выглядит так:

SELECT id, MAX(rev) AS rev
 , 0+SUBSTRING_INDEX(GROUP_CONCAT(numeric_content ORDER BY rev DESC), ',', 1) AS numeric_content
FROM t1
GROUP BY id

Да, это выглядит ужасно (преобразование в строку и обратно и т. д.), но по моему опыту это обычно быстрее, чем другие решения. Возможно, это только для моих случаев использования, но я использовал его на таблицах с миллионами записей и множеством уникальных идентификаторов. Возможно, это связано с тем, что MySQL очень плохо оптимизирует другие решения (по крайней мере, в 5,0 дней, когда я придумал это решение).

Важно то, что GROUP_CONCAT имеет максимальную длину для строки, которую она может построить. Вероятно, вы захотите поднять этот предел, установив переменную group_concat_max_len. И имейте в виду, что это будет предел для масштабирования, если у вас есть большое количество строк.

В любом случае вышеупомянутое не работает напрямую, если ваше поле содержимого уже является текстом. В этом случае вы, вероятно, захотите использовать другой разделитель, например, \ 0. Вы также быстрее столкнетесь с пределом group_concat_max_len.

146
задан undur_gongor 14 July 2015 в 08:09
поделиться

8 ответов

У нас есть два файла: foo.c и bar.c.

Вот foo.c

#include <stdio.h>

volatile unsigned int stop_now = 0;
extern void bar_function(void);

int main(void)
{
  while (1) {
     bar_function();
     stop_now = 1;
  }
  return 0;
}

Теперь, здесь bar.c

#include <stdio.h>

extern volatile unsigned int stop_now;

void bar_function(void)
{
   while (! stop_now) {
      printf("Hello, world!\n");
      sleep(30);
   }
}

Как вы можете видеть, у нас нет общего заголовка между foo.c и bar.c, однако для bar.c требуется что-то, объявленное в foo.c, когда оно связано, а foo.c требуется функция из bar.c когда он связан.

Используя «extern», вы сообщаете компилятору, что все, что следует за ним, будет найдено (нестатическое) во время соединения; не оставляйте за ним ничего в текущем проходе, так как он будет встречен позже. Функции и переменные обрабатываются одинаково в этом отношении.

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

Технически каждая функция в публичном заголовке библиотеки является «extern», однако ее маркировка как таковая имеет очень мало пользы, в зависимости от компилятора. Большинство компиляторов могут понять это самостоятельно. Как вы видите, эти функции фактически определены где-то еще.

В приведенном выше примере main () будет печатать мир hello только один раз, но продолжать вводить bar_function (). Также обратите внимание: bar_function () не будет возвращаться в этом примере (поскольку это просто простой пример). Просто представьте, что stop_now изменяется при обслуживании сигнала (следовательно, волатильности), если это не представляется достаточно практичным.

Экстерны очень полезны для таких вещей, как обработчики сигналов, мьютексы, которые вы не хотите помещать заголовок или структуру и т. д. Большинство компиляторов оптимизируют, чтобы гарантировать, что они не резервируют какую-либо память для внешних объектов, так как они знают, что они будут резервировать ее в модуле, где объект определен. Однако, опять же, нет смысла указывать его с современными компиляторами при прототипировании публичных функций.

Надеюсь, что это помогает:)

120
ответ дан Tim Post 31 August 2018 в 17:57
поделиться

Ключевое слово extern информирует компилятор о том, что функция или переменная имеет внешнюю связь - другими словами, она видна из файлов, отличных от тех, в которых она определена. В этом смысле ключевое слово static имеет противоположный смысл. Немного странно ставить extern во время определения, поскольку никакие другие файлы не будут иметь видимость определения (или это приведет к нескольким определениям). Обычно вы помещаете extern в объявление в какой-то момент с внешней видимостью (например, файл заголовка) и помещаете определение в другое место.

3
ответ дан 1800 INFORMATION 31 August 2018 в 17:57
поделиться

Вам нужно различать две отдельные концепции: определение функции и объявление символа. «extern» - это модификатор привязки, подсказка для компилятора о том, где определен символ, упомянутый впоследствии (подсказка «нет»).

Если я напишу

extern int i;

в области файлов (вне функционального блока) в файле C, то вы говорите: «переменная может быть определена в другом месте».

extern int f() {return 0;}

является объявлением функции f и определение функции f. Определение в этом случае переопределяет extern.

extern int f();
int f() {return 0;}

- это сначала объявление, за которым следует определение.

Использование extern неверно, если вы хотите объявить и одновременно определить переменную области файла. Например,

extern int i = 4;

выдаст ошибку или предупреждение в зависимости от компилятора.

Использование extern полезно, если вы явно хотите избежать определения переменной.

Позвольте мне объяснить:

Скажем, файл ac содержит:

#include "a.h"

int i = 2;

int f() { i++; return i;}

Файл ah включает в себя:

extern int i;
int f(void);

и файл bc содержит:

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

int main(void){
    printf("%d\n", f());
    return 0;
}

Внешний текст в заголовке полезен, поскольку он сообщает компилятору во время фазы ссылки: «это объявление, а не определение». Если я удалю строку в a.c, которая определяет i, выделяет для нее место и присваивает ему значение, программа не должна компилироваться с неопределенной ссылкой. Это говорит разработчику, что он ссылался на переменную, но еще не определил ее. Если, с другой стороны, я опускаю ключевое слово «extern» и удаляю строку int i = 2, программа все еще компилируется - я буду определен со значением по умолчанию 0.

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

Для функций в объявлениях функций ключевое слово действительно избыточно. Объявление функций не имеет неявного определения.

17
ответ дан 5 revs, 5 users 94%Dave Neary 31 August 2018 в 17:57
поделиться

Ключевое слово extern принимает различные формы в зависимости от среды. Если объявление доступно, ключевое слово extern принимает ссылку, указанную ранее в блоке перевода. В отсутствие такой декларации extern указывает внешнюю связь.

static int g();
extern int g(); /* g has internal linkage */

extern int j(); /* j has tentative external linkage */

extern int h();
static int h(); /* error */

Вот соответствующие абзацы из проекта C99 (n1256):

6.2.2 Связи идентификаторов

[... ]

4 Для идентификатора, объявленного с помощью спецификатора класса хранения extern в области видимости, в которой видна предварительная декларация этого идентификатора, 23) если предыдущее объявление указывает внутреннюю или внешнюю связь, связь идентификатор в более позднем объявлении совпадает с идентификатором, указанным в предыдущем объявлении. Если никакое предварительное объявление не видно или если в предыдущем объявлении не указана ссылка, то идентификатор имеет внешнюю привязку.

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

14
ответ дан dirkgently 31 August 2018 в 17:57
поделиться

Причина, по которой это не имеет эффекта, заключается в том, что в момент ссылки компоновщик пытается разрешить определение extern (в вашем случае extern int f()). Не имеет значения, найдет ли он его в том же файле или в другом файле, если он найден.

Надеюсь, это ответит на ваш вопрос.

0
ответ дан Mac 31 August 2018 в 17:57
поделиться

Насколько я помню стандарт, все объявления функций считаются «extern» по умолчанию, поэтому нет необходимости явно указывать его.

Это не делает это ключевое слово бесполезным, поскольку оно также может использоваться с переменными (и в этом случае - это единственное решение для решения проблем связи). Но с функциями - да, это необязательно.

70
ответ дан Rageous 31 August 2018 в 17:57
поделиться

Объявление функции extern означает, что ее определение будет разрешено во время связывания, а не во время компиляции.

В отличие от обычных функций, которые не объявлены extern, его можно определить в любом из источников (но не в нескольких исходных файлах, иначе вы получите сообщение об ошибке компоновщика, говорящее, что вы указали несколько определений функции), в том числе тот, в котором он объявлен extern.So, в ур случае компоновщик разрешает определение функции в тот же файл.

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

2
ответ дан Rampal Chaudhary 31 August 2018 в 17:57
поделиться

Встроенные функции имеют специальные правила о том, что означает extern. (Обратите внимание, что встроенные функции являются расширением C99 или GNU, они не были в оригинале C.

Для не-встроенных функций extern не требуется, поскольку он включен по умолчанию.

Обратите внимание, что правила для C ++ различны. Например, extern "C" требуется в объявлении C ++ для функций C, которые вы собираетесь вызывать из C ++, и существуют разные правила inline.

6
ответ дан user9876 31 August 2018 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: