У меня есть задание, в котором было предложено использовать сокрытие данных, в первую очередь использовать один файл заголовка вместо двух. Я следую учебнику, который нам предоставили, но компилятор все еще не может определить размер структуры. Если вас сдерживает тот факт, что это задание, вы должны знать, что это не входит в мою оценку и что я хочу знать, как на самом деле использовать сокрытие данных для дальнейшего использования.
Ниже приведена реализация стека в виде массива фиксированной длины после того, как я отредактировал заголовочный файл, включив в него определения структур, определения препроцессора и любые структуры, из которых агрегируется реализация:
#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_
Я знаю, что у многих людей были подобные проблемы, но темы, которые я нашел, пока не пригодились. Пожалуйста, не перенаправляйте меня просто в тему, которая может быть похожей, но все же не той же проблемой. Это не говорит мне о том, что не так в контексте этой проблемы, которая, собственно, и интересует меня.
Мой вопрос заключается в том, почему компилятор не может определить размер реализации стека после попытки использовать сокрытие данных?
Я все еще не понимаю вопроса. Пожалуйста, опубликуйте код, в котором возникает ошибка, и точное сообщение об ошибке.
Если вы используете непрозрачный указатель на неполный тип, вы не сможете получить размер структуры, на которую он указывает, поскольку определение структуры не было обнаружено.
Это нормально, когда вы используете структуры для сокрытия информации. Если вы скроете определение, вы скроете и размер.
Да, это правда. Моя проблема в том, что вы можете использовать сокрытие информации и при этом компилятор найдет размер в определении в исходном коде, или мне так сказали. Это то, чего я пытаюсь достичь.
Вам сказали неправильно или неправильно поняли. Если вы скроете это от программиста, вы также скроете это от компилятора.
Это очень прискорбно. Я знаю, что это может быть глупый вопрос, но как я могу использовать структуру, если тогда я скрываю ее содержимое? Похоже, я не могу объявить его, если его размер не известен компилятору. Я вижу, что вы можете определить структуру по самому указателю, может ли это иметь к этому какое-то отношение?
Если вы скрываете содержимое, вам придется вызывать функции, которые видят определение. Вот и весь смысл сокрытия информации. Поскольку пользователь не знает определения структуры, вы можете изменить его, не изменяя вызывающую сторону. Вам просто нужно обновить функции библиотеки.
Я понимаю, что он используется для инкапсуляции, моя проблема заключается в простом объявлении структуры. Тестовый файл, который я использовал, не имеет доступа к содержимому структуры, вместо этого он использует его, как вы описываете, с методами, имеющими доступ к данным. Я все еще получал ошибку «неизвестный размер».
Вы не можете объявить структуру, не определив ее содержимое. Невозможно сказать: «Стек структур составляет 16 байт, но мы не говорим вам, что находится в этих байтах».
Вот почему мы всегда используем указатели, когда скрываем информацию.
Если мне просто нужно использовать указатели вместо передачи структуры по ссылке, то я считаю, что это мое решение. Кажется, мне не сказали об этом, и я не ожидал, что так подумаю, учитывая, что typedef для указателей.
Вот файл с форвардной декларацией: 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; }
Вот «частный» .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); }
Обратите внимание, что, по моему мнению, «сокрытие информации» — это плохо. Несколько новых руководств по стилю не одобряют этого. На этих двух примерах вы можете понять, почему. Используйте «скрытие» только в особых случаях.
почему компилятор не может определить размер реализации стека после попытки скрыть данные?
Потому что другие исходные файлы, включающие 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;
}
Хотя это хороший и довольно очевидный ответ, оглядываясь назад, код, который вы упомянули, был предоставлен, и у меня сложилось впечатление, что я не собираюсь его менять.
Спасибо Бармару за разъяснение мне, как реализовано сокрытие информации.
Кажется, здесь нет никаких вопросов. В чем проблема с вашим кодом?