Один объект, который, кажется, был пропущен, является звездообразными преобразованиями. операторы Index Intersection разрешают предикат путем вычисления набора строк, пораженных каждым из предикатов, прежде чем любой ввод-вывод будет сделан на таблице фактов. На схеме "звезда" Вы индексировали бы каждый отдельный ключ измерения, и оптимизатор запросов может решить который строки выбрать индексным перекрестным вычислением. Индексы на отдельных столбцах дают лучшую гибкость для этого.
Взгляните на этот пример библиотеки, используя общедоступный файл заголовка, частный файл заголовка и файл реализации.
В файле public.h :
struct Point;
Point* getSomePoint();
В файле private.h :
struct Point
{
int x;
int y;
}
В файле private.c :
Point* getSomePoint()
{
/* ... */
}
Если вы объединяете эти три файла в библиотеку, вы передаете потребителю библиотеки только public.h и объектный файл библиотеки. .
getSomePoint
должен возвращать указатель на Point
, потому что public.h не определяет размер Point
, только это struct и что она существует.Пользователи библиотеки могут использовать указатели на Point
, но не могут получить доступ к элементам или скопировать их, потому что они не знают размер структуры.
Относительно вашего дальнейшего вопроса: Вы не можете разыменовать, потому что программа, использующая библиотеку, имеет только информацию из private.h , которая не содержит объявлений членов. Следовательно, он не может получить доступ к элементам точечной структуры.
Вы можете рассматривать это как функцию инкапсуляции C, точно так же, как вы объявляете члены данных класса C ++ как частные.
Он имеет в виду, что вы не можете вернуть структуру по значению в заголовке, потому что для этого структура должна быть полностью объявлена. Но это происходит в файле C (объявление, которое делает X
полным типом, «скрыто» в файле C и не отображается в заголовке) в его примере. Следующее объявление объявляет только неполный тип, если это первое объявление структуры
struct X;
Затем вы можете объявить функцию
struct X f(void);
Но вы не можете определить функцию, потому что вы не можете создать переменную этого типа, и тем более вернуть его (размер неизвестен).
struct X f(void) { // <- error here
// ...
}
Ошибка возникает из-за того, что «x» все еще не заполнен. Теперь, если вы включаете только заголовок с неполным объявлением в нем, вы не можете вызвать эту функцию, потому что выражение вызова функции приведет к неполному типу, что запрещено.
Если бы вы предоставили объявление полного типа struct X
между ними, оно было бы действительным
struct X;
struct X f(void);
// ...
struct X { int data; };
struct X f(void) { // valid now: struct X is a complete type
// ...
}
Это применимо и к способу, использующему typedef
: они оба название того же (возможно, неполного) типа. Один раз с использованием обычного идентификатора X
, а в другой раз с использованием тега struct X
.
struct X
. и в другой раз с использованием тега struct X
. Это сообщение означает следующее: если вы видите заголовок
typedef struct _Point Point;
Point * point_new(int x, int y);
, значит, вы не знаете деталей реализации Point
.
В заголовочном файле:
typedef struct _point * Point;
После того, как компилятор увидит это, он знает:
Компилятор не знает:
И не только компилятор этого не знает - мы, программисты, не знаем » я тоже этого не знаю. Это означает, что мы не можем писать код, который зависит от этих свойств _point, а это означает, что наш код может быть более переносимым.
Используя приведенный выше код, вы можете написать такие функции, как:
Point f() {
....
}
, потому что Point - это указатель, а все указатели имеют одинаковый размер, и компилятору не нужно ничего знать о них. Но вы не можете написать функцию, которая возвращает значение по значению:
_point f() {
....
}
, потому что компилятор ничего не знает о _point, особенно его размер, который необходим для построения возвращаемого значения.
Таким образом, мы можем ссылаться на _point только через тип Point, который на самом деле является указателем. Вот почему в Стандартном C есть такие типы, как FILE, к которым можно получить доступ только через указатель - вы не можете создать экземпляр структуры FILE в своем коде.
В качестве альтернативы использованию непрозрачных указателей (как уже упоминалось другими), вы можете вместо этого вернуть непрозрачный пакет байтов, если хотите избежать использования кучи памяти:
// In public.h:
struct Point
{
uint8_t data[SIZEOF_POINT]; // make sure this size is correct!
};
void MakePoint(struct Point *p);
// In private.h:
struct Point
{
int x, y, z;
};
void MakePoint(struct Point *p);
// In private.c:
void MakePoint(struct Point *p)
{
p->x = 1;
p->y = 2;
p->z = 3;
}
Затем вы можете создать экземпляры структуры в стеке в клиентском коде, но клиент не знает, что в нем - все, что он знает, это то, что это большой двоичный объект с заданным размером. Конечно, он все еще может получить доступ к данным, если он может угадать смещения и типы данных членов, но опять же, у вас та же проблема с непрозрачными указателями (хотя клиенты не знают размер объекта в этом случае).
Например, различные структуры, используемые в библиотеке pthreads
, используют структуры непрозрачных байтов для таких типов, как pthread_t
, pthread_cond_t
и т. Д. - вы все еще можете создавать экземпляры тех, что находятся в стеке (и вы обычно это делаете), но вы не знаете, что в них находится. Просто загляните в ваш /usr/include/pthreads.h
и различные файлы, которые он включает.