Обозначение скобок позволяет вам получить доступ к свойствам по имени, хранящимся в переменной:
var obj = { "abc" : "hello" };
var x = "abc";
var y = obj[x];
console.log(y); //output - hello
obj.x
не будет работать в этом случае.
В общем, это невозможно сделать. Вот один особенно неприятный случай:
struct Point2d {
int x;
int y;
};
struct Point3d {
int x;
int y;
int z;
};
void dump(Point3 *p)
{
printf("[%d %d %d]\n", p->x, p->y, p->z);
}
Point2d points[2] = { {0, 1}, {2, 3} };
Point3d *p3 = reinterpret_cast<Point3d *>(&points[0]);
dump(p3);
На многих платформах это будет распечатываться:
[0 1 2]
Вы вынуждаете систему времени выполнения неправильно интерпретировать бит памяти, но в этом случае он не будет разбиваться, потому что бит все имеет смысл. Это часть дизайна языка (посмотрите на полиморфизм C-стиля с struct inaddr
, inaddr_in
, inaddr_in6
), поэтому вы не можете надежно защитить его на любой платформе.
В Win32 / 64 есть способ сделать это. Попытайтесь прочитать указатель и поймать полученный SEH exeception, который будет брошен на неудачу. Если он не бросает, то это действительный указатель.
Проблема с этим методом заключается в том, что он просто возвращает, можете ли вы читать данные из указателя. Это не гарантирует гарантии безопасности типа или любого другого числа инвариантов. В общем, этот метод хорош для другого, кроме как сказать «да, я могу прочитать это конкретное место в памяти за время, прошедшее сейчас».
Короче говоря, не делайте этого;)
Raymond Chen имеет сообщение в блоге на эту тему: http://blogs.msdn.com/oldnewthing/archive /2007/06/25/3507294.aspx
Не очень хорошая политика принимать произвольные указатели в качестве входных параметров в публичном API. Лучше иметь типы «простых данных», например, целое число, строку или структуру (я имею в виду классическую структуру с простыми данными внутри, конечно, официально что-либо может быть структурой).
Почему? Хорошо, потому что, как говорят другие, нет стандартного способа узнать, был ли вам задан действительный указатель или указатель на нежелательный.
Но иногда у вас нет выбора - ваш API должен принять указатель.
В этих случаях обязанность вызывающего абонента передать хороший указатель. NULL может приниматься как значение, но не указатель на хлам.
Можете ли вы дважды проверить? Хорошо, что я сделал в таком случае, чтобы определить инвариант для типа, на который указывает указатель, и вызвать его, когда вы его получите (в режиме отладки). По крайней мере, если инвариант выходит из строя (или падает), вы знаете, что вам присвоено плохое значение.
// API that does not allow NULL
void PublicApiFunction1(Person* in_person)
{
assert(in_person != NULL);
assert(in_person->Invariant());
// Actual code...
}
// API that allows NULL
void PublicApiFunction2(Person* in_person)
{
assert(in_person == NULL || in_person->Invariant());
// Actual code (must keep in mind that in_person may be NULL)
}
Вы знаете, новый драйвер (по крайней мере, на Linux), который способен на это, вероятно, не будет так сложно писать.
С другой стороны, было бы глупо строить ваши программы как это. Если у вас нет особого и единственного использования для такой вещи, я бы не рекомендовал ее. Если вы построили большое приложение, загруженное с проверками действительности с постоянными указателями, оно, вероятно, будет ужасно медленным.
Во-первых, я не вижу смысла пытаться защитить себя от вызывающего, намеренно пытаясь вызвать крушение. Они могут легко сделать это, пытаясь получить доступ через недействительный указатель. Есть много других способов - они могут просто перезаписать вашу память или стек. Если вам нужно защититься от такого рода вещей, вам нужно работать в отдельном процессе с использованием сокетов или другого IPC для связи.
Мы пишем довольно много программного обеспечения, которое позволяет партнерам / клиентам / пользователям для расширения функциональности. Неизбежно любая ошибка сообщается нам в первую очередь, поэтому полезно иметь возможность легко показать, что проблема заключается в подключаемом коде. Кроме того, есть проблемы с безопасностью, и некоторые пользователи более доверяют другим.
Мы используем несколько разных методов в зависимости от требований производительности и пропускной способности и надежности. Из наиболее предпочтительных:
Мы стараемся никогда не прибегать к тому, что вы пытаетесь сделать, когда имеете дело с сторонним программным обеспечением - особенно когда нам предоставляются плагины / библиотеки как двоичные, а не исходные.
Использование пула памяти довольно просто в большинстве случаев и не должно быть неэффективным. Если вы выбрали данные в первую очередь, тогда тривиально проверить указатели на значения, которые вы выделили. Вы также можете сохранить выделенную длину и добавить «магические» значения до и после данных, чтобы проверить допустимый тип данных и переполнение данных.
Добавление к принятому ответу:
Предположим, что ваш указатель может содержать только три значения - 0, 1 и -1, где 1 означает действительный указатель, -1 недопустимый и 0 другой недействительный. Какова вероятность того, что ваш указатель будет NULL, все значения одинаково вероятны? 1/3. Теперь возьмите действительный аргумент, поэтому для каждого недопустимого случая у вас есть соотношение 50:50, чтобы поймать все ошибки. Выглядит хорошо? Масштабируйте это для 4-байтового указателя. Возможны 2 ^ 32 или 4294967294 возможных значения. Из них только одно значение верно, одно значение NULL, и вы по-прежнему остаетесь с 4294967292 другими недопустимыми случаями. Пересчитайте: у вас есть тест на 1 из (4294967292+ 1) неверных случаев. Вероятность 2.xe-10 или 0 для большинства практических целей. Такова тщетность проверки NULL.
AFAIK нет способа. Вы должны попытаться избежать этой ситуации, всегда устанавливая указатели на NULL после освобождения памяти.
Относительно ответа в этом потоке:
IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () для Windows.
blockquote>Мой совет - держаться подальше от них, кто-то уже разместил это: http://blogs.msdn.com/oldnewthing/archive/2007/06/25/3507294.aspx
Еще одно сообщение по той же теме и тем же автором (я думаю): http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx («IsBadXxxPtr действительно нужно называть CrashProgramRandomly»).
Если пользователи вашего API отправляются с плохими данными, пусть это сбой. Если проблема в том, что переданные данные не используются дольше (и это затрудняет поиск причины), добавьте режим отладки, в котором строки и т. Д. Регистрируются при записи. Если они плохие, это будет очевидно (и, вероятно, сбой). Если это происходит часто, возможно, стоит переместить API из процесса и позволить им разрушить процесс API вместо основного процесса.
Вот три простых способа для программы C под Linux получить интроспективную информацию о состоянии памяти, в которой она работает, и почему в некоторых контекстах заданы соответствующие сложные ответы.
В Microsoft Windows существует функция QueryWorkingSetEx, которая задокументирована в API состояния процесса (также в API NUMA). В качестве следствия сложного программирования NUMA API эта функция также позволит вам выполнять простые «указатели на проверку работоспособности (C / C ++)», поэтому маловероятно, что они будут устаревать не менее 15 лет.
Как говорили другие, вы не можете надежно обнаружить недопустимый указатель. Рассмотрим некоторые формы, которые может принять недопустимый указатель:
У вас может быть нулевой указатель. Это то, о чем вы могли бы легко проверить и что-то сделать.
У вас может быть указатель где-то за пределами допустимой памяти. То, что составляет действительную память, зависит от того, как среда выполнения вашей системы устанавливает адресное пространство. В системах Unix обычно это виртуальное адресное пространство, начинающееся с 0 и идущее на некоторое количество мегабайт. На встроенных системах это может быть довольно мало. В любом случае он может начинаться не с 0. Если ваше приложение работает в режиме супервизора или эквивалент, ваш указатель может ссылаться на реальный адрес, который может быть или не быть резервным с реальной памятью.
У вас может быть указатель где-то внутри вашу действительную память, даже внутри вашего сегмента данных, bss, стек или кучу, но не указывая на действительный объект. Вариант этого - указатель, который указывал на действительный объект, прежде чем что-то случилось с объектом. Плохие вещи в этом контексте включают освобождение памяти, повреждение памяти или повреждение указателя.
У вас может быть запрещенный указатель с плавающей точкой, например указатель с незаконным выравниванием для ссылки на предмет.
Проблема становится еще хуже, если вы рассматриваете архитектуры на основе сегментов / смещений и другие реализации нечетных указателей. Такие вещи, как правило, скрыты от разработчика хорошими компиляторами и разумным использованием типов, но если вы хотите пробить завесу и попытаться перехитрить операционную систему и разработчиков компилятора, ну, вы можете, но нет единого способа для этого, который будет обрабатывать все проблемы, с которыми вы могли бы столкнуться.
Лучшее, что вы можете сделать, это разрешить крах и выпустить хорошую диагностическую информацию.
эти ссылки могут быть полезны
_CrtIsValidPointer Проверяет, что указанный диапазон памяти действителен для чтения и записи (только для отладочной версии). http://msdn.microsoft.com/en-us/library/0w1ekd5e.aspx
_CrtCheckMemory Подтверждает целостность блоков памяти, выделенных в куче отладки (только отладочная версия ). http://msdn.microsoft.com/en-us/library/e73x0s4b.aspx
Эта статья MEM10-C. Определить и использовать функцию проверки указателя говорит, что можно сделать проверку в некоторой степени, особенно в ОС Linux.
Предотвращение сбоя, вызванного отправкой вызывающего абонента недопустимым указателем, является хорошим способом сделать тихие ошибки, которые трудно найти.
Разве не лучше, если программист, использующий ваш API, получить четкое сообщение о том, что его код подделка, сбив его, а не скрывая?
В Unix вы должны использовать ядро syscall, которое проверяет указатель и возвращает EFAULT, например:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdbool.h>
bool isPointerBad( void * p )
{
int fh = open( p, 0, 0 );
int e = errno;
if ( -1 == fh && e == EFAULT )
{
printf( "bad pointer: %p\n", p );
return true;
}
else if ( fh != -1 )
{
close( fh );
}
printf( "good pointer: %p\n", p );
return false;
}
int main()
{
int good = 4;
isPointerBad( (void *)3 );
isPointerBad( &good );
isPointerBad( "/tmp/blah" );
return 0;
}
return:
bad pointer: 0x3
good pointer: 0x7fff375fd49c
good pointer: 0x400793
Возможно, есть лучше использовать syscall, чем open () [возможно, доступ], так как есть вероятность, что это может привести к фактической кодовой программе создания файла и последующему закрытию.
IsBadReadPtr (), IsBadWritePtr (), IsBadCodePtr (), IsBadStringPtr () для Windows. Они занимают время пропорционально длине блока, поэтому для проверки работоспособности я просто проверяю начальный адрес.
У меня много сочувствия на ваш вопрос, так как я сам в почти одинаковой позиции. Я ценю то, что говорят многие ответы, и они правильны - в подпрограмме, указывающей указатель , должен быть предоставлен действительный указатель. В моем случае почти невозможно представить, что они могли испортить указатель, но если они управляли , это будет сбой программного обеспечения MY, и ME, который получит вину: - (
Мое требование не в том, что я продолжаю после ошибки сегментации - это было бы опасно - я просто хочу сообщить, что случилось с клиентом, до завершения, чтобы они могли исправить свой код, а не обвинять меня!
Вот как я нашел это (в Windows): http://www.cplusplus.com/reference/clibrary/csignal/signal/
Чтобы дать синопсис:
#include <signal.h>
using namespace std;
void terminate(int param)
/// Function executed if a segmentation fault is encountered during the cast to an instance.
{
cerr << "\nThe function received a corrupted reference - please check the user-supplied dll.\n";
cerr << "Terminating program...\n";
exit(1);
}
...
void MyFunction()
{
void (*previous_sigsegv_function)(int);
previous_sigsegv_function = signal(SIGSEGV, terminate);
<-- insert risky stuff here -->
signal(SIGSEGV, previous_sigsegv_function);
}
Теперь этот появляется , чтобы вести себя так, как я надеюсь (он печатает сообщение об ошибке, а затем завершает программу), - но если кто-то может заметить недостаток, пожалуйста, дайте мне знать!
exit()
, он обходит RAII и, следовательно, может вызвать утечку ресурсов.
– Sebastian Mach
9 May 2012 в 12:30
exit()
, и мой портативный C ++ Alarm Bell начал звонить. Это должно быть хорошо в этой конкретной ситуации Linux, где ваша программа выйдет в любом случае, извините за шум.
– Sebastian Mach
9 May 2012 в 13:15
man 2 signal
в Linux есть параграф, объясняющий почему.
– rptb1
22 October 2015 в 13:22
Я видел, что в разных библиотеках используется какой-либо метод для проверки непечатной памяти и т. д. Я считаю, что они просто «переопределяют» методы распределения памяти и удаления (malloc / free), которые имеют некоторую логику, которая отслеживает указатели. Я полагаю, что это излишне для вашего случая использования, но это был бы один из способов сделать это.
вам следует избегать этих методов, потому что они не работают. blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx - JaredPar
blockquote>Если они не работают - следующее обновление Windows это исправит? Если они не работают на уровне концепции - функция будет, вероятно, полностью удалена из windows api.
Документация MSDN утверждает, что они запрещены, и причиной этого является, вероятно, недостаток дальнейшей разработки приложения (например, как правило, вы не должны использовать недействительные указатели молча - если вы отвечаете за разработку всего приложения курс) и производительность / время проверки указателя.
Но вы не должны утверждать, что они не работают из-за какого-то блога. В моем тестовом приложении я проверил, что они работают.
Установка указателя на NULL до и после использования является хорошей техникой. Это легко сделать в C ++, если вы управляете указателями внутри класса, например (строка):
class SomeClass
{
public:
SomeClass();
~SomeClass();
void SetText( const char *text);
char *GetText() const { return MyText; }
void Clear();
private:
char * MyText;
};
SomeClass::SomeClass()
{
MyText = NULL;
}
SomeClass::~SomeClass()
{
Clear();
}
void SomeClass::Clear()
{
if (MyText)
free( MyText);
MyText = NULL;
}
void SomeClass::Settext( const char *text)
{
Clear();
MyText = malloc( strlen(text));
if (MyText)
strcpy( MyText, text);
}
В C ++ нет никаких условий для проверки правильности указателя как общего случая. Очевидно, что NULL (0x00000000) плохой, и различные компиляторы и библиотеки любят использовать «специальные значения» здесь и там, чтобы облегчить отладку (например, если я когда-либо вижу указатель, отображаемый как 0xCECECECE в визуальной студии, я знаю Я сделал что-то не так), но правда в том, что, поскольку указатель - это просто индекс в памяти, почти невозможно сказать, просто взглянув на указатель, если это «правильный» индекс.
Существуют различные трюки, которые вы можете сделать с dynamic_cast и RTTI такими, чтобы гарантировать, что указанный объект имеет тот тип, который вы хотите, но все они требуют, чтобы вы указывали на что-то действительное в первую очередь ,
Если вы хотите, чтобы программа могла обнаруживать «недействительные» указатели, то мой совет таков: установите каждый указатель, который вы объявляете либо NULL, либо действительный адрес сразу после создания, и установите его в NULL сразу после освобождения память, на которую указывает. Если вы прилежны в этой практике, тогда проверка NULL - это все, что вам когда-либо понадобится.
Технически вы можете переопределить оператор new (и удалить) и собрать информацию обо всей выделенной памяти, чтобы вы могли иметь способ проверить, действительно ли память кучи. но:
a) выделена память на этом адресе
b) память на этом адресе является начальным адресом объекта (например, адрес не находится в середине огромного массива)
c) память на этом адресе является начальным адресом объекта ожидаемого типа
. Нижняя строка: подход, о котором идет речь, не является способом C ++, вам необходимо определить некоторые правила, которые гарантируют, что функция получит действительные указатели.
Следующее работает в Windows (кто-то предложил это раньше):
static void copy (void * target, const void * source, int size) {__try {CopyMemory (target, source, size); } __except (EXCEPTION_EXECUTE_HANDLER) {doSomething (- whatever--); }}
Функция должна быть статичной, автономной или статической для некоторого класса. Чтобы проверить только на чтение, скопируйте данные в локальный буфер. Чтобы протестировать запись без изменения содержимого, напишите их. Вы можете проверить только первые / последние адреса. Если указатель недействителен, управление будет передано в «doSomething», а затем вне скобок. Просто не используйте ничего, требующее деструкторов, например CString.
Действительно, что-то может быть сделано в определенном случае: например, если вы хотите проверить, является ли строка указателя строки действительной, использование syscall (fd, buf, szie) syscall может помочь вам сделать магию: пусть fd будет файлом дескриптор временного файла, который вы создаете для теста, и buf, указывающий на строку, которую вы tesing, если указатель недействителен. write () возвращает -1 и errno устанавливает значение EFAULT, указывающее, что buf находится за пределами вашего доступного адресного пространства.
Невозможно выполнить эту проверку на C ++. Что делать, если другой код передает вам недопустимый указатель? Вы должны потерпеть крах. Почему? Посмотрите эту ссылку: http://blogs.msdn.com/oldnewthing/archive/2006/09/27/773741.aspx