memcpy не работает, но назначение не указателей символов - PullRequest
0 голосов
/ 01 ноября 2010

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

Может кто-нибудь помочь мне понять, почему здесь не работает memcpy, или, что еще лучше, какЯ мог бы понять это сам.Мне очень трудно понять проблемы, возникающие в моем коде c / c ++.

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;
setStaticAndDynamicPointers(ppc, ppc2);

char c;
c = (*ppc)[1];  
assert(c == 'b');                     // assertion doesn't fail.

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
  puts("memcpy didn't work.");  // this gets printed out.

c = (*ppc2)[3];
assert(c=='d');                      // assertion doesn't fail.
memcpy(&c, &(*ppc2[3]), 1);

if(c != 'd')
  puts("memcpy didn't work again.");

memcpy(&c, pc, 1);
assert(c == 'a');   // assertion doesn't fail, even though used memcpy

void setStaticAndDynamicPointers(char **charIn, char **charIn2)
{
  // sets the first arg to a pointer to static memory.
  // sets the second arg to a pointer to dynamic memory.
  char stat[5];
  memcpy(stat, "abcd", 5);
  *charIn = stat;

  char *dyn = new char[5];
  memcpy(dyn, "abcd", 5);
  *charIn2 = dyn;
}

Ответы [ 7 ]

4 голосов
/ 01 ноября 2010

Ваш комментарий подразумевает, что char stat[5] должен быть статическим, но это не так.В результате charIn указывает на блок, который размещен в стеке, а когда вы возвращаетесь из функции, он выходит за рамки.Вы имели в виду static char stat[5]?

1 голос
/ 02 ноября 2010

При работе с указателями вы должны твердо держать перед собой следующие две точки:

# 1 Сам указатель отделен от данных, на которые он указывает . Указатель - это просто число. Число говорит нам, где в памяти мы можем найти начало некоторого другого фрагмента данных. Указатель может использоваться для доступа к данным, которые он указывает на , но мы также можем манипулировать значением самого указателя . Когда мы увеличиваем (или уменьшаем) значение самого указателя, мы перемещаем «назначение» указателя вперед (или назад) от точки, на которую он первоначально указывал. Это подводит нас ко второму пункту ...

# 2 Каждая переменная-указатель имеет тип , который указывает, какие данные указывают на . A char * указывает на char; int * указывает на int; и так далее. Указатель может даже указывать на другой указатель (char **). Тип важен, потому что, когда компилятор применяет арифметические операции к значению указателя, он автоматически учитывает размер типа данных, на который указывает. Это позволяет нам работать с массивами, используя простую арифметику указателей:

int *ip = {1,2,3,4};
assert( *ip == 1 );    // true

ip += 2;   // adds 4-bytes to the original value of ip
           // (2*sizeof(int)) => (2*2) => (4 bytes)

assert(*ip == 3);      // true

Это работает, потому что массив - это просто список идентичных элементов (в данном случае int s), последовательно расположенных в одном непрерывном блоке памяти. Указатель начинает указывать на первый элемент в массиве. Арифметика указателя позволяет нам продвигать указатель через массив, элемент за элементом. Это работает для указателей любого типа (за исключением того, что арифметика не разрешена на void *).

На самом деле, именно так компилятор переводит использование оператора индексатора массива []. Это буквально сокращение от добавления указателя с помощью оператора разыменования.

assert( ip[2] == *(ip+2) );  // true

Итак, как все это связано с вашим вопросом?

Вот ваши настройки ...

char *pc = "abcd";
char **ppc = &pc;
char **ppc2 = &pc;

на данный момент я упростила удаление вызова на setStaticAndDynamicPointers. (В этой функции тоже есть проблема - пожалуйста, смотрите ответ @ Nim и мой комментарий там, чтобы узнать больше о функции).

char c;
c = (*ppc)[1];  
assert(c == 'b');     // assertion doesn't fail.

Это работает, потому что (*ppc) говорит "дай мне все, на что ppc указывает". Это эквивалент ppc[0]. Это все совершенно верно.

memcpy(&c,&(*ppc[1]),1);

