LINUX.ORG.RU

[C] Странное поведение двойного указателя

 


0

0

Уже второй час не могу понять, что не так в функции.

float **image - двумерный массив (изображение), float *ptr - временный указатель. Если я пишу

		for(j=0; j<YHb; j++){
                        ...
			ptr = &image[j][1];
			...
то все работает замечательно. Но мне нужно пропустить первую строку изображения. И я пишу
		for(j=1; j<YHb; j++){
			...
			ptr = &image[j][1];
                        ...
При этом ptr указывает непонятно куда (все значения пикселей получаются равными 0.0), и вычисление выполняется раз в 20 дольше.

Перепробовал и отключать оптимизацию при компиляции, и -O1 - все равно программа ведет себя так странно. В чем может быть проблема?

☆☆☆☆☆

Ответ на: комментарий от eXire
float **allocate_2d_float(int N,int M,char zero)
{
	int i;
	float **mymat;
	
	mymat=(float **)malloc(N*sizeof(float *));
	check_ptr(mymat,"allocate_2d_float");
	if(!zero)
		for(i=0;i<N;i++) {
			mymat[i]=(float *)malloc(M*sizeof(float));
			check_ptr(mymat[i],"allocate_2d_float");
	}
	else
		for(i=0;i<N;i++) {
			mymat[i]=(float *)calloc(M,sizeof(float));
			check_ptr(mymat[i],"allocate_2d_float");
	}
	return(mymat);
}

потом наподобие такого:

...
	for(i=0;i<Ni;i++) {
		
		// Copy row.
		for(j=0;j<Nj;j++)
			data[j]=image[i][j];
...
Но при этом в моей функции
	inline void findminmax(float **image, char flag){
		int w2 = GRAB_WIDTH/2-1, h2 = GRAB_HEIGHT/2-1, s2 = wImageSize/2;
		int XLb = w2, XHb = s2+w2, YLb = h2, YHb = s2+h2;
		int i, j, px, py;
		float *ptr, pixval;
		if(flag){
			min_ = 1e30; max_ = -1e30;
			for(i=0; i<2; i++) for(j=0; j<2; j++){
				QMax[i][j] = -1e30; QMin[i][j] = 1e30;
				Areas[i][j] = 0.;
			}
		}
		for(j=1; j<YHb; j++){
			if(j < YLb) py = 1;
			else if(j > s2) py = 0;
			else continue;
			ptr = &image[j][1];
			for(i=1; i<XHb; i++){
				if(i < XLb) px = 0;
				else if(i > s2) px = 1;
				else continue;
				pixval = *ptr;
				if(min_ > pixval) min_ = pixval;
				else if(max_ < pixval) max_ = pixval;
				if(QMin[py][px] > pixval) QMin[py][px] = pixval;
				else if(QMax[py][px] < pixval) QMax[py][px] = pixval;
				Areas[py][px] += fabs(pixval);
			}
		}
	}
получается какая-то чушь. inline тоже убирал - не помогает...

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Да, заполняются все три массива (соответствующие цветовым компонентам) вот так:

	for(j = wImageSize - GRAB_HEIGHT; j < wImageSize; j++){
		ptr[0] = imageR[j]; ptr[1] = imageG[j]; ptr[2] = imageB[j];
		for(i = 0; i < GRAB_WIDTH; i++){
			for(k = 0; k < 3; k++)
				*(ptr[k]++) = (float)(*bytes++);
		}
	}

Eddy_Em ☆☆☆☆☆
() автор топика

Поскольку глазами не видно ровным счетом нифига, а по симптомам что-то вроде выхода за границы массива, ставишь валгринд, компилируешь свою программу с -O0 -fno-inline -g и valgrind --tool=memcheck --leak-check=full -v ./myprogram

linuxfan
()
Ответ на: комментарий от Eddy_Em
   float **mymat; 
    
   mymat=(float **)malloc(N*sizeof(float *)); 
   check_ptr(mymat,"allocate_2d_float"); 
   if(!zero) 
      for(i=0;i<N;i++) { 
         mymat[i]=(float *)malloc(M*sizeof(float)); 
         check_ptr(mymat[i],"allocate_2d_float"); 
   } 

И что ты после этого ожидаешь?

mv ★★★★★
()
Ответ на: комментарий от linuxfan

И как этим пользоваться? Получил огромное количество вывода вроде

==17190== 209920 errors in context 441 of 447:
==17190== Use of uninitialised value of size 4
==17190==    at 0x40CEA00: ??? (in /usr/lib/libjpeg.so.7.0.0)
==17190==    by 0x40D690C: ??? (in /usr/lib/libjpeg.so.7.0.0)
==17190==    by 0x40D1F7A: ??? (in /usr/lib/libjpeg.so.7.0.0)
==17190==    by 0x40CB7A2: jpeg_read_scanlines (in /usr/lib/libjpeg.so.7.0.0)
==17190==    by 0x8049485: process_grabbing (wavinfo.c:21)
==17190==    by 0x40A7884: start_thread (in /lib/i686/libpthread-2.10.1.so)
==17190==    by 0x41C957D: clone (in /lib/i686/libc-2.10.1.so)
==17190== 
--17190-- 
--17190-- used_suppression:     59 dl-hack3-cond-1
==17190== 
==17190== ERROR SUMMARY: 3625393 errors from 447 contexts (suppressed: 59 from 6)

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Сохраняй stderr в файл и грепай по нему «bound». Если есть выход за пределы выделенных блоков памяти, будут соответствующие сообщения.

А вообще лучше запускать его на минимальном наборе данных, чтобы было поменьше сообщений об ошибках.

linuxfan
()
Ответ на: комментарий от linuxfan

Сохраняй stderr в файл и грепай по нему «bound».

bound ни разу не попадается.

yoghurt

mymat=(float **)malloc(N*sizeof(float *));

Вот оно

Что - оно?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Ты двойными указателями только усложнил себе жизнь. Что мешало сделать обычный плоский массив?

В valgrind'е смотри все ошибки, относящиеся к твоим исходникам. Вот тут, например, у тебя что? by 0x8049485: process_grabbing (wavinfo.c:21)

Reset ★★★★★
()
Ответ на: комментарий от Reset

Ты двойными указателями только усложнил себе жизнь. Что мешало сделать обычный плоский массив?

Скорость сильно упадет, если использовать плоский массив (мне нужно хотя бы со скоростью 30 кадров в секунду «на лету» вейвлет-преобразование делать).

Вот тут, например, у тебя что? by 0x8049485: process_grabbing (wavinfo.c:21)

Это к делу не относится (декомпрессия очередного кадра с веб-камеры).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Скорость сильно упадет, если использовать плоский массив (мне нужно хотя бы со скоростью 30 кадров в секунду «на лету» вейвлет-преобразование делать).

Это почему это ? Наоборот должна возрасти, так как не будет двойной адресации.

Это к делу не относится (декомпрессия очередного кадра с веб-камеры).

смотри тогда то, что относится

Reset ★★★★★
()
Ответ на: комментарий от Eddy_Em

Скорость сильно упадет, если использовать плоский массив

С чего бы вдруг? Или ты реально думаешь, что image[y][x] будет быстрее, чем image[y * line_pitch + x]? Спешу тебя огорчить, внутри оно будет по второму варианту, даже если написано, как в первом :)

mv ★★★★★
()
Ответ на: комментарий от Eddy_Em

Ты делаешь обычный плоский массив длиной MxN. К нему нельзя обращаться по [j], ибо у тебя он на самом деле одномерный, и приведение типа к float** тут не прокатит.

Либо делаешь

float **ptr = malloc (rows * sizeof (element_type *));
for (int i = 0; i < rows; i++)
   ptr[i] = malloc (cols * sizeof (element_type));

Либо так и работаешь с одним большим куском памяти, но с хитрым доступом, где a[j] равносильно your_ptr[cols * i + j]

yoghurt ★★★★★
()
Ответ на: комментарий от mv

Как по второму будет только если image объявлен как статический массив float image[N][N]. В варианте автора будет всё хуже. Мы сначала полезем по адресу image[y], а потом полезем image[y][x]. Если обходим это дело последовательно, то компилятор сможет сделать оптимизацию, а вот если бы обходим массив в случайном порядке, то с двойной адресацией будет жопа.

Reset ★★★★★
()
Ответ на: комментарий от ananas

а накой в findminmax() вообще нужны ptr и pixval?

Для уменьшения количества обращения по косвенной адресации.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от yoghurt

Ё-ный стыд, недосмотрел, оригинально так всё и делается. Беру свои замечания назад :)

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

Либо делаешь

У меня так и инициализируются массивы:

float **mymat = (float **)malloc(N*sizeof(float *));
...
for(i=0;i<N;i++)
   mymat[i]=(float *)calloc(M,sizeof(float));

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Для уменьшения количества обращения по косвенной адресации.

во-во. ты погряз в двойных указателях, наставил непонятных оптимизаций ... отсюда и результат. Если бы сделал обычным плоским массивом, то и код был бы проще и компилятор сам бы сделал за тебя всё что требуется.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Как по второму будет только если image объявлен как статический массив float image[N][N].

Я это и имел в виду. То, что у ТС будет, видно по коду.

mv ★★★★★
()
Ответ на: комментарий от Eddy_Em

> Скорость сильно упадет, если использовать плоский массив

Прежде чем делать такие заявления надо хотя бы профайлером померить.

anonymous
()
Ответ на: комментарий от Boy_from_Jungle

Покажи весь код.

Много там - 8 сишных файлов... Вот файл с основной проблемой.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Для уменьшения количества обращения по косвенной адресации.

вот только где у тебя ptr в цикле по i меняется, я так и не понял

ananas ★★★★★
()
Ответ на: комментарий от ananas

вот только где у тебя ptr в цикле по i меняется

sorry, это я ptr++ удалил, чтобы проверить, не из-за этого ли все проблемы. Вернул обратно (строчка 83).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

>мне нужно хотя бы со скоростью 30 кадров в секунду «на лету» вейвлет-преобразование делать

/me тихо пустил слезу, вспоминая, как когда-то такой скорости безнадёжно хотели на линейных преобразованиях в 320x200 :)

