Нарушение доступа с использованием memcpy или присваивания массиву в структуре - PullRequest
1 голос
/ 10 апреля 2010

Обновление 2:

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


Обновление:

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

Я пытаюсь найти (простой / эффективный) способ изменения нескольких байтов массива в структуре. Мой текущий метод обхода: динамическое выделение буфера одинакового размера, копирование массива, внесение изменений в буфер, использование буфера вместо массива, затем освобождение буфера кажется чрезмерным и менее чем оптимальным. Если мне нужно сделать это таким образом, я могу просто поместить два массива в структуру и инициализировать их оба для одних и тех же данных, внеся изменения во второй. Моя цель состоит в том, чтобы уменьшить как объем памяти (хранить только различия между исходным и измененным массивами), так и объем ручной работы (автоматически исправлять массив).


Оригинальный пост:

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

Оригинальная версия имела жестко запрограммированный массив байтов. После некоторой обработки некоторые байты были записаны в массив, а затем была выполнена дополнительная обработка.

Чтобы избежать жесткого кодирования шаблона, я поместил массив в структуру, чтобы я мог добавить некоторые связанные данные и создать их массив. Однако сейчас я не могу записать в массив в структуре. Вот пример псевдокода:

main() {
  char pattern[]="\x32\x33\x12\x13\xba\xbb";
  PrintData(pattern);
  pattern[2]='\x65';
  PrintData(pattern);
}

Это работает, но это не так:

struct ENTRY {
  char* pattern;
  int   somenum;
};

main() {
  ENTRY Entries[] = {
      {"\x32\x33\x12\x13\xba\xbb\x9a\xbc", 44}
    , {"\x12\x34\x56\x78", 555}
  };
  PrintData(Entries[0].pattern);
  Entries[0].pattern[2]='\x65';   //0xC0000005 exception!!! :(
  PrintData(Entries[0].pattern);
}

Вторая версия вызывает исключение нарушения прав доступа для назначения. Я уверен, что это потому, что вторая версия распределяет память по-разному, но у меня начинает болеть голова, когда я пытаюсь понять, что и как и как это исправить. (В настоящее время я работаю над этим, динамически выделяя буфер того же размера, что и массив шаблонов, копируя шаблон в новый буфер, внося изменения в буфер, используя буфер вместо массива шаблонов, а затем пытаясь не забыть освободить «временный» буфер.)

(В частности, исходная версия привела массив шаблонов + смещение к DWORD * и присвоила ему константу DWORD для перезаписи четырех целевых байтов. Новая версия не может этого сделать, поскольку длина источника неизвестна - может быть не четыре байта - поэтому он использует memcpy вместо этого. Я проверил и перепроверил и убедился, что указатели на memcpy верны, но я все еще получаю нарушение прав доступа. Я использую memcpy вместо str (n) cpy потому что я использую простые символы (как массив байтов), а не символы Юникода и игнорирую нулевой терминатор. Использование присваивания, как указано выше, вызывает ту же проблему.)

Есть идеи?

Ответы [ 4 ]

7 голосов
/ 10 апреля 2010

Запрещено пытаться модифицировать строковые литералы. Ваш

Entries[0].pattern[2]='\x65'; 

строка пытается именно это. Во втором примере вы не выделяете память для строк. Вместо этого вы делаете свои указатели (в объектах структуры), чтобы указывать непосредственно на строковые литералы И строковые литералы не могут быть изменены.

Этот вопрос задают несколько раз каждый день. Прочитайте Почему этот код C обращения строки возвращает ошибку сегментации?

3 голосов
/ 23 июля 2010

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

Этот вопрос помечен как C ++, поэтому простой способ решить вашу проблему - использовать std::string.

struct ENTRY {
  std::string pattern;
  int         somenum;
};
3 голосов
/ 10 апреля 2010

Проблема сводится к тому, что char[] не является char*, даже если char[] действует во многом как char* в выражениях.

1 голос
/ 10 апреля 2010

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

Следующий пример кода показывает, как это сделать:

// Allocate the memory for the strings, on the stack so they'll be editable, and
// initialize them:
char ptn1[] = "\x32\x33\x12\x13\xba\xbb\x9a\xbc";
char ptn2[] = "\x12\x34\x56\x78";

// Now, initialize the structs with their char* pointers pointing at the editable
// strings:
ENTRY Entries[] = {
  {ptn1, 44}
, {ptn2, 555}
};

Это должно работать нормально. Однако обратите внимание, что память для строк находится в стеке и, следовательно, исчезнет, ​​если вы покинете текущую область. Конечно, это не проблема, если записи находятся в стеке (как в этом примере), поскольку они исчезнут одновременно.

Некоторые вопросы и ответы по этому вопросу:

В: Почему мы не можем инициализировать строки в инициализации массива структур? A: Поскольку сами строки не находятся в структурах, и инициализация массива выделяет память только для самого массива, а не для вещей, на которые он указывает.

В: Можем ли мы включить строки в структуры, тогда? A: нет; структуры должны иметь постоянный размер, а строки не имеют постоянного размера.

В: Это экономит память за счет наличия строкового литерала, а затем хранения памяти и копирования в него строкового литерала, в результате чего получается две копии строки, верно? A: Вероятно, нет. Когда вы пишете

char pattern[] = "\x12\x34\x56\x78";

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

...