Интерфейс/реализация в ANSI C

Я работаю над большим проектом на C и хочу организовать его с помощью файлов интерфейса (.h) и файлов реализации (.c), подобно многим объектно-ориентированным языкам, таким как Objective-C или Java. Я знаком с созданием статических библиотек на C, но думаю, что делать это для моего проекта излишне сложно. Как я могу реализовать парадигму интерфейса/реализации в ANSI C? В основном я использую GCC для компиляции, но стремлюсь к строгому соблюдению ANSI C и совместимости между компиляторами. Спасибо!


person user2105505    schedule 05.10.2014    source источник
comment
Вы действительно говорите о том, чтобы делать ООП в C?   -  person Oliver Charlesworth    schedule 05.10.2014
comment
Нет, я в основном ищу способ отделить определения функций от внутренней работы указанных функций, а также метод группировки связанных функций и тому подобного (например, math.h). Я не хочу использовать библиотеку, потому что я не буду повторно использовать функции в других программах, и мне также может понадобиться часто вносить изменения в функциональность.   -  person user2105505    schedule 05.10.2014
comment
Сделать функцию статической в ​​файле .h, чтобы она была видна только в файле .c;   -  person Ivan Ivanov    schedule 05.10.2014
comment
Разве это не то, что вы уже получаете, когда помещаете свои объявления функций в файл .h и определения в файл .c?   -  person Oliver Charlesworth    schedule 05.10.2014
comment
Функция extern видна вне файла, когда вы его включаете. статическая функция видна только в реализации.   -  person Ivan Ivanov    schedule 05.10.2014


Ответы (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 является указателем (в отличие от структуры по значению/копии). Другим модулям, которые зависят от этого модуля, нужно знать только то, что указатель передается туда и обратно, а не то, на что он указывает.

person Bernd Elkemann    schedule 05.10.2014
comment
Спасибо за ваш четкий ответ. Можно ли это сделать одной командой в GCC? Простой вызов gcc main.c приводит к ошибке с неопределенными символами. - person user2105505; 06.10.2014
comment
Вы должны вызывать gcc -c filename.c для каждого c-файла. Каждый из них генерирует .o-файл (модуль). В конце вы вызываете gcc *.o, который связывает их все вместе. Если вы изменили только b.c, то только его нужно перекомпилировать (b.o), а затем снова связать. (неизмененные c-файлы не нужно компилировать заново) - person Bernd Elkemann; 06.10.2014
comment
@ user2105505 также это обычно автоматизируется make-файлом. при этом вам нужно только ввести make в командной строке, и он выберет, какие c-файлы нужно перекомпилировать, а затем свяжет программу вместе. - person Bernd Elkemann; 06.10.2014
comment
в C не используйте пустые скобки в объявлениях функций (которые не являются частью определения); Obj make(); сообщает компилятору, что функция принимает неопределенное количество аргументов, подлежащих повышению аргумента по умолчанию; правильный прототип будет читать Obj make(void); - person Christoph; 06.10.2014
comment
@ Кристоф, это не имеет ничего общего с вопросом. Также дело вкуса, писать ли (void), сегодня это уже не нужно. - person Bernd Elkemann; 06.10.2014
comment
@eznme: в C это не дело вкуса - один является прототипом, другой - нет; например 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. Но, делая это, у вас никогда не будет сильной типизации ООП.

person Jean-Baptiste Yunès    schedule 05.10.2014
comment
Кроме того, это сведет вас с ума;) - person Oliver Charlesworth; 05.10.2014
comment
Это немного преувеличено, но вам понадобится как минимум строгая дисциплина (которая может свести вас с ума). - person Jean-Baptiste Yunès; 05.10.2014
comment
Я повеселился, обнаружив, что ANSI-C — полноценный объектно-ориентированный язык, и перестал читать. Веселый. Да, можно МОДЕЛИРОВАТЬ шаблоны ООП с ОГРОМНЫМИ усилиями, но это в значительной степени бессмысленно И неэффективно, а также: это будет в основном заново изобретать колесо. В C нет необходимости в объектно-ориентированных шаблонах. C - это в значительной степени низкоуровневый язык, принуждение к использованию в нем объектно-ориентированного программирования только спровоцирует проблемы - поэтому был изобретен Obj-C. - person specializt; 05.10.2014
comment
Спасибо, что разрушил мои ночи. - person gon1332; 18.11.2016

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

Вот мой репозиторий, который содержит некоторые библиотеки, написанные на C с использованием шаблона интерфейсов и реализации, описанных в книга.

person gon1332    schedule 18.11.2016