LinuxForum.Fans
Тема

Си. двумерный глобальный динамический массив (GCC 4.4.5)

Автор: webber 34 сообщений 8830 просмотров
#317182
Cruiser78 написал:
[quote=Alexey_1]Посмотрите под отладчиком, c выводом значения массива a, вот этот пример:

Не упорствуйте. Я вижу только, что Вы совершенно не понимаете сути организации массивов на Сях, вопросов типизации переменных и вопросы распределения памяти при объявлении переменных там.
Смотреть в отладчике мне Ваш пример незачем. Я и без него знаю, что я там увижу.
А увижу я там ошибку компилятора. Вам подсказать какую именно и в какой строке? Так что до отладчика и дело не дойдет.

Вам я предлагаю решить следующую задачу. Опять же по массивам.
Вопрос: Массивы а[3][4] и b[4][3] и c[12] (выделенные по Вашему правилу malloc(12*sizeof(int)))- тождественны? А именно - на что именно указывают указатели a[1], b[1] и c[1]? А а[3] и b[3]? А на какую ячейку будет казывать указатели a[1][5] и b[1][5]? Что будет происходить, если введем новый указатель int **p и сделаем так:
p=a;
p+=2; - куда показываем?
++p; - куда показываем?
p=b;
p+=2; куда показываем?
++p; Куда показываем?
А если мы сделаем так:
int **p=(int **)malloc(12,sizeof(int)) - куда будет показывать p[1][1]?

Еще раз повторяю - многомерные массивы на С это массивы массивов. Для корректного распределения памяти под эти массивы необходимо вначале распределить массив указателей на массивы значений, а затем - каждому из этих указателей на массив значений присвоить значение адреса начала массива самих значений, адрес начала строки.[/quote]
Если бы Вы соизволили откомпилировать мой пример и прогнать его под отладчиком... Но Вы из своего снобизма только теоретизировать можете.
У мня всё скомпилировалось и я под отладчиком данный пример прогонял...

#317198

Мне кажется вы о разном.

text

int p1[N1][N2];
int *p2 = (int *) malloc(N1*N2*sizeof(int));

вполне себе похожи. Только доступ во втором случае будет p2[x*N1+y].

text

#include <stdio.h> 
#include <stdlib.h>
 
int main(void) { 
  int a[4][4];
  int *c = a;
  int *b = (int*)malloc(4*4*sizeof(int));
  int i,j;
  for (i = 0; i<4; ++i)
    for (j = 0; j<4; ++j)
    {
        a[i][j] = i*10+j;
        b[i*4+j] = i*10+j;
    }
  c+=10;
  b+=10;
  printf("%d\n", *c);
  printf("%d\n", *b);
  return 0; 
} 
#317204
"Cruiser78⇓" написал:
Еще раз повторяю - многомерные массивы

Угу многомерный массив интов. Это такой же последовательный кусок в памяти содержащий друг за другом инты.

#317207
"Cruiser78⇓" написал:
Вопрос: Массивы а[3][4] и b[4][3] и c[12] (выделенные по Вашему правилу malloc(12*sizeof(int)))-

С точки зрения размещения в памяти да. Доступ только по разному. Хотя если получить указатель на начало области памяти. То по одному и тому же смещению будет содержаться одно и то же значение.

#317223
marikk написал:
text

    int a[2][3];
    int *pa;
    pa=a;
text

int а[3][4];
int **p;
p=a;

Разве такое можно откомпилировать ?

Именно второй пример откомпилировать можно. А вот первый - никак нельзя. Ошибка выйдет на строке pa=a. Ошибка несоответствия типов переменных.
Правильно:

text
int a[3],*p;
p=a;

или

text
int a[3][3],**p;
p=a;

или (этот синтаксис рекомендую настоятельно)

text

typedef INT *int;
int a[3][3];
INT *p;
p=a;

Во всех этих примерах типы переменных соответствуют друг другу.
А вот в примере

text
int a[3][3],*p;
p=a;

тип переменной p не соответствует типу переменной а. Переменная "а" есть адрес массива адресов массивов целых чисел, а "p" - адрес массива целых чисел. Если в компиляторе отключить опциями строгий контроль типов, то оно скомпилируется. Но на выходе (в реальной задаче) будет глюкало страшное. Если же компилятор контролирует типы переменных, то получим, как минимум, предупреждение о несоответствии типов.

То, что компилятор соорудит на стеке единую непрерывную область для всех значений матрицы а - ничего не значит. Компилятор так же проинициализирует (помимо этой области) и массив а[3], вписав в него нужные указатели на a[0][0], a[1][0],a[2][0].
Можно иметь такое:

text
int a[3][3],*p;
p=a;

