Путаница по поводу встроенных объявлений в C - PullRequest
3 голосов
/ 19 октября 2011

Я реализую реализацию очередей на C. Мой интерфейс состоит из пяти простых функций для доступа к очереди:

#ifndef QUEUE_H
#define QUEUE_H

#include <stdbool.h>
#include <stddef.h>

struct queue {
  struct cell* first;
  struct cell* last;
};

typedef struct queue queue;

extern queue newQueue(void);
extern bool  isEmpty(queue);
extern queue enqueue(queue,void*);
extern queue dequeue(queue);
extern void* front(queue);
extern void  freeQueue(queue);

Поскольку два из них (newQueue и isEmpty) настолько тривиальны, что я считаю, что компилятор может сделать с ними много хороших оптимизаций, я решил написать для них встроенные объявления:

/* replacing the two lines

extern queue newQueue(void);
extern bool  isEmpty(queue);

in the original header */

extern inline queue newQueue(void) {
  queue q = { NULL, NULL };
  return q;
}

extern inline bool isEmpty(queue q) {
  return q.first == NULL;
}

Это нормально скомпилируется с gcc. Но когда я компилирую его с помощью clang, это выдает мне ошибку. Быстрое исследование показывает, что официальный способ выполнения этих встроенных объявлений отличается от стиля GNU. Я мог бы либо передать -std=gnu89, либо изменить сигнатуры функций по ссылке выше. Я выбрал второй вариант:

inline queue newQueue(void) {
  queue q = { NULL, NULL };
  return q;
}

inline bool isEmpty(queue q) {
  return q.first == NULL;
}

Но теперь и clang, и gcc что-то говорят о дублированных объявлениях функций при компиляции в режиме c99. Это сопутствующее определение в queue.c:

#include "queue.h"

/* ... */

queue newQueue() {
  queue q = { NULL, NULL };
  return q;
}

bool isEmpty(queue q) {
  return q.first == NULL;
}

Что я делаю не так? Как получить то, что я хочу, без необходимости переключения в режим gnu89?

Это сообщения об ошибках, которые я получаю со вторым стилем:

$ gcc -std=c99 queue.c 
queue.c:12:7: error: redefinition of ‘newQueue’
queue.h:14:21: note: previous definition of ‘newQueue’ was here
queue.c:17:6: error: redefinition of ‘isEmpty’
queue.h:19:20: note: previous definition of ‘isEmpty’ was here
$ clang -std=c99 queue.c
queue.c:12:7: error: redefinition of 'newQueue'
queue newQueue() {
      ^
In file included from queue.c:5:
./queue.h:14:21: note: previous definition is here
extern inline queue newQueue(void) {
                    ^
queue.c:17:6: error: redefinition of 'isEmpty'
bool isEmpty(queue q) {
     ^
In file included from queue.c:5:
./queue.h:19:20: note: previous definition is here
extern inline bool isEmpty(queue q) {
                   ^
2 errors generated.

Ответы [ 3 ]

5 голосов
/ 19 октября 2011

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

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

Я провел некоторое исследование и имею больше информации:

Вы можете использовать inline таким образом.По крайней мере, в C99.Вы просто не можете иметь как встроенные, так и не встроенные определения в queue.c.Вам нужно заключить встроенные определения в #ifdef или переместить их в заголовок, не включенный в queue.c.

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

1 голос
/ 29 марта 2014

Правильный способ сделать это в c99 и более поздних версиях - просто иметь внешнее объявление встроенной функции в файле .c вместо ее определения.Это заставит создавать отдельный код для этой функции, чтобы он правильно связывался, если встраивание по какой-либо причине невозможно.См .: http://www.greenend.org.uk/rjk/tech/inline.html

Поскольку для функций по умолчанию установлено значение extern, этого достаточно:

queue.c

#include "queue.h"

/* ... */

queue newQueue();

bool isEmpty(queue q);
1 голос
/ 19 октября 2011

Вы не должны объявлять их в очереди.c

...