if(c!='b')
    puts("memcpy didn't work.");  // this gets printed out.

Проблемная часть, как отмечали другие, это &(*ppc[1]), что буквально означает «дай мне указатель на то, на что указывает ppc [1]».

Прежде всего, давайте упростим ... приоритет оператора говорит, что: &(*ppc[1]) совпадает с &*ppc[1]. Тогда & и * являются инверсиями и отменяют друг друга. Так &(*ppc[1]) упрощается до ppc[1].

Теперь, учитывая вышеприведенное обсуждение, мы теперь готовы понять , почему это не работает: Короче говоря, мы рассматриваем ppc, как будто он указывает на массив указателей, когда на самом деле он указывает только на один указатель.

Когда компилятор встречает ppc[1], он применяет арифметику указателей, описанную выше, и создает указатель на память, которая следует сразу за переменной pc - независимо от того, что эта память может содержать. (Поведение здесь всегда не определено).

Так что проблема вовсе не в memcopy(). Ваш вызов memcpy(&c,&(*ppc[1]),1) покорно копирует 1 байт (согласно запросу) из памяти, на которую указывает фальшивый указатель ppc[1], и записывает ее в символьную переменную c.

Как уже отмечали другие, вы можете исправить это, переместив круглые скобки:

memcpy(&c,&((*ppc)[1]),1)

Надеюсь, объяснение было полезным. Удачи!

0 голосов
/ 02 ноября 2010

Обратите внимание, что круглые скобки в выражениях в ваших операторах присваивания находятся в разных местах по сравнению с круглыми скобками в выражениях memcpy. Так что не слишком удивительно, что они делают разные вещи.

0 голосов
/ 01 ноября 2010

Хотя предыдущие ответы поднимают действительные баллы, я думаю, что другая вещь, на которую вам нужно обратить внимание, это правила вашего оператора, когда вы memcpy:

memcpy(&c, &(*ppc2[3]), 1);

Что здесь происходит? Это может быть не то, что вы собираетесь. Запись массива имеет более высокий приоритет , чем оператор разыменования, поэтому вы сначала попытаетесь выполнить арифметику указателя, эквивалентную ppc2++. Затем вы разыменовываете это значение и передаете адрес в memcpy. Это не то же самое, что (*ppc2)[1]. Результатом на моей машине является ошибка нарушения прав доступа (XP / VS2005), но в целом это неопределенное поведение. Однако, если вы разыменовываете так же, как делали это ранее:

memcpy(&c, &((*ppc2)[3]), 1);

Тогда это нарушение доступа исчезнет, ​​и я получу надлежащие результаты.

0 голосов
/ 01 ноября 2010

В дополнение к определению «stat», главная проблема в моих глазах состоит в том, что *ppc[3] не совпадает с (*ppc)[3].То, что вам нужно, это последний (четвертый символ из строки, на которую указывает ppc), но в ваших memcpy () вы используете первый, первый символ четвертой строки в ppc «строкового массива» (очевидно, что ppc немассив char*, но вы заставляете компилятор обрабатывать его как таковой).

При отладке таких проблем я обычно нахожу полезным распечатать адреса памяти и соответствующее содержимое.

0 голосов
/ 01 ноября 2010

Как и то, что сказал Прит, я не думаю, что проблема в memcpy.В вашей функции "setStaticAndDynamicPointers" вы устанавливаете указатель на автоматическую переменную, созданную в стеке вызова этой функции.К моменту выхода из функции память, на которую указывает переменная stat, больше не будет существовать.В результате первый аргумент ** charIn будет указывать на то, что не существует.Возможно, вы можете прочитать более подробно о фрейме стека (или записи активации) здесь: текст ссылки

Вы фактически создали в этом коде висячий указатель на переменную стека.Если вы хотите проверить копирование значений в стек var, убедитесь, что оно создано в функции вызывающего, а не в вызываемой функции.

0 голосов
/ 01 ноября 2010

char stat[5];

- это переменная стека, которая выходит из области видимости, она не // sets the first arg to a pointer to static memory.. Вам нужно malloc / new часть памяти, в которую вставлен abcd. Как вы делаете для charIn2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...