определяя функцию дважды в C - PullRequest
       3

определяя функцию дважды в C

3 голосов
/ 27 сентября 2010

У меня проблема. я написал этот код, а. a.c и main.c:

файл: a.h

#ifndef _a_H
#define _a_H
int poly (int a, int b, int c, int x);

int square (int x)
{
       return x*x;
}
#endif // _a_H

файл: a.c

#include "a.h"
int poly (int a, int b, int c, int x)
{
     return a*square(x) + b * x +c;
}

файл: main.c

#include <stdio.h>
#include "a.h"
int main()
{
    int p1 = poly1 (1 ,2 , 1, 5);
    int p2 = poly2 (1 ,1 , 3, 5);

    printf ("p1 = %d, p2 = %d\n", p1, p2);
    return 0;
}

и я получил ошибку:

/ tmp / ccKKrQ7u.o: в функции 'квадрат':
main.c :(. text + 0x0): множественное определение 'square'
/tmp/ccwJoxlY.o:a.c:(.text+0x0): сначала определено здесь
collect2: ld вернул 1 статус выхода

поэтому я переместил реализацию квадрата функции в файл a.c, и он работает.

кто-нибудь знает почему?

1025 * спасибо *

Ответы [ 6 ]

8 голосов
/ 27 сентября 2010

Вообще говоря, файлы * .c компилируются в файлы * .o, а файлы * .o связаны для создания исполняемого файла.Файлы * .h не компилируются, они включаются в текстовом виде в файлы * .c, в тот момент, когда они # включаются.

Таким образом, дважды #include «ah» в два отдельных файла * .cВы поместили свое определение для square () в два отдельных файла.Когда они скомпилированы, вы получите две копии square (), по одной в каждом файле * .o.Затем, когда вы связываете их, компоновщик видит два файла и выдает ошибку.

Как этого избежать?Вы уже обнаружили это.Не помещайте определения функций в файлы * .h.Поместите их в файлы * .c, а объявления функций поместите только в файлы * .h.

6 голосов
/ 27 сентября 2010

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

5 голосов
/ 27 сентября 2010

Это происходит потому, что ваш компилятор C встраивает каждый файл .c в объектный (.o) файл, а затем связывает объектные файлы, чтобы сделать исполняемый файл.Содержимое файла .c и всех включаемых в него файлов известны как единица компиляции .

В вашем примере есть две единицы компиляции:

  • main.c, включая stdio.h и a.h → компилируется в main.o
  • a.c, включая a.h → компилируется в ao

Затем компоновщик (ld) пытаетсясвязать файлы .o, но обнаруживает, что они оба определяют square(), потому что он был в общем a.h - отсюда и ошибка.Вот почему, как некоторые уже отметили, вы не должны помещать определения функций в заголовки.

Если у вас установлена ​​утилита nm (которая должна быть у вас), вы можете запустить

$ nm main.o
$ nm a.o

в оболочке, чтобы увидеть, что square существует в обоих файлах.

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

4 голосов
/ 27 сентября 2010

Когда square() было в заголовке, оно было включено как в ac, так и в main.c, поэтому каждый из соответствующих объектных файлов имел свой собственный square(), но без статического модификатора они имели одно и то же имя.Перемещение его в переменный означает, что оно определено только один раз.

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

static inline int square (int x)
{
       return x*x;
}

Статика будет означать, чтокаждый файл .c, который включает в себя .h, будет иметь свою собственную версию square (), inline означает, что код будет вставлен в строку, и на самом деле не произойдет никакого вызова функции, что, вероятно, здесь и нужно

1 голос
/ 27 сентября 2010

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

Как правило, в заголовочные файлы помещаются только объявления, а не определения.

0 голосов
/ 27 сентября 2010

Вы ответили на свой вопрос: вы определили квадрат дважды, один раз в каждом скомпилированном файле, который включает в себя:

Чтобы избежать этого, вы можете сделать квадрат статической функцией

static int square (int x)
{
   return x*x;
}

и добавьте любой встроенный совет, используемый вашим компилятором, например, inline или __inline.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...