Узнайте, является ли полученный указатель строкой, ushort или массивом
Я вставляю функцию memcpy() в C, потому что целевое приложение использует ее для объединения строк, и я хочу выяснить, какие строки создаются. Код является:
void * my_memcpy ( void * destination, const void * source, size_t num )
{
void *ret = memcpy(destination, source, num);
// printf ("[MEMCPY] = %s \n", ret);
return ret;
}
Функция вызывается успешно, но первый параметр может быть любым, и я хочу отследить его, только если результатом является строка или массив. Я хотел бы спросить, если это массив или строка. Я знаю, что это не может быть сделано просто: есть ли способ узнать, на что указывает RET?
Я работаю под MACOSX и работаю с DYLD.
Большое спасибо.
4 ответа
Как void*
представляет собой необработанный блок памяти, нет способа определить, какие именно данные там находятся.
Тем не менее, вы можете создать "строковый" дамп памяти для каждой операции, просто дать результирующему выводу своего рода "верхний предел вывода".
Это может быть реализовано следующим образом:
const size_t kUpperLimit = 32;
void output_memory_dump(void* memory) {
std::cout.write(reinterpret_cast<char*>(memory), kUpperLimit);
}
Для нестроковых данных вывод будет трудно интерпретируемым, но в противном случае вы получите то, что искали.
Вы можете попытаться применить некоторый основанный на предположениях подход, такой как итерация reinterpret_cast<void*>(memory)
и делая is_alphanumeric && is_space
проверяет каждый символ, но этот подход не кажется очень устойчивым (кто знает, что на самом деле может лежать в этом void*
...)
Во всяком случае, для некоторых ситуаций это может быть хорошо.
Сначала вы можете применить некоторую эвристику к скопированной памяти и на основании этого вы можете решить, хотите ли вы распечатать ее.
static int maybe_string(const void *data, size_t n) {
const unsigned char *p;
size_t i;
p = data;
for (i = 0; i < n; i++) {
int c = p[i];
if (c == '\n' || c == '\r' || c == '\t')
continue;
if (1 <= c && c < 32)
return 0; /* unusual ASCII control character */
if (c == '\0' && i > 5)
return 1; /* null-terminated and more than a few characters long */
}
return 0; /* not null-terminated, so it isn't a string */
}
Эта эвристика не идеальна. Например, это терпит неудачу для следующего шаблона:
const char *str = "hello, world";
size_t len = strlen(str);
char *buf = malloc(1024);
memcpy(buf, str, len);
buf[len] = '\0';
Если вы хотите поймать это тоже, вам придется изменить вышеуказанную функцию.
ret
равно указателю назначения. Но невозможно определить, является ли это массив или строка, если вы не знаете больше информации о массиве или строке (например, строка имеет определенную длину и заканчивается нулем).
Нет, вы не можете понять это по указателю типа void. Кроме того, вы не знаете размера источника или места назначения, поэтому эвристический подход не будет работать. Он также не будет работать по другим причинам, например, двоичные данные, хранящиеся в области памяти, указанной void*
действительно может иметь нулевой байт в конце, но это не значит, что это строка.