PDA

Показать полную графическую версию : Вопрос по динамическим массивам


nikit-xxx
09-05-2011, 18:42
Объясните пожалуйста разницу между
int b[4][5][6]; и
int i, j;

int ***b;
b = (int***) malloc(4 * sizeof(int**));
for(i = 0; i < 4; i++){
b[i] = (int**) malloc(5 * sizeof(int*));
for(j = 0; j < 5; j++){
b[i][j] = (int*) malloc(6 * sizeof(int));
}
}


Спрашиваю потому что:
Следующий код не компиллится, если в формальном параметре 3-х мерный массив объявить "обычным" способом (закомментировано):
пишет ошибку: строка 23 | error: cannot convert 'int***' to 'int (*)[5][6]' for argument '1' to 'void test_function(int (*)[5][6])'

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

void test_function(int ***e);
//void test_function(int e[][5][6]); //- вот так не компиллится

int
main(void)
{
int i, j;

int ***b;
b = (int***) malloc(4 * sizeof(int**));
for(i = 0; i < 4; i++){
b[i] = (int**) malloc(5 * sizeof(int*));
for(j = 0; j < 5; j++){
b[i][j] = (int*) malloc(6 * sizeof(int));
}
}
b[2][3][4] = -123;

test_function(b);

printf("b[2][3][4] = %d\n", b[2][3][4]);

getch();
return(0);
}

void
test_function(int ***e)
/*void
test_function(int e[][5][6])*/ // вот так не компиллится
{
;
}

Beyound
10-05-2011, 19:42
разница в способе выделения памяти.

в первой цитате под переменную b будет выделено последовательно 4*5*6 полей int. последовательных. обязательно ровно 4*5*6 чтобы не случилось.

во второй цитате память выделяется динамически. сначала выделяется ячейка памяти размером 4*int** и ее адрес записывается в b(тройной указатель). Затем в цикле в кусок размером int** (двойной указатель) хранящемуся по адресу b[i] (то что было выделено ранее) записывается адрес вновь выделяемого куска памяти размером 5*int*. В каждый кусочек по адресу b[i][j] мы записываем адрес вновь выделяемой ячейки памяти размером 6*int. И того получаем у нас 4 двойных указателя, каждый из которых хранит адрес 5-и указателей, которые указывают на 6*int памяти.

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

на счет ошибки
В теле мейна ты своей функции подсовываешь тройной указатель. А в объявлении функции ты говоришь о какомто непойми e[][5][6] который явно не тройной указатель. учи английский ))

nikit-xxx
10-05-2011, 22:04
сообщение компилятора на английском мне было понятно, я просто думал что объявление массива в первой цитате и массива во второй цитате - одно и тоже. Да терь понимаю, что во второй цитате указатель на указатель на указатель на тип int.
Спасибо.

nikit-xxx
12-05-2011, 20:39
Кстати, а в Кернигане говорится что
Следующие две формы эквивалентны, если употребляются в виде формальных
параметров функции:
char s [] ;
char *s;



Ещё вопрос по функциям malloc / free:
int *t;
t = (int*) malloc(5);
картинка с моими предположениями:
http://imageshack.us/m/691/2136/55191666.png

Теперь t указывает на самую первую ячейку памяти, отведённую для типа int ?
Тогда откуда free(t) узнает сколько ячеек следует освободить?

PhilB
12-05-2011, 21:59
malloc является системным вызовом, он запрашивает память в куче у ОС.
free также является системным вызовом, он освобождает участок памяти в куче.
ОС хранит информацию о выделенной памяти в своих внутренних структурах, к которым оно и обращается при вызове malloc , free, таким образом ОС знает сколько памяти надо освободить.
t = (int*) malloc(5); - выделит лишь 5 байт, а вам надо t = (int*) malloc(5*sizeof(int)); - 5 элементов типа int (на 32бит архитектуре - 20 байт)
Теперь t указывает на самую первую ячейку памяти, отведённую для типа int ? »
Именно так.

El Scorpio
13-05-2011, 03:46
В первом случае память выделяется в стеке последовательно - идентификатор массива представляет собой "указатель первого порядка" (int *), а доступ к элементу одномерного массива осуществляется через сложение индексов (I = I1 + I2*N2 + I3*N3)
Во втором случае память выделяется в стеке отдельными блоками - идентификатор массива представляет собой "указатель третьего порядка" (int ***), а доступ к элементу массива осуществляется через последовательное обращение по адресам указателей (к I1-му элементу массива указателей второго порядка, оттуда к I2-му элементу массива указателей первого порядка, а уже оттуда к I3-му элементу )

пишет ошибку: строка 23 | error: cannot convert 'int***' to 'int (*)[5][6]' for argument '1' to 'void test_function(int (*)[5][6])' »
Для использования статического массива (вариант 1) нужно объявить функцию void test_function(int *array[5][6]) - это позволит программе правильно вычислять индекс для указателя array [i1][i2][i3] по известным "размерам" .

А вот если объявить функцию void test_function (int array [4][5][6]), то произойдёт копирование статического массива в функцию. Понятно, что передавать объекты большого размера крайне нежелательно (занимает "много" времени, да и стэк - не резиновый). Это делается, если в процессе работы функции переданный объект изменится, в то время как оригинальный массив меняться не должен.
Если же изменений не будет, то можно передать объект константной ссылкой test_function (const int &array [4][5][6])

И самое главное, никогда не задавай границы массивов цифрами - только через константы
const int sizeA = 4;
const int sizeB = 5;
const int sizeC = 6;
.................
int Array [sizeA][sizeB][sizeC];
Если использовать обычные цифры, то при изменении размера в одном месте программы можно легко забыть изменить его в другом.

P.S.
t = (int*) malloc(5); - выделит лишь 5 байт, а вам надо t = (int*) malloc(5*sizeof(int)); - 5 элементов типа int (на 32бит архитектуре - 20 байт) »
Для выделения памяти под массивы можно также использовать функцию calloc с двумя параметрами (размер элемента, кол-во элементов) или оператор new []

malloc является системным вызовом, он запрашивает память в куче у ОС.
free также является системным вызовом, он освобождает участок памяти в куче.
ОС хранит информацию о выделенной памяти в своих внутренних структурах, к которым оно и обращается при вызове malloc , free, таким образом ОС знает сколько памяти надо освободить. »
Между прочим, операторы new (new[]) и delete (delete[]) используют несколько другие вызовы, чем функции malloc-realloc-free.




© OSzone.net 2001-2012