[С++] объявить указатель на двухмерный массив?

Ventalf

Напомните пожалуйста, как объявить указатель на двойной массив типа ?
 __int32 replModule[8][16];

mira-bella

ужос
Когда вы все наконец забудете про сущестование многомерных массивов?
Риччи и Кернигана убить за них мало. Даже оператор goto не такое зло.

Dasar

а в чем подстава многомерных массивов?

Dasar

typedef __int32 TMyArray[8][16];
typedef TMyArray* PMyArray;
PMyArray parray;

Ventalf

спасибо

Ventalf

а в чем подстава многомерных массивов?
присоединяюсь к вопросу и ещё попрошу подсказать какую нибудь более удобную замену

mira-bella

а в чем подстава многомерных массивов?
в том что они нафик не нужны (не добавляют функциональности)
в том, что читабельность от них только ухудшается (надо долго разбираться как там эти элементы расположены в памяти, и не путать индексы)
И самое главное - они портят логику C-шных массивов, если бы не существование многомерных массивов, можно было бы считать массив просто константным указателем, указывающим на определенное количество последовательных элементов, занимающих статически выделенную память, а так это якобы какой-то отдельный тип, который тем не менее не аналогичен другим типам (например: объект этого типа не передается в функцию по значению как объекты всех остальных типов при аналогичном объявлении функции, и так же мы не можем одному массиву присвоить значение другого оператором =, как для всех остальных типов бред короче.

mira-bella

и ещё попрошу подсказать какую нибудь более удобную замену
очевидно обычный одномерный массив
например двумерный размером M*N: вместо a[j][k] будет a[j*N+k]

bleyman

Или массив массивов

Dasar

> в том, что читабельность от них только ухудшается (надо долго разбираться как там эти элементы расположены в памяти, и не путать индексы)
зачем?
договорись сам с собой - используешь ты индексацию x, y или y, x и всё
> можно было бы считать массив просто константным указателем
что реально полезного дает такое обобщение?

Realist

 
vector<vector<int> > myVector;

Realist

Привер, который показывает, что одномерный массив — не тоже самое, что указатель:
 
sub.cpp:
char str[] = "hello\n";
main.cpp:
#include <iostream>
extern char * str;
int main
{
std::cout<<str<<std::endl;
return 0;
}
kai:~$ g++ -W -Wall -ansi -pedantic main.cpp sub.cpp
kai:~$ ./a.out
Segmentation fault

(C) С. Уэллин

tamusyav

Интересно, почему компилятор не видит противоречия между extern char* и char[]... По идее он должен сказать о неразрешенной (unresolved) ссылке.

ppplva

Компилятор ниразу не видит их одновременно.

tamusyav

Пардон, неправильно выразился. Не компилятор, а линкер.

okunek

блин, это высосаный из пальца пример
ясен пень, что в памяти, отведенной под str, в sub.cpp пишется сама строка, а в main.cpp полагается, что там указатель лежит
зы. array-to-pointer конверсия не просто же так придумана

vertyal17

А чем это противоречит утверждению что указатель это не массив?
А пример интересный, я не знал ...

psihodog

а кто-нть понимает, почему вылетает этот пример?

erotic

Откровенно говоря, было трудно
Но ИМХО, так:
В sub.cpp отводится память под строку, при этом конкретно под указатель памяти не отводится. Везде, где ты пытаешься использовать str как указатель, идет преобразования из массива символов в указатель.
В main.cpp предполагается, что str - это переменная, в которой хранится указатель. На самом деле там хранится буква 'h', поэтому получается неправильный указатель, при выводе которого вылетает segmentation fault.
Я так думаю.

psihodog

похоже, ты прав

Realist

Да, в книжке такое же объяснение.
Только там не 'h', а 'hell' для 32-битной архитектуры

amkharchenko

В сущности, чудный каламбурчик насчет hell.

tamusyav

Что происходит - это понятно. Интересно, что линкер на это не ругается. Вместе с тем, если этот extern поместить в .h-файл, который будет включаться в оба .cpp-шника, то компилятор укажет на ошибку. Почему компилятор такие ошибки видит, а линкер - нет?
Только там не 'h', а 'hell' для 32-битной архитектуры
Точнее, если указатель 32-битный. Конкретное количество байтов и их порядок в общем случае зависит не только от разрядности, но также и других особенностей архитектуры процессора и ОС, да и от компилятора тоже.

psihodog

может потому что линкеру аплевать на типы?
ему главное, чтобы по кол-ву байт всё сошлось...

tamusyav

А, начинаю понимать... Компилятор всегда массивы и указатели называет одинаково (в смысле суффиксов к имени переменной или функции). В случае вызова функции механизм передачи параметров реализует компилятор, и никаких проблем нет, даже если происходит преобразование массива в указатель. А с переменными это не проходит, потому что невозможно привести описание переменной к универсальному виду (например, чтобы по адресу переменной хранился всегда указатель).
Короче, глобальные статические массивы использовать крайне не рекомендуется. Глобальные переменные, правда, лучше ненамного, но все-таки они не позволяют таким образом переводить ошибки компиляции или компоновки в ошибки выполнения.

ppplva

Размер тоже неважен. В этом примере он разный, а линкер молчит. Оно и понятно, откуда в main.o взяться информации о размере str ?

vertyal17

Ну потому что в дерективе extern мы указали тип str - указатель на char, так что о размере должен бы догадываться вроде

ppplva

Да нет же, проблема не в этом.
Просто у тебя переменная в разных единицах компиляции имеет разные типы, только и всего. С функцией в плюсах такое не пройдет, потому что типы аргументов шифруются в имени символа, но в Си - запросто.
Чтобы избежать такой фигни, extern должен быть помещен в заголовочный файл, и включен в оба cpp.

ppplva

Просто для функций размер заранее не известен, а механизм линковки - общий.

lurgi48

Хммммм
Код не претендует на оптимальность и отсутствие ошибок,
особенно часть про двумерные массивы

int _tmain(int argc, _TCHAR* argv[])
{
double *arr1;
double *arr2;
clock_t t = clock;
for (int i=0; i< C_MAX; i++) {
arr1 = new double [M_MAX*M_MAX];
arr2 = new double [M_MAX*M_MAX];
memcpy(arr1, arr2, M_MAX*M_MAX*sizeof(double;
delete arr1;
delete arr2;
}
std::cout << "First Type. Time -- " << doubleclock - t / CLOCKS_PER_SEC << "s" <<std::endl;
t = clock;
double **darr1;
double **darr2;
for (int i=0; i < C_MAX; i++) {
darr1 = new double * [M_MAX];
darr2 = new double * [M_MAX];
for (int j=0; j < M_MAX; j++) {
darr1[j] = new double [M_MAX];
darr2[j] = new double [M_MAX];
memcpy(darr1[j], darr2[j], M_MAX);
}
delete [] darr1;
delete [] darr2;
}
std::cout << "First Type. Time -- " << doubleclock - t / CLOCKS_PER_SEC << "s" << std::endl;
return 0;
}

Результат
#define M_MAX 1024
#define C_MAX 50
First Type. Time — 1.671s
First Type. Time — 47.032s
//------------------------------
#define M_MAX 10
#define C_MAX 999
First Type. Time — 0.015s
First Type. Time — 0.063s
Однако
#define M_MAX 1000
#define C_MAX 10
First Type. Time — 0.312s
First Type. Time — 0.156s

kokoc88

А ты что хотел? Надо правильно выделять память, а не так, как ты делаешь.
PS Удалять тоже надо правильно.

lurgi48

честно говоря я как раз и хотел, чтобы мне показали как правильно

kokoc88

чтобы мне показали как правильно
Как делать правильно зависит от задачи.

lurgi48

да, наверно делать это в цикле 999 раз действительно не очень жизненно

kokoc88

да, наверно делать это в цикле 999 раз действительно не очень жизненно
Да. Особенно если на каждой итерации удалять всё.

erotic

double **darr1; double **darr2; for (int i=0; i < C_MAX; i++) { darr1 = new double * [M_MAX]; darr2 = new double * [M_MAX]; for (int j=0; j < M_MAX; j++) { darr1[j] = new double [M_MAX]; darr2[j] = new double [M_MAX]; memcpy(darr1[j], darr2[j], M_MAX); } delete [] darr1; delete [] darr2; }
А где же delete [] darr1[i], delete [] darr2[i]?
И кстати, удалять arr1 и arr2 тоже желательно оператором delete [], а не просто delete.

Olenenok

Желательно?

erotic

Насколько я знаю, фишка между delete и delete[] проявляется в вызове деструкторов для всех уничтожаемых объектов во втором случае. Т.ч. со встроенными типами разница незаметна. Хотя мб я чего-то не помню уже, давно обсуждалось где-то в разделе.
Оставить комментарий
Имя или ник:
Комментарий: