Может ли заголовок использовать определение макроса, определенного в исходном файле? - PullRequest
0 голосов
/ 09 ноября 2019

Я пытаюсь реализовать стек, используя массив фиксированной длины. Я хочу создать несколько экземпляров Stack, поэтому моя реализация стека находится в моем заголовке stack.h.

stack.h


#ifndef STACK_H
#define STACK_H

typedef int Item;

typedef struct stack{
        Item array[SIZE];
        int current_size;
}Stack;

Stack *create_stack(void);
void push_stack(Stack *stack, Item value);
Item pop_stack(Stack *stack);

#endif

Однако я хочу сохранить SIZE массива приватным, поэтому я определил его в стеке. .c

stack.c

#include "stack.h"

#define SIZE 100

Stack *create_stack(void)
{
    ....
}

void push_stack(Stack *stack, Item value)
{
    ....
}

Item pop_stack(Stack *stack)
{
    ....
}

Возможно ли это? Макрос определен в исходном файле, но использован в заголовочном файле? Спасибо

Ответы [ 2 ]

1 голос
/ 09 ноября 2019

Технически, вы должны рассматривать инструкцию препроцессора #include как инструкцию «копировать и вставить».

В основном это означает, что конечным результатом является один файл, содержащий весь текст в заказе

Конечно, в этом случае у вас есть ошибка в вашем коде, поскольку SIZE определяется только после .

Учтите, в stack.c:

#define SIZE 100
#include "stack.h"

Это позволило бы использовать SIZE в stack.h.

Кроме того, не определяя SIZE в заголовке (по крайней мере, в качестве запасного значения). ), вы не сможете нигде включить заголовок, не указав сначала SIZE.

Рассмотрите в заголовке добавление:

#ifndef SIZE
#define SIZE 10
#endif

ИМХО, такой подход неидеальный способ использовать эту возможность, но это должно ответить на ваш вопрос.

Наилучшим подходом будет, IMHO, использование неполного типа или «гибкого массива».

Например,Вы можете определить тип, используя:

typedef struct stack{
        int current_size;
        Item array[];
} Stack;

Затем выделите достаточно памятиМожно управлять размером стека с помощью (например):

Stack * s = malloc(sizeof(*s) + (SIZE*sizeof(Item)));

Однако я, вероятно, предпочел бы сделать тип непрозрачным указателем.

Это означает, что данные будут доступны только при использованиифункции.

Использование непрозрачного указателя обеспечило бы гораздо более сильное разделение.

Это также полезно, позволяя обновлять тип данных в будущем без нарушения совместимости ABI / обратной совместимости.

то есть, в заголовке ограничьте себя:

typedef struct stack Struct;

Функции будут использовать указатель на неполный тип.

Stack *create_stack(void);

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

Файл C определит структуру только для внутреннего использования:

struct stack{
        Item array[SIZE];
        int current_size;
};

Или, используя динамический размерПодход, как отмечалось выше:

struct stack{
        int current_size;
        Item array[];
};


Stack *create_stack(size_t size)
{
    Stack * s = malloc(sizeof(*s) + (size * sizeof(*s->array)));
    // ....
    return s;
}

PS - IMHO

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

В Си вы обнаружите, что большинство разработчиков склонны называть вещи, используя snake_case, а не CamelCase.

Кроме того, typedefs часто имеют суффикс, подобный суффиксу (зарезервированный) _t, используемому в POSIX и некоторых стандартныхтипы (т. е. типы структур могут заканчиваться _s).

Это всего лишь мое личное мнение , но, как считыватель длинного кода, мне легче читать код, который соответствуетстиль стандартной библиотеки, предоставляемый языком. Очевидно, что не все согласны со мной.

Используя эти соглашения об именах, я бы назвал эти типы как:

typedef int item_i;

typedef struct stack{
        int current_size;
        item_i array[SIZE];
} stack_s;
0 голосов
/ 09 ноября 2019

Да, это очень возможно. Вам просто нужно поставить строку #define SIZE 100 над строкой #include "stack.h". Pre-Processor C в основном будет копировать / вставлять содержимое файла заголовка в положение, в которое он был включен, поэтому, если вы определите SIZE выше, где вы его включаете, тогда это содержимое файла заголовка будет помещено под негои скомпилируйте просто отлично.

Примечание: в зависимости от ваших настроек, вы можете запустить только препроцессор C, используя cpp source.c, и вы сможете увидеть, как код собирается доотправляется компилятору.

...