KRoN73 ★★★★★
()
Ответ на: комментарий от legolegs

Без разницы: и ptr = &(image[j][1]), и ptr = &image[j][1], и даже без пропуска первого столбца ptr = image[j] - не работает.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

>Скорость сильно упадет, если использовать плоский массив (мне нужно хотя бы со скоростью 30 кадров в секунду «на лету» вейвлет-преобразование делать).

Производительность наоборот немного увеличится. Многомерный массив — это абстракция. На деле его всегда лучше представлять одномерным.

linuxfan
()
Ответ на: комментарий от KRoN73

/me тихо пустил слезу, вспоминая, как когда-то такой скорости безнадёжно хотели на линейных преобразованиях в 320x200 :)

:) На скорости ~100 кадров в секунду безо всяких CUDA спокойно считается центр тяжести фрейма 32х32 точки с вейвлет-фильтрацией. Для фрейма 256х256 - ~20 кадров в секунду... А здесь у меня - проблема быстрой автофокусировки. Хочу научиться исправлять расфокусировку изображения.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от linuxfan

Производительность наоборот немного увеличится.

Во-первых, в K&R про это хорошо расписано. Во-вторых, я уже сравнивал скорости операций

ptr[j] ==> *(*(ptr + j) + i) (для **ptr)

(всего лишь две операции суммирования и две адресации) и

