C: Как вы моделируете «экземпляр»? - PullRequest
1 голос
/ 07 августа 2009

Допустим, у меня есть следующий код в C, который представляет стек:

#define MAX 1000

int arr[MAX];
static int counter = 0;
isstackempty()
{
    return counter <= 0;
}
void push(int n)
{
    if (counter >= MAX) {
        printf("Stack is full.  Couldn't push %d", n);
        return;
    }
    arr[counter++] = n;
}

int pop(int* n)
{
    if(isstackempty() || n == 0) {
        printf("Stack is empty\n");
        return 0;
    }
    *n = arr[--counter];
    return 1;
}

Приведенный выше код находится в файле stack.c, а прототипы функций - в заголовке.


Теперь, исходя из фона C # и OO, если бы я хотел отделить stack s для использования в моем приложении, на языке OO я бы создал два экземпляра. Но в Си, как вы справляетесь с таким сценарием?

Скажем, я хочу использовать два отдельных stack в моем C-коде ... с приведенным выше кодом, как бы я поступил?

Ответы [ 7 ]

10 голосов
/ 07 августа 2009

Поместите массив arr внутрь struct.

struct stack {
    int arr[MAX];
    ...
}

Эта структура становится вашим экземпляром. Затем вы можете объявить его в стеке:

struct stack mystack;

или в куче, используя malloc:

struct stack *mystack = malloc(sizeof(struct stack));

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

7 голосов
/ 07 августа 2009

Способ C сделать это состоит в том, чтобы обернуть все состояния для вашего «объекта» в структуру, а затем явно передать ее во все функции, которые работают со стеками, поэтому должно быть:

typedef struct _stack {
  int arr[MAX];
  int counter;
} stack;

int isstackempty(stack *s)
{
    return s->counter <= 0;
}

int push(stack *s, int n)
{
    if (s->counter >= MAX) {
        printf("Stack is full.  Couldn't push %d", n);
        return -1;
    }
    arr[s->counter++] = n;
    return 0
}

int pop(stack *s, int *n)
{
    if(isstackempty(s) || n == 0) {
        printf("Stack is empty\n");
        return -1;
    }
    *n = arr[--s->counter];
    return 0;
}

Проблема с вашим примером заключается в том, что вы пишете определения функций, как будто у нас есть объектная структура на основе классов, которой нет в Си. Самый простой способ подумать о том, как это делается в C, состоит в том, что вы пишете методы, которые требуют явной передачи параметра this.

Также вы можете иметь эквивалент конструкторов и деструкторов, которые могут дополнительно абстрагировать ваш «объект».

stack* newStack() {
    stack* s = malloc(sizeof(stack));
    s->counter = 0;
    return s;
}

void freeStack(stack* s) {
    free(s);
}
3 голосов
/ 07 августа 2009

Один (чрезвычайно упрощенный) способ это сделать - определить структуру, представляющую стек:

typedef struct {
    int arr[MAX];
    int counter = 0;
} myStack;

, а затем переписать push() и pop() для работы с экземпляром myStack:

int push(myStack *s, int n)
{
    if (s->counter >= MAX) {
        printf("Stack is full.  Couldn't push %d", n);
        return -1;
    }
    s->arr[(s->counter)++] = n;
    return s->counter;
}

int pop(myStack *s, int* n)
{
    if(0 == s->counter || 0 == n) {
        printf("Stack is empty\n");
        return -1;
    }
    *n = s->arr[--(s->counter)];
    return 1;
}

(Также добавлено значимое возвращаемое значение и значение ошибки к push(). ГММВ.)

1 голос
/ 07 августа 2009

Надеюсь, вы найдете эту статью полезной. Это дает более одного ответа на ваш вопрос:)

Шестнадцать способов сложить кошку

1 голос
/ 07 августа 2009

Просто сделайте ваш указатель 'this' явным:

struct stack* create_stack();
void push(struct stack* mystack, int n);
void pop(struct stack* mystack, int* n);
0 голосов
/ 08 августа 2009

Динамически размещаемая структура для каждого экземпляра - правильный путь. Дело в том, что если вы пишете более широко используемый API, то, вероятно, будет хорошей идеей скрыть данные для лучшей абстракции. Простейший способ сделать это - сохранить определение внутренней структуры в C-файле (или частном заголовочном файле) и ввести пустой указатель (например,) 'stack_handle_t'. Именно этот тип возвращается из вашего «конструктора» и передается обратно каждой функции. Реализация знает, что значение дескриптора на самом деле является указателем на структуру и в начале каждой функции просто делает:

int pop(stack_handle_t handle, int* n)
{
    stack *p_stack = (stack *)handle;
    ...

Еще лучше использовать вместо этого внутренний идентификатор, будь то индекс в массиве этих структур или просто идентификатор, который можно сопоставить с одним из (связанных?) Списков структур. Очевидно, что все это не имеет значения, если оно предназначено только для внутреннего использования в вашем проекте, в таких обстоятельствах оно просто делает ненужную работу и чрезмерное усложнение.

0 голосов
/ 08 августа 2009

Мой ответ на этот другой вопрос имеет полный рабочий пример структуры буфера данных OO в C.

...