Размер стека хранилища неизвестен

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

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

#include <assert.h>
#include "stack.h"

#define CAP 100

struct stack
{
   ELEM arr[CAP] ;
   int size ;           // # number of elements (size of stack)
   int cap ;   // capacity
} ;

void init( stack* s )
{
        s->size = 0 ;
        s->cap = CAP ;
}

_Bool isEmpty( const stack* s )
{
        return s->size == 0 ;
}

// ... yadda yadda functionality implementation

Следующий код представляет собой реализацию связанного списка с теми же изменениями:

#include <assert.h>
#include <stdlib.h>

#include "stack.h"

typedef struct sNode sNode ;

struct sNode
{
   ELEM data ;
   sNode* next = NULL ;
} ;

struct stack
{
   sNode* head ;
} ;


void init( stack* s )
{
        s->head->next = NULL ;
        return ;
}

// ... yadda yadda more functionality, not struct definitions

А теперь вот заголовочный файл после перемещения определений в исходные файлы:

#ifndef __KS_STACK_H_
#define __KS_STACK_H_

typedef int ELEM ;

typedef struct stack stack ;

// Initialises a new stack
// Must be called before using
void init( stack* s ) ;

// return true of stack is empty
_Bool isEmpty( const stack* s ) ;

// Inserts i on top of stack
// Stack must have space
void push( stack* s, ELEM i ) ;

// Removes item, returns it
// Stack can't be empty
ELEM pop( stack* s ) ;

// Returns item at top of stack
// Does NOT modify the stack
// Stack can't be empty
ELEM top( const stack* s ) ;

#endif // __KS_STACK_H_

Я знаю, что у многих людей были подобные проблемы, но темы, которые я нашел, пока не пригодились. Пожалуйста, не перенаправляйте меня просто в тему, которая может быть похожей, но все же не той же проблемой. Это не говорит мне о том, что не так в контексте этой проблемы, которая, собственно, и интересует меня.

Мой вопрос заключается в том, почему компилятор не может определить размер реализации стека после попытки использовать сокрытие данных?

Кажется, здесь нет никаких вопросов. В чем проблема с вашим кодом?

Barmar 30.05.2024 23:41

Я все еще не понимаю вопроса. Пожалуйста, опубликуйте код, в котором возникает ошибка, и точное сообщение об ошибке.

Barmar 30.05.2024 23:45

Если вы используете непрозрачный указатель на неполный тип, вы не сможете получить размер структуры, на которую он указывает, поскольку определение структуры не было обнаружено.

Barmar 30.05.2024 23:47

Это нормально, когда вы используете структуры для сокрытия информации. Если вы скроете определение, вы скроете и размер.

Barmar 30.05.2024 23:48

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

Graham Hornecker 31.05.2024 00:16

Вам сказали неправильно или неправильно поняли. Если вы скроете это от программиста, вы также скроете это от компилятора.

Barmar 31.05.2024 00:17

Это очень прискорбно. Я знаю, что это может быть глупый вопрос, но как я могу использовать структуру, если тогда я скрываю ее содержимое? Похоже, я не могу объявить его, если его размер не известен компилятору. Я вижу, что вы можете определить структуру по самому указателю, может ли это иметь к этому какое-то отношение?

Graham Hornecker 31.05.2024 00:35

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

Barmar 31.05.2024 00:38

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

Graham Hornecker 31.05.2024 00:42

Вы не можете объявить структуру, не определив ее содержимое. Невозможно сказать: «Стек структур составляет 16 байт, но мы не говорим вам, что находится в этих байтах».

Barmar 31.05.2024 00:43

Вот почему мы всегда используем указатели, когда скрываем информацию.

Barmar 31.05.2024 00:44

Если мне просто нужно использовать указатели вместо передачи структуры по ссылке, то я считаю, что это мое решение. Кажется, мне не сказали об этом, и я не ожидал, что так подумаю, учитывая, что typedef для указателей.

Graham Hornecker 31.05.2024 00:47

Вот файл с форвардной декларацией: struct foo; // forward declaration ; struct foo *foo_new(void); size_t foo_size(void); // fails ... void test_size_1(void) { printf("%zu\n,sizeof(struct foo)); } ; void test_size_2(void) { printf("%zu\n",foo_size()); } ; // fails ... struct foo * test_new_3(void) { struct foo *foo = malloc(sizeof(struct foo)); return foo; } ; struct foo * test_new_4(void) { struct foo *foo = foo_new(); return foo; }

Craig Estey 31.05.2024 00:49

Вот «частный» .c файл: // definition struct foo { int data; struct foo *next; }; struct foo * foo_alloc(void) { ; return malloc(sizeof(struct foo)); } ; size_t foo_size(void) { ; return sizeof(struct foo); } Обратите внимание, что, по моему мнению, «сокрытие информации» — это плохо. Несколько новых руководств по стилю не одобряют этого. На этих двух примерах вы можете понять, почему. Используйте «скрытие» только в особых случаях.

Craig Estey 31.05.2024 00:52
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
1
14
62
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

почему компилятор не может определить размер реализации стека после попытки скрыть данные?

Потому что другие исходные файлы, включающие stack.h, знают только о том, что struct stack он же stack существует, а не о его содержимом. Это означает, что другие файлы не могут создать экземпляр структуры, но могут создать указатель на нее.

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

Итак, вместо этого:

void init( stack* s )
{
    s->size = 0 ;
    s->cap = CAP ;
}

Вам нужно сделать это:

stack *init(void)
{
    stack *s = malloc(sizeof *s);
    s->size = 0;
    s->cap = CAP;
    return s;
}

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

Graham Hornecker 31.05.2024 00:14
Ответ принят как подходящий
  1. Используйте указатели на структуру вместо структуры.
  2. Члены структуры инкапсулированы и будут доступны при передаче обратно в методы файла, в котором они находятся.
  3. Выделяйте новые структуры в куче и возвращайте их, не выделяйте по ссылке.
  4. Введите структуру для указателя на себя или похожее имя, чтобы облегчить клиенту работу со скрытием информации.

Спасибо Бармару за разъяснение мне, как реализовано сокрытие информации.

Другие вопросы по теме