определение встроенной функции в заголовке против исходного файла - PullRequest
0 голосов
/ 29 октября 2018

Я не уверен, как встроить функции в c (C99 и далее). В разделе 18.6 «Программирование на С, современный подход» (К. Н. Кинг, 2-е издание) или, например, 3 в этом учебнике (в разделе «Стратегии использования встроенных функций»), определение встроенной функции дается в файле заголовка (.h), затем функция снова указывается как extern в файле источника (.c).

Например, что я делаю: в заголовке "stencil.h"

#ifindef _STENCIL_H
#define _STENCIL_H

inline double D1_center_2ndOrder(double vp1, double vm1, double dr) 
{
    return (vp1-vm1) / (2.0 * dr) ;
}

#endif 

Затем в соответствующем исходном файле "stencil.c" определяется

#include "stencil.h"

extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;

Я сделал это, а затем в файле "main.c" я назвал среднее:

#include <stdio.h>
#include <stdlib.h>

#include "stencil.h"

int main(int argc, char *argv[])
{
    double vp1 = 1 ; 
    double vp2 = 2 ;
    dr = 0.1 ;

    double der_v = D1_center_2ndOrder(vp1, vm1, dr) ; 
    printf("der_v\t%f\n", der_v) ;

    return 0 ; 
}

Я компилирую все с помощью следующего make-файла

CC = gcc 

CFLAGS = -Wall -lm -std=gnu11  

OBJECTS = main.o stencil.o

DEPS_MAIN = stencil.h

test: $(OBJECTS)
    $(CC) -o collapse $(OBJECTS) $(CFLAGS)

main.o: main.c $(DEPS_MAIN)
    $(CC) -c main.c

stencil.o: stencil.c stencil.h
    $(CC) -c stencil.c

И тогда я получаю следующую ошибку компилятора:

gcc  -c main.c
gcc  -c stencil.c
gcc  -o test main.o stencil.o -Wall -lm -std=gnu11 
stencil.o: In function `D1_center_2ndOrder':
stencil.c:(.text+0x0): multiple definition of `D1_center_2ndOrder'
main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
make: *** [collapse] Error 1

Когда я определяю функцию в исходном файле .c "stencil.c" и объявляю ее в заголовочном файле, я не получаю вышеуказанную ошибку. Я использую версию gcc: gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28).

Мои вопросы:

(1) Почему тогда «Программирование на C, современный подход» и учебные пособия по встраиванию функций, которые я нахожу в Интернете, предлагают определить функцию в заголовочном файле и снова перечислить ее как extern в исходном файле? Это приводит к ошибке компилятора.

(2) Когда я объявляю встроенную функцию в заголовочном файле, а затем определяю как extern в исходном файле, будет ли компилятор по-прежнему включать мою функцию?

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

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

Итак, ваш make-файл должен выглядеть примерно так:

CC = gcc 

CFLAGS = -Wall -std=gnu11
LDFLAGS = -lm

OBJECTS = main.o stencil.o

DEPS_MAIN = stencil.h


test: $(OBJECTS)
    $(CC) -o collapse $(OBJECTS) $(LDFLAGS)

main.o: main.c $(DEPS_MAIN)
    $(CC) -c main.c $(CFLAGS)

stencil.o: stencil.c stencil.h
    $(CC) -c stencil.c $(CFLAGS)

Я проверил ваш пример с помощью этого шеллскрипта:

#!/bin/sh -eu
cat > stencil.h <<EOF
#ifndef _STENCIL_H
#define _STENCIL_H

inline double D1_center_2ndOrder(double vp1, double vm1, double dr)
{
    return (vp1-vm1) / (2.0 * dr) ;
}

#endif
EOF
cat > stencil.c <<EOF
#include "stencil.h"
extern double D1_center_2ndOrder(double vp1, double vm1, double dr) ;
EOF
cat > main.c <<EOF
#include <stdio.h>
#include <stdlib.h>

#include "stencil.h"

int main(int argc, char *argv[])
{
    double vp1 = 1 ;
    double vp2 = 2 ;
    double dr = 0.1 ;
    double vm1 = 0;

    double der_v = D1_center_2ndOrder(vp1, vm1, dr) ;
    printf("der_v\t%f\n", der_v) ;

    return 0 ;
}
EOF
: ${CC:=gcc}
set -x
gcc -c main.c -std=c99
gcc -c stencil.c -std=c99
gcc -o test main.o stencil.o -lm 

и gcc 4.6.4, и он работает нормально, пока компиляции (а не команда связывания) получают по крайней мере -std=c99 (он не работает с настройками по умолчанию 4.6.4).

(Примечание: защита заголовка не должна начинаться с подчеркивания и буквы в верхнем регистре.)

0 голосов
/ 29 октября 2018

Вы используете переносную стратегию, основанную на стандарте C99 (и более позднюю версию), которая является полностью правильной.

Это сбой, потому что вы вызываете gcc со значением по умолчанию -std, которое для GCC 4.8.5 эффективно говорит ему использовать его устаревшую семантику, а не стандартную семантику C11. Значение по умолчанию для -std было gnu90 в версии 4.8.5. В версии 5 он был изменен на gnu11, который реализует семантику C99 / C11 inline.

Вы используете настройки по умолчанию, потому что ваш Makefile не включает $(CFLAGS) в рецепты компиляции; только в окончательном рецепте ссылки. (Вы можете видеть это в командах, напечатанных make).

Хотя -std=gnu11 должно работать, лучше использовать -std=c11. И рассмотрите возможность обновления до более новой версии GCC.

...