То есть указать явное приведение типа и тогда p[0] будет равно a[0][0], а p[8] будет равно а[2][2].
Но вот сотворить двумерный массив таким образом (я специально пишу явное приведение типов):

text
int **p;
p=(int **)malloc(9,sizeof(int));

невозможно. Что мы прочитаем из такого p[0][0]? Ответ - нулевая ячейка такого p (p[0]) всегда трактуется как указатель на начало (первый элемент) массива адресов массивов. malloc (в отличие от calloc) не инициализирует распределяемые области памяти. Следовательно там лежит случайный мусор. Таким образом наш массив p[0] будет начинаться со случайного адреса. И если мы начнем туда писать, в p[0][0] то потрем всё что угодно. Ну, а p[1] - вообще непонятно что содержит...

Именно поэтому во всех книжках по С русским и английским по понятному пишут. Язык С поддерживает контроль типов. Но этот контроль можно обмануть, отключить. И тогда эффекты при исполнении программы будут непредсказуемы. Чтобы этого избежать категорически недопустимо отключать контроль типов. И если компилятор начинает ругаться на неосоответствие типов переменных - тщательно анализировать код. Возможно там грубая ошибка.

Возможность прямой работы с памятью это сильная сторона С. Но вот эта сила в руках дикаря... :) Прихвата от некорректных действий нет. Страшная штука. Знать язык С это не значит уметь пользоваться функцией main() и так далее. Знать язык С - это понимать как он распределяет память и как с ней надлежит работать, как контролировать типы переменных.

Надеюсь то, что я изложил понятно?

#317224

Не понял задачу :)

"Cruiser78⇓" написал:
Вопрос: Массивы а[3][4] и b[4][3] и c[12] (выделенные по Вашему правилу malloc(12*sizeof(int)))- тождественны? А именно - на что именно указывают указатели a[1], b[1] и c[1]? А а[3] и b[3]? А на какую ячейку будет казывать указатели a[1][5] и b[1][5]?  Что будет происходить, если введем новый указатель int **p и сделаем так:

p=a;
p+=2; - a[0][2];
++p; - a[0][3];
p=b;
p+=2; - b[0][2];
++p;  - b[1][0];

#317230

Если вопрос именно в доступе к элементам. То да - естественно разница будет. И нужно понимать как получить доступ к тому элементу к которому нужен. И тут надо выбирать по задаче. Чохом выделить память сразу для всех элементов одним
malloc(N1*N2*sizeof(int));
или

text

array=(INT *)malloc(N*sizeof(INT))
for (int i=0; i<N; i++) array[i]=(INT)malloc(M*sizeof(int));

Согласитесь писать a[x][y] или a[x*N1+y] - отличается по сути только удобством на стадии написания кода. сегментация памяти и расходы на вызов malloc - более "интересные" параметры.

#317234
voral написал:
[quote="Cruiser78⇓"]Вопрос: Массивы а[3][4] и b[4][3] и c[12] (выделенные по Вашему правилу malloc(12*sizeof(int)))-

С точки зрения размещения в памяти да. Доступ только по разному. Хотя если получить указатель на начало области памяти. То по одному и тому же смещению будет содержаться одно и то же значение.[/quote]

Что такое "смещение"?
Имеем массив a[N][M]. То есть некую переменную "а" за которой стоит наша матрица.
Простые вопросы, если к переменной a прибавить 1 (то есть задать некое "смещенние"), то что будет на выходе? При b = а+1, что есть b? Каково содержимое этого b? Это будет указатель на вторую строку нашей матрицы a, или b будет содержать второе значение первой строки матрицы "а"? И что будет с программой, если мы неправильно ответим на этот вопрос?

#317239
"Cruiser78⇓" написал:
Но вот сотворить двумерный массив таким образом (я специально пишу явное приведение типов):
"Cruiser78⇓" написал:
Но вот сотворить двумерный массив таким образом (я специально пишу явное приведение типов):
int **p;
p=(int **)malloc(9,sizeof(int));

А тут вообще не понятно что случайно работающее. Как минимум тут тогда уж должно быть размер не инта, а указателя на инт. Т.к. судя по приведению вы хотите иметь указатель на массив указатлей и эта строка не станет причиной багов только если размер указателя равен размеру инт.

#317241
"Cruiser78⇓" написал:
Простые вопросы, если к переменной a прибавить 1 (то есть задать некое "смещенние"), то что будет на выходе? При b = а+1, что есть b? Каково содержимое этого b? Это будет указатель на вторую строку нашей матрицы a, или b будет содержать второе значение первой строки матрицы "а"? И что будет с программой, если мы неправильно ответим на этот вопрос?

Ну так С вообще такая штука которая подразумевает осознанный подход.
Может я не правильно понял вопрос. Попробую ответить примером:

text

