Нет значения по умолчанию для вашего указателя. Ваш указатель будет указывать на то, что он хранит в настоящее время. Как вы еще не инициализировали, строка
newCell.subcells[i] = ...
Эффективно обращается к некоторой неопределенной части памяти. Помните, что подэлементы [i] эквивалентны
*(newCell.subcells + i)
Если левая сторона содержит мусор, вы добавите i
к значению мусора и получите доступ к памяти в этом неопределенном месте. Как вы правильно сказали, вам придется инициализировать указатель так, чтобы он указывал на некоторую допустимую область памяти:
newCell.subcells = malloc(bytecount)
После какой строки вы можете получить доступ к такому количеству байтов. Что касается других источников памяти, существуют разные виды хранилищ, которые все используют. Какой тип вы получите, зависит от того, какой у вас объект и какой класс хранилища вы сообщаете компилятору.
malloc
возвращает указатель на объект без типа. Вы можете указать указатель на эту область памяти, и тип объекта фактически станет типом указателя на тип объекта. Память не инициализируется каким-либо значением, и доступ обычно медленнее. Полученные таким образом объекты называются allocated objects
.
- Вы можете размещать объекты по всему миру. Их память будет обнулена. За очки вы получите NULL-указатели, а для поплавков вы также получите правильный ноль. Вы можете положиться на правильное начальное значение.
- Если у вас есть локальные переменные, но используется спецификатор класса хранения
static
, то у вас будет то же правило начальных значений, что и для глобальных объектов. Память обычно распределяется так же, как глобальные объекты, но это ни в коем случае не является необходимостью.
- Если у вас есть локальные переменные без какого-либо спецификатора класса хранения или с
auto
, тогда ваша переменная будет размещена в стеке (даже если это не определено в C, это то, что компиляторы делают практически). Вы можете взять его адрес, и в этом случае компилятору придется пропустить оптимизацию, например, поместить ее в регистры.
- Локальные переменные, используемые со спецификатором класса хранилища
register
, помечаются как имеющие специальное хранилище. В результате вы не можете больше узнать его адрес. В последних компиляторах обычно нет необходимости использовать register
из-за их сложных оптимизаторов. Если вы действительно эксперт, то вы можете получить некоторую производительность, если будете его использовать.
У объектов есть связанные сроки хранения, которые можно использовать для отображения различных правил инициализации (формально они определяют только как долго, по крайней мере, объекты живут). Объекты, объявленные с auto
и register
, имеют автоматическую продолжительность хранения и инициализируются , а не . Вы должны явно инициализировать их, если хотите, чтобы они содержали какое-то значение. Если вы этого не сделаете, они будут содержать то, что компилятор оставил в стеке до того, как они начали жизнь. Объектам, которые выделены malloc
(или другой функции этого семейства, например calloc
), выделена продолжительность хранения. Их хранилище не инициализировано либо. Исключением является использование calloc
, когда память инициализируется в ноль («реальный» ноль. Т.е. все байты 0x00, без учета какого-либо представления указателя NULL). Объекты, объявленные с static
и глобальными переменными, имеют статическую продолжительность хранения. Их хранилище инициализируется нулем, соответствующим их соответствующему типу. Обратите внимание, что объект не должен иметь тип, но единственный способ получить объект без типа - использовать выделенное хранилище. (Объект в C является «областью хранения»).
Так что к чему? Вот фиксированный код. Поскольку после того, как вы выделили блок памяти, вы не можете больше узнать, сколько элементов вы выделили, лучше всегда хранить где-то это количество. Я ввел переменную dim
в структуру, которая хранит счетчик.
Cell makeCell(int dim) {
/* automatic storage duration => need to init manually */
Cell newCell;
/* note that in case dim is zero, we can either get NULL or a
* unique non-null value back from malloc. This depends on the
* implementation. */
newCell.subcells = malloc(dim * sizeof(*newCell.subcells));
newCell.dim = dim;
/* the following can be used as a check for an out-of-memory
* situation:
* if(newCell.subcells == NULL && dim > 0) ... */
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim - 1);
}
return newCell;
}
Теперь все выглядит примерно так: dim = 2:
Cell {
subcells => {
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
},
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
}
},
dim = 2
}
Обратите внимание, что в C возвращаемое значение функции не обязательно должно быть объектом. Для хранения вообще не требуется никакого хранилища. Следовательно, вы не можете изменить его. Например, следующее невозможно:
makeCells(0).dim++
Вам понадобится «свободная функция», которая снова освобождает выделенную память. Поскольку хранилище для выделенных объектов не освобождается автоматически. Вы должны вызвать free
, чтобы освободить эту память для каждого subcells
указателя в вашем дереве. Это оставлено для вас, чтобы написать это:)