Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Проверка указателей на валидность


Автор: Sergio 5.11.2007, 16:31
Здраствуйте. Как проверить указатель на валидность? Проверять на NULL или еще есть способы?
Заранее спасибо.

Автор: MAKCim 5.11.2007, 16:47
Sergio
все зависит от того, какие в твоем случае критерии валидного указателя
в общем случае проверить валидность средствами языка нельзя
должна быть поддержка со стороны API ОС
однако если указатель нулевой, то с большой вероятностью он не валиден

Автор: Sergio 5.11.2007, 17:37
А переменные типа int лучше инициализировать 0 или NULL?

Автор: Alexeis 5.11.2007, 18:19
Цитата(MAKCim @  5.11.2007,  16:47 Найти цитируемый пост)
однако если указатель нулевой, то с большой вероятностью он не валиден 

  Нечетные указатели обычно неверные, они могут быть верными только если это указатель на элемент массива или указали нестандартное выравнивание.

Добавлено через 6 минут и 46 секунд
  Обычно это связанно с 32х разрядными регистрами процессора и выполняется с целью оптимизации. В месте с тем если это какой-нибудь контроллер, то это может быть неверным. А вот скажем на процессорах типа ARM, вообще не удасться адресовать нечетный адрес, возможно из-за того, что них нельзя обратиться к половинке/четвертинке регистра типа как eax/ax/al/ah

Автор: MAKCim 5.11.2007, 18:48
Alexeis
при чем здесь 32-ух разрядные регистры?
они вполне могут хранить адрес, не кратный степени двойки
Цитата(Alexeis @  5.11.2007,  18:19 Найти цитируемый пост)
не удасться адресовать нечетный адрес, возможно из-за того, что них нельзя обратиться к половинке/четвертинке регистра типа как eax/ax/al/ah 

как одно связано с другим?
нулевой разряд регистра не может быть единичным?

Автор: Alek86 5.11.2007, 19:11
_CrtIsValidPointer посмотри

Автор: Alexeis 5.11.2007, 20:51
MAKCim, представь что предыдущая структура имела нечетную длину, например заканчивается байтом. За ней начинается новая структура. Если прочитать этот байт как DWORD, то автоматом захватим еще кусок соседней структуры. Если мы можем использовать четвертинку регистра, то это нам позволит прочитать только 1 байт и получить верное значение в регистре, а если нет, то единственная возможность прочитать правильное значение, это производить несколько битовых операций. Компилятор сразу отбрасывает использование битовых операций, как неэффективное средство доступа к памяти, потому все структуры и объекты выравниваются на границу 4х байт. Если объявляется сначала переменная в байт, а затем скажем DWORD, то вместо байта компилятор выделит тоже DWORD, т.о. почти всегда адреса получается четными. 
  Для процессоров i486 совместимых обычно делается тоже самое, но в целях оптимизации по скорости.

MAKCim, можешь глянуть под дебагом адреса и проверить.

Автор: archimed7592 5.11.2007, 20:51
Цитата(Sergio @  5.11.2007,  16:31 Найти цитируемый пост)
Как проверить указатель на валидность?

В общем случае - никак. Давай конкретные примеры.
Учти, что, если появилась необходимость проверять не нулевой указатель на валидность, то это, с огромной вероятностью, ошибка проектирования.

Автор: Alexeis 5.11.2007, 20:53
Цитата(Alek86 @  5.11.2007,  19:11 Найти цитируемый пост)
_CrtIsValidPointer посмотри 