a[0][0]=1;
a[0][1]=2;
a[1][0]=3;
a[1][1]=4;

int *b=a; // *b вернет 1 ++b; // *b вернет 2 ++b; // *b вернет 3 ++b; // *b вернет 4

#317243
voral написал:
Не понял задачу :)
[quote="Cruiser78⇓"]Вопрос: Массивы а[3][4] и b[4][3] и c[12] (выделенные по Вашему правилу malloc(12*sizeof(int)))- тождественны? А именно - на что именно указывают указатели a[1], b[1] и c[1]? А а[3] и b[3]? А на какую ячейку будет казывать указатели a[1][5] и b[1][5]?  Что будет происходить, если введем новый указатель int **p и сделаем так:

p=a;
p+=2; - a[0][2];
++p; - a[0][3];
p=b;
p+=2; - b[0][2];
++p;  - b[1][0];[/quote]

неверно. Переменная p имеет тип "int **" то есть указатель на указатель. То есть увеличивая на единицу этот указатель мы идем не по столбцам матрицы, не по её ячейкам, а по её строкам, по указателям на её строки. То есть:
p=a;
тогда
p[0] = a[0];
p[1] или p+1 = a[1]
p[2] или p+2 = a[2] и это всё не значения ячеек, а адреса строк нашей матрицы.
а p[3], как и a[3] показывают в небо, ибо там сидят случайные числа со стека.
Другое дело, если мы сделаем переменную p типа int * и сотворим такое:
p=a[0]. Вот тогда увеличивая p на единичку мы пойдем перебирать значения ячеек нашей матрицы. И то только в том случае, если она распределена непрерывным блоком.

#317255
Cruiser78 написал:
неверно. Переменная p имеет тип "int **" то есть указатель на указатель.

Ну так в задаче это не было указано.... Естественно это уже сразу ошибка.

Cruiser78 написал:
Другое дело, если мы сделаем переменную p типа int * и сотворим такое:
p=a[0]. Вот тогда увеличивая p на единичку мы пойдем перебирать значения ячеек нашей матрицы. И то только в том случае, если она распределена непрерывным блоком.

Так и я о том.


Только тогда какой смысл вообще смотреть что будет в ячейке. Если ошибка на более ранней стадии кроется. Надо понимать чем отличается указатель на величину и сама величина. Работать с двумерным массиывом для которого разом выделена память можно. Надо это делать правильно. Начиная с выделения памяти:
код ниже УЖЕ содержит ошибку

text

int **p;
p=(int **)malloc(9,sizeof(int));
#317313
voral написал:
[quote=Cruiser78]неверно. Переменная p имеет тип "int **" то есть указатель на указатель.

Ну так в задаче это не было указано.... Естественно это уже сразу ошибка.
[/quote]
Смотрите внимательно мой оригинальный текст. Именно что указано. Я специально весь этот тред замутил, чтобы показать, что с памятью в сях надо обращаться очень аккуратно. Ошибиться, опечататься можно очень легко, компилятор твою ошибку воспримет как твое правильное желание и его исполнит. Ну а потом...

voral написал:
код ниже УЖЕ содержит ошибку
text

int **p;
p=(int **)malloc(9,sizeof(int));

Формально ошибки нет. Мы запросили некий блок памяти фиксированного объема в 18, наверное, байт (если целое - 2 байта), и только наше дело, как мы его будем использовать. Вот захотелось мне так заказать память под небольшой массив на пару-другую адресов (если адрес - 4 байта). Потом я их туда впишу, как адреса, по 4 байта на адрес. Где здесь ошибка? Здесь нет ошибки (компиляторной). Здесь, скорее, дурной тон программирования. Правильнее, конечно, написать - p=(int **)malloc(4,sizeof(int*)); Это читабельнее. Но нужное количество памяти отведется и по p=(int **)malloc(8,sizeof(int)); А вот использоваться она будет в соответствии с тем, какой тип имеет наша переменная, указывающая на эту память. Если она будет иметь тип int *, то этот блок будем читать-писать как целые числа, если int**, то как адреса.
И в заключение. Наш двумерный массив int a[3][3], переменная а, имеет тип именно int **, а не int *. И если напутать в типах, то получим непредсказумые ошибки. Содержимое а[2] - это адрес начала третьей строки массива, а не третий элемент в массиве. А содержимое a[8] - это вообще невесть что, некий произвольный адрес снятый со стека. Но никак не 9-й элемент матрицы.

#317337
"Cruiser78" написал:
И если напутать в типах, то получим непредсказумые ошибки

Я, конечно, не спец в программировании, но думаю, тут мы получим предупреждение об ошибке что-то про "несогласованные типы данных"... Но это уже лирика...

Войдите или зарегистрируйтесь, чтобы ответить.