Я работаю над большим проектом на C и хочу организовать его с помощью файлов интерфейса (.h) и файлов реализации (.c), подобно многим объектно-ориентированным языкам, таким как Objective-C или Java. Я знаком с созданием статических библиотек на C, но думаю, что делать это для моего проекта излишне сложно. Как я могу реализовать парадигму интерфейса/реализации в ANSI C? В основном я использую GCC для компиляции, но стремлюсь к строгому соблюдению ANSI C и совместимости между компиляторами. Спасибо!
Интерфейс/реализация в ANSI C
Ответы (3)
Похоже, вы уже поступаете правильно: хороший код C также организует интерфейсы в .h-файлах, а реализации — в .c-файлах.
Пример файла a.h:
void f(int a);
Пример файла .c:
#include "a.h"
static void helper(void) {...}
void f(int a) {... use helper()...}
Пример файла main.c:
#include "a.h"
int main(void) { f(123); return 0; }
Вы получаете модульность, потому что вспомогательные функции не объявлены в заголовках, поэтому другие модули не знают о них (вы можете объявить их в верхней части файла .c
, если хотите). Такая модульность уменьшает количество необходимых повторных компиляций и уменьшает количество повторных компиляций. (Однако связывание должно выполняться каждый раз). Обратите внимание: если вы не объявляете вспомогательные функции в заголовке, вы уже в безопасности, однако наличие static
перед ними также скрывает их от других модулей во время компоновки, поэтому не возникает конфликта, если несколько модулей используют одну и ту же вспомогательную функцию. -имена.
Если вы работаете только с примитивными типами, то это все, что вам нужно знать, и вы можете не читать здесь. Однако, если вашему модулю нужно работать со структурой, все становится немного сложнее.
Проблемный пример заголовка b.h:
typedef struct Obj {
int data;
}*Obj;
Obj make(void);
void work(Obj o);
Ваш модуль хочет передавать объекты внутрь и наружу. Проблема здесь в том, что внутренности просачиваются в другие модули, которые зависят от этого заголовка. Если представление изменено на float data
, то все используемые модули должны быть перекомпилированы. Один из способов исправить это — использовать только void*
. Так делают многие программы. Однако это громоздко, потому что каждая функция, получающая аргумент void*
, должна привести его к Obj
. Другой способ сделать это:
Заголовок к.ч:
typedef struct Obj*Obj;
Obj make(void);
void work(Obj);
Реализация cc:
#include "c.h"
typedef struct Obj {
int data;
}*Obj;
Причина, по которой это работает, заключается в том, что Obj является указателем (в отличие от структуры по значению/копии). Другим модулям, которые зависят от этого модуля, нужно знать только то, что указатель передается туда и обратно, а не то, на что он указывает.
gcc -c filename.c
для каждого c-файла. Каждый из них генерирует .o-файл (модуль). В конце вы вызываете gcc *.o
, который связывает их все вместе. Если вы изменили только b.c
, то только его нужно перекомпилировать (b.o
), а затем снова связать. (неизмененные c-файлы не нужно компилировать заново)
- person Bernd Elkemann; 06.10.2014
Obj make();
сообщает компилятору, что функция принимает неопределенное количество аргументов, подлежащих повышению аргумента по умолчанию; правильный прототип будет читать Obj make(void);
- person Christoph; 06.10.2014
echo "void foo(); int main() { foo(42); }" | clang -fsyntax-only -Weverything -xc -
будет анализировать без жалоб; добавьте void
, и он не будет
- person Christoph; 06.10.2014
Вы должны прочитать что-нибудь об ООП без ООЛ, например http://www.cs.rit.edu/~ats/books/ooc.pdf. Но, делая это, у вас никогда не будет сильной типизации ООП.
Пожалуйста, сделайте себе одолжение и прочитайте Интерфейсы и реализации C: методы создания программного обеспечения многократного использования
Вот мой репозиторий, который содержит некоторые библиотеки, написанные на C с использованием шаблона интерфейсов и реализации, описанных в книга.