ptr[j*w+i] ==> *(ptr + j*w + i) (для *ptr)

(операция умножения, две операции суммирования и адресация).

Второй способ потормознее будет.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Boy_from_Jungle

Может нужно *ptr++?!

ptr - указатель на float, поэтому, чтобы перейти к следующему элементу, надо сделать просто ptr++. А *ptr++ добавляет совершенно ненужную операцию разыменования указателя.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Второй способ потормознее будет.

Не будет. Адресация существенно медленнее арифметических действий.

Reset ★★★★★
()

рецепт в общем-то озвучили - переходи на одномерный массив, увеличишь этим скорость и уменьшишь размер потребляемой памяти, а n-мерными никогда не пользуйся :)

dt1 ★★
()
Ответ на: комментарий от Reset

Присоединяюсь к этому решению.

Плюс, для таких массивов рационально будет использовать библиотеку GSL, где многомерные массивы реализированы таким образом. Цитирую стандартный пример с матрицей:

#include <stdio.h>
#include <gsl/gsl_matrix.h>
int main (void)
{
  int i, j;
  gsl_matrix * m = gsl_matrix_alloc (10, 3);
  
  for (i = 0; i < 10; i++)
    for (j = 0; j < 3; j++)
      gsl_matrix_set (m, i, j, 0.23 + 100*i + j);
  
  for (i = 0; i < 100; i++) /* OUT OF RANGE ERROR */
  
  for (j = 0; j < 3; j++)
  printf ("m(%d,%d) = %g\n", i, j,  gsl_matrix_get (m, i, j));

  gsl_matrix_free (m);

return 0;
}
blinkenlichten
()
Ответ на: комментарий от Reset

