двойной указатель и как изменить указатель в функции - PullRequest
0 голосов
/ 22 марта 2012

в этом примере из opencv, почему мы не создали простую переменную img (вместо указателя), а затем использовали простой указатель в параметре функции cvReleaseImage (тогда как здесь мы будем использовать двойной указательв параметре, потому что мы передаем адрес указателя: &img):

IplImage* img = NULL; 
img = cvLoadImage(argv[1], CV_LOAD_IMAGE_UNCHANGED);
cvReleaseImage (&img);

?

Кроме того, когда мы используем простой указатель в функции, мы можем изменитьуказанное значение, добавив к указателю еще один *:

, например:

function random(int* pointer, int* pointer2){
    *pointer = *pointer2 / 2;
}

Итак, с двойным указателем мы должны сделать это?:

function random(int** doublepointer, int* pointer2){
    **doublepointer = *pointer2 / 2;
}

Спасибо за помощь

Ответы [ 5 ]

2 голосов
/ 22 марта 2012

Простой: cvLoadImage(), как следует из названия, загружает все изображение в память (рассмотрим файл размером 10 МБ) и возвращает IplImage*, в котором указано, где находятся данные изображения в памяти.

Это невероятно полезно, потому что если бы оно вернуло IplImage, нашему приложению нужно было бы выделить еще 10 МБ памяти, дублируя данные в памяти программы!Таким образом, возвращение указателя с адресом памяти данных на самом деле является очень разумным проектным решением.

cvReleaseImage() получает двойной указатель, чтобы отразить этот дизайн.Если вы загрузите исходный код OpenCV v2.3, вы увидите его реализацию в modules/core/src/array.cpp:

2979 CV_IMPL void
2980 cvReleaseImage( IplImage ** image )
2981 {
2982     if( !image )
2983         CV_Error( CV_StsNullPtr, "" );
2984 
2985     if( *image )
2986     {
2987         IplImage* img = *image;
2988         *image = 0;
2989 
2990         cvReleaseData( img );
2991         cvReleaseImageHeader( &img );
2992     }
2993 }

Понятно, что фактическое освобождение ресурсов памяти выполняется двумя другими вспомогательными функциями, которые в какой-то моментпозвоните cvFree(), чтобы освободить память.

Упрощение cvReleaseImage(), которое разделяет @Nikolai, все же верно.

РЕДАКТИРОВАТЬ: для ответа на ваш комментарий.

Назначениеуказатель на 0 никогда не освободит память , зарезервированную с помощью malloc() / new, он просто указывает указатель куда-то еще, и в этом случае, никуда!Давайте разберемся, что означает cvReleaseImage (&img).Все начинается с:

IplImage* img = NULL; 

Объявление указателя делает то же самое, что и объявление обычной переменной: оно выделяет определенный объем памяти для хранения некоторых данных.Объявление указателя (в 32-битной арке), подобное приведенному выше, выделяет 4 байта памяти для хранения адреса некоторой другой переменной.Другими словами, сам указатель потребляет 4 байта памяти в функции, которую он объявил.

Вызов cvReleaseImage(&img) передает адрес указателя , а не адрес данных, на которые он указывает (момент A-HA прямо здесь).

Теперь давайтепроанализируйте оставшуюся часть кода:

2985     if( *image ) // does the original pointer points somewhere?
2986     {
             // img copies the address of the data pointed by it's cousing image 
2987         IplImage* img = *image; 
             // and handles the deallocation procedure from now own using img.

             // By clearing the original pointer,
2988         *image = 0; 
             // OpenCV allows us to test for it's release 
             // after cvReleaseImage() executes. 

2989 
2990         cvReleaseData( img );

2991         cvReleaseImageHeader( &img );
2992     }

Таким образом, выполнение *image = 0; - это просто стандартная процедура, поэтому позже мы можем проверить на удачное освобождение, как это:

cvReleaseImage (&img);
if (img != NULL)
{
  // OOPS! Something went wrong, memory was not released!
} 
1 голос
/ 22 марта 2012

В функциях «release» или «free» довольно типично передавать указатель на указатель, чтобы фактический указатель (в данном случае img), который видит пользователь, мог быть установлен в NULL и, следовательно, не случайно использовался после удаления.

И да, вы просто дважды разыменовываете, чтобы изменить данные в указателе на указатель, но я бы порекомендовал использовать скобки:

(*(*doublepointer)) = (*pointer)/2;

Мне кажется, это выглядит лучше.

1 голос
/ 22 марта 2012

Что касается первого вопроса: размер IplImage, вероятно, неизвестен до его загрузки, тогда как размер «простой переменной» должен быть известен во время компиляции.

1 голос
/ 22 марта 2012

Я считаю, что идея в том, что cvReleaseImage() может сделать что-то вроде этого:

void cvReleaseImage( IplImage** ppimg ) {
    /* ... */
    free( *ppimg );
    *ppimg = NULL;
}

т.е. освободить память и очистить исходный указатель.

0 голосов
/ 22 марта 2012

Функция cvLoadImage возвращает указатель на IplImage, поэтому вы не можете использовать локально определенный IplImage:

IplImage* cvLoadImage( const char* filename, int iscolor=CV_LOAD_IMAGE_COLOR );

Для cvReleaseImage требуется адрес указателя на структуру IplImage. Делая это, они могут установить указатель на значение NULL при удалении структуры, чтобы удаленный объект не мог быть использован непреднамеренно.

...