Цитата(http://msdn2.microsoft.com/en-us/library/0w1ekd5e(VS.80).aspx)

Verifies that a specified memory range is valid for reading and writing (debug version only).

Автор: Alek86 5.11.2007, 21:17
Цитата(Alexeis @  5.11.2007,  20:53 Найти цитируемый пост)
(debug version only)

что вспомнил, то и ляпнул smile
тогда только умные ресурсы, которые при удалении обнуляют указатели.

или всеми любимый boost::shared_ptr (хотя все еще не знаю, насколько он тормозит программу smile )

Автор: MAKCim 5.11.2007, 21:36
Цитата(Alexeis @  5.11.2007,  20:51 Найти цитируемый пост)
то это нам позволит прочитать только 1 байт и получить верное значение в регистре, а если нет, то единственная возможность прочитать правильное значение, это производить несколько битовых операций

Код

movzx eax, byte ptr [eax]

Цитата(Alexeis @  5.11.2007,  20:51 Найти цитируемый пост)
Компилятор сразу отбрасывает использование битовых операций, как неэффективное средство доступа к памяти, потому все структуры и объекты выравниваются на границу 4х байт

битовые операции и доступ к памяти никак не связаны
Код

mov eax, dword ptr [eax]
and eax, 0xFF ; нет memory-операнда

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

Автор: Lazin 5.11.2007, 22:21
Есть еще такие функции IsBadWritePtr IsBadReadPtr, _CrtIsValidPointer видимо на их основе построена

Автор: Alexeis 5.11.2007, 23:54
MAKCim, а как тогда объяснить причину ошибки "Data Type Misalignment", которая возникает при попытке обратиться по нечетному адресу и при включении директивы  pragma pack(0) Значит так оптимальнее или еще по чему. Мож гляуть в хелп.

Добавлено через 6 минут и 3 секунды
Цитата(MAKCim @  5.11.2007,  21:36 Найти цитируемый пост)
битовые операции и доступ к памяти никак не связаны

  Я имел ввиду, что для того чтобы считать значение нужно производить битовую операцию. Это дольше чем просто обратиться! 

Цитата(MAKCim @  5.11.2007,  21:36 Найти цитируемый пост)
movzx eax, byte ptr [eax]

С массивами я же говорил все ок. Видимо так он и делает. Для элементов массивов можно использовать нечетные.

Автор: DominiK 6.11.2007, 00:24
Цитата(Sergio @ 5.11.2007,  17:37)
А переменные типа int лучше инициализировать 0 или NULL?

Разницы нету.

Автор: archimed7592 6.11.2007, 08:41
Цитата(DominiK @  6.11.2007,  00:24 Найти цитируемый пост)
Разницы нету. 

Зависит от того Си это или Си++.

Автор: MAKCim 6.11.2007, 10:10
Цитата(Alexeis @  5.11.2007,  23:54 Найти цитируемый пост)
а как тогда объяснить причину ошибки "Data Type Misalignment", которая возникает при попытке обратиться по нечетному адресу и при включении директивы  pragma pack(0) Значит так оптимальнее или еще по чему. Мож гляуть в хелп.

установка AM в CR0 и AC в EFLAGS включает генерацию #AC (Alignment Check) в случае обращения по невыровненным адресам
но это никак не связано с оптимизацией
обработка исключительной ситуации на уровне процессора куда менее эффективное средство для достижения оптимального результата
 
Цитата(Alexeis @  5.11.2007,  23:54 Найти цитируемый пост)
С массивами я же говорил все ок. Видимо так он и делает. Для элементов массивов можно использовать нечетные.

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

Автор: Alexeis 6.11.2007, 10:34
Цитата(MAKCim @  6.11.2007,  10:10 Найти цитируемый пост)
установка AM в CR0 и AC в EFLAGS включает генерацию #AC (Alignment Check) в случае обращения по невыровненным адресам
но это никак не связано с оптимизацией
обработка исключительной ситуации на уровне процессора куда менее эффективное средство для достижения оптимального результата

  Почему? Разве эффективнее делать 10 проверок чем одну обработку исключения?

Автор: MAKCim 6.11.2007, 10:57
Цитата(Alexeis @  6.11.2007,  10:34 Найти цитируемый пост)
Почему? Разве эффективнее делать 10 проверок чем одну обработку исключения? 

хорошо
а как ты отличишь обращение к элементу массива от, например, обращения к полю структуры?
каждый раз компилятор вынужден будет встявлять куски кода по установке и сбросу AC
это более эффективно?

Автор: Alexeis 6.11.2007, 11:21
MAKCim, ну мой текущий компилятор это как-то делает, хотя у меня создается впечатление, что для массивов он использует другую схему обращения при этом избегает нечетных адресов. 
Цитата(MAKCim @  6.11.2007,  10:57 Найти цитируемый пост)
хорошо
а как ты отличишь обращение к элементу массива от, например, обращения к полю структуры?

  Думаю он использует для этого разные команды.

  По крайней мере с вероятностью 50/50 можно определить, то что указатель невалидный. Чем раньше об этом узнаешь тем ближе окажешься к источнику ошибки. Так что положительный момент в этом есть. Вот только я не уверен, можно ли организовать такую же схему для i486.

Автор: archimed7592 6.11.2007, 11:37
Цитата(Alexeis @  6.11.2007,  11:21 Найти цитируемый пост)
MAKCim, ну мой текущий компилятор это как-то делает

Дельфи? IIRC, он программно полностью контролирует доступ к памяти(в дебаге).

Автор: Alexeis 6.11.2007, 11:45
archimed7592, я сейчас говорю про eVC.

Добавлено @ 11:46
Мы же в разделе С++. С чего бы тут обсуждать компилятор Делфи...
[оффтоп]У делфей такие проблемы возникают намного реже, так как там строки/массивы/интерфейсы это управляемая память. Зря напомнили  smile , ща как разведу оффтопа[/оффтоп]

Автор: MAKCim 6.11.2007, 11:51
Цитата(Alexeis @  6.11.2007,  11:21 Найти цитируемый пост)
Думаю он использует для этого разные команды.

 smile 
не в команде дело
если у нее есть адресный операнд, то в любом случае существует возможность исключения
если чтение/запись массива выполняется по выровненным адресам, то тогда требуются дополнительные телодвижения для того, чтобы прочитать/записать данные
а ты вроде сам писал
Цитата(Alexeis @  5.11.2007,  23:54 Найти цитируемый пост)
Я имел ввиду, что для того чтобы считать значение нужно производить битовую операцию. Это дольше чем просто обратиться! 

где оптимизация?  smile 

Автор: Alexeis 6.11.2007, 11:57
MAKCim ну с асмом мне работать не приходиться, тем более с его различными спецификами, потому даж не знаю как. Читал, что выравнивание делается в целях оптимизации. А как тут без понятия. Если хош могу показать кусок асма. Ща попробую. 

Автор: MAKCim 6.11.2007, 12:01
Цитата(Alexeis @  6.11.2007,  11:57 Найти цитируемый пост)
Если хош могу показать кусок асма. Ща попробую

давай

Автор: archimed7592 6.11.2007, 12:26
Цитата(Alexeis @  6.11.2007,  11:57 Найти цитируемый пост)
Читал, что выравнивание делается в целях оптимизации.

Не только, особенно, если речь про eVC - многие архитектуры не поддерживают обращение по невыровненому адресу.

Автор: Alexeis 6.11.2007, 12:29
Код

596:      byte *b = new byte[50];

1205630A   mov       r0, #50
1205630C   bl        |CreateStaticMapping+18h (1208a930)|
12056310   str       r0, [sp, #0x48]
12056312   ldr       r1, [sp, #0x48]
12056314   str       r1, [sp, #0x20]
12056316   ldr       r0, [sp, #0x20]
12056318   str       r0, [sp, #0x18]

597:      b[1] = 4;

1205631A   ldr       r0, [sp, #0x18]
1205631C   mov       r1, #4
1205631E   strb      r1, [r0, #1]

598:      b[2] = 4;

12056320   ldr       r2, [sp, #0x18]
12056322   mov       r0, #4
12056324   strb      r0, [r2, #2]


Автор: MAKCim 6.11.2007, 12:48
Alexeis
Цитата(Alexeis @  6.11.2007,  12:29 Найти цитируемый пост)
1205630A   mov       r0, #50
1205630C   bl        |CreateStaticMapping+18h (1208a930)|
12056310   str       r0, [sp, #0x48]

неизвестно, какое значение в r0 после bl
поэтому неизвестно, какой адрес получается в этом месте (r0 + 1)
Цитата(Alexeis @  6.11.2007,  12:29 Найти цитируемый пост)
1205631E   strb      r1, [r0, #1]

и в этом
Цитата(Alexeis @  6.11.2007,  12:29 Найти цитируемый пост)
12056324   strb      r0, [r2, #2]

в не зависимости от базового адреса массива x
из двух адресов x + 1 и x + 2 по определению как минимум один из них - не кратен 4-м
Добавлено @ 12:49
Alexeis
ассемблер похож на MIPS
оно?

Автор: pompei 6.11.2007, 13:56
Как видишь проверка указателя на валидность очень сложная задача. А раз так то постарайтся поступить по простому, постарайся избежать её. Например с помощью такого метода: прими что = 0 (NULL) - это инвалидный указатель, остальные валидные. Тогда получается необходимо всегда писать так:
Код


// Берём ресурсы
MY_STRU *my_pointer = malloc( размер );
if (my_pointer == NULL) { // по спецификации malloc возвращает NULL в случае ошибки
  perror( "А-а-а-а-а-а" );
  exit( 1 );
}

//освобождаем ресурс
if (my_pointer != NULL) {
  free( my_pointer );
  my_pointer = NULL; // чтобы указать что он инвалидный
}

// работаем с указателем
if (my_pointer == NULL) {
  //указатель инвалидный
} else {
  // пользуемся валидным указателем
  my_pointer->...
}



Автор: Lazin 6.11.2007, 14:23
Цитата(pompei @  6.11.2007,  13:56 Найти цитируемый пост)
прими что = 0 (NULL) - это инвалидный указатель, остальные валидные

это неверно, если указатель не равен 0, это не значит что он валидный, указатель может указывать неизвестно куда, например в адресное пространство другого процесса. Я уже писал, что есть функции для проверки, указывает ли указатель на область памяти в куче или хрен знает куда (IsBadWritePtr IsBadReadPtr, итд, смотри MSDN). Но даже если он указывает на объект в куче, это еще не значит что он правильный, так как может указывать на уже удаленный объект.
Обычно указатели инициализируют 0, и после удаления объекта обнуляют указатель, чтобы по значению указателя можно было узнать указывает ли он на объект или нет.

Автор: MAKCim 6.11.2007, 14:58
есть решение под Linux (но немного извращенное) 
Код

int is_valid_pointer(void * address) {
    size_t size = (size_t)getpagesize();
    unsigned long pointer = (unsigned long)address;
    void * handle;
    if ((handle = mmap((void*)(pointer - ponter % size), size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED)
        return 0;
    munmap(handle, size);
    return 1;
}

Автор: Alexeis 6.11.2007, 17:04
Цитата(MAKCim @  6.11.2007,  12:48 Найти цитируемый пост)
в не зависимости от базового адреса массива x
из двух адресов x + 1 и x + 2 по определению как минимум один из них - не кратен 4-м

  Я тоже так подумал. Да и дебагере можно прочитать с нечетного адреса прибавив к указателю на байт единицу и разъыменовав. При это все ок. Выходит, что проверка включается при обращении к структурам/массивам. 
p.s. я пропадаю на пару дней. Приеду гляну как происходит обращение к структуре. 

Цитата(MAKCim @  6.11.2007,  12:48 Найти цитируемый пост)
Alexeis, 
ассемблер похож на MIPS
оно?

Я не знаю что такое MIPS.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)