Ладно, пойду пообедаю, выкурю сигаретку и хряпну 100 грамм. Может, мозги лучше заработают. Уж очень не хочется все операции с вейвлетами переписывать к одномерным массивам... (я и день потратил на «выпиливание» нужных мне операций).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от dt1

Но как получается, что при начале цикла с нуля, адресация работает нормально. Как только я начинаю цикл с другого числа, возникают жуткие тормоза, и данные берутся вообще непонятно откуда?

Eddy_Em ☆☆☆☆☆
() автор топика

А если в варианте, где всё работает замечательно, проверить чему равен YHb. А то есть ощущение, что оно 1, таким образом ты пропускаешь кусок ptr=... и что у тебя там лежит - икс три.

Delirium_veritas
()
Ответ на: комментарий от Eddy_Em

>(операция умножения, две операции суммирования и адресация).

Ох уж мне эти теоретики. Ты проходишь массив последовательно, поэтому:

ptr = image;
for (row = 0; row < ROWS; row++) {
  for (col = 0; col < COLS; col++) {
    ptr[col] = ...; // set image[row][col]
  }
  ptr += COLS;
}
linuxfan
()
Ответ на: комментарий от Delirium_veritas

Это при условии, что проблемный кусок кода правильно обнаружен. А то был у меня случай, когда 2 месяца бился с рандомно вылазиющими ***Glibc detected double free or memory corruption... в многопоточной проге, а потом выснилось, что в соседнем потоке out of bound был

Delirium_veritas
()
Ответ на: комментарий от Eddy_Em

Да хз, судя по тому остатку вывода валгринда, который ты привел, у тебя многопоточная прога и видомо где-то память портится. Во-первых, попробуй valgrind с ключом --undef-value-errors=no выполнить, он тогда на неинициализированные значения ругаться не будет, а еще --tool=helgrind попробуй

dt1 ★★
()
Ответ на: комментарий от Delirium_veritas

А если в варианте, где всё работает замечательно, проверить чему равен YHb.

это я первым делом проверил - все вычисляется правильно.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от dt1

Не помогло. valgrind вообще ничего дельного по этому поводу не говорит. Основная ругань - на libjpeg и dri, а по поводу моих функций - тишина...

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

>Но как получается, что при начале цикла с нуля, адресация работает нормально.

Я на всякий случай напоминаю, что на этот случай есть gdb.

linuxfan
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.