Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   Вопрос по динамическим массивам (http://forum.oszone.net/showthread.php?t=206509)

nikit-xxx 09-05-2011 18:42 1673198

Вопрос по динамическим массивам
 
Объясните пожалуйста разницу между
Код:

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 1673908

разница в способе выделения памяти.

в первой цитате под переменную 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 1673977

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

nikit-xxx 12-05-2011 20:39 1675170

Кстати, а в Кернигане говорится что
Цитата:

Следующие две формы эквивалентны, если употребляются в виде формальных
параметров функции:
char s [] ;
char *s;


Ещё вопрос по функциям malloc / free:
Код:

int *t;
t = (int*) malloc(5);

картинка с моими предположениями:


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

PhilB 12-05-2011 21:59 1675220

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

Цитата nikit-xxx
Теперь t указывает на самую первую ячейку памяти, отведённую для типа int ? »

Именно так.

El Scorpio 13-05-2011 03:46 1675351

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

Цитата:

Цитата nikit-xxx
пишет ошибку: строка 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.
Цитата:

Цитата PhilB
t = (int*) malloc(5); - выделит лишь 5 байт, а вам надо t = (int*) malloc(5*sizeof(int)); - 5 элементов типа int (на 32бит архитектуре - 20 байт) »

Для выделения памяти под массивы можно также использовать функцию calloc с двумя параметрами (размер элемента, кол-во элементов) или оператор new []

Цитата:

Цитата PhilB
malloc является системным вызовом, он запрашивает память в куче у ОС.
free также является системным вызовом, он освобождает участок памяти в куче.
ОС хранит информацию о выделенной памяти в своих внутренних структурах, к которым оно и обращается при вызове malloc , free, таким образом ОС знает сколько памяти надо освободить. »

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


Время: 05:16.

Время: 05:16.
© OSzone.net 2001-