Арифметика с указателем void

#c #pointers #memory-address

#c #указатели #адрес памяти

Вопрос:

Я вызываю функцию, которая меняет местами сдвиги массажа в памяти:

         for (int i = now->size - 1; i >= 0; i--)
        {
           void *address2 = prev->start_address   i;
           void *address1 = now->start_adress   i;

           address1 = address2;
           address2 = '';
        }
  

Итак, в основном у меня есть два адреса, один из которых указывает на первое начальное местоположение, а другой — на второе начальное местоположение, в которое необходимо вставить содержимое.
Проблема в том, что единственное решение, которое я нахожу, — это добавить int (это значение i) и prev->start_adress (это void *), как я показал. Я хочу сделать это правильно, я не могу изменить указатель void на int. Есть ли какие-либо другие возможности.

Мои ошибки:

 error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
 warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
  214 |                void *address2 = prev->start   i;
Warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
  215 |                void *address1 = now->start   i;
  

Вспомогательная информация:
У меня есть связанный список (полный segementdescriptors) и «память», представляющая собой простой массив [].Аналогично тому, как работает malloc.

 typedef struct segmentdescriptor

{
   Byte allocated;
   void *start;
   size_t size;
   struct segmentdescriptor *next;
} Node;
  

начальные указатели указывают на начало выделенного пространства в массиве[].

Обновление: самый простой способ — использовать typecast для выполнения арифметики с указателями void, если вы знаете их размер, например :

 char *address2 = (char *)prev->start   i;
  

Если вы не знаете тип, это невозможно, потому что, например:
char *pointer указывает на один байт памяти, и если вы пишете pointer , переходит
к следующему байту. int *pointer допустим, указывает четыре раза. если вы пишете pointer , переходит к четырем байтам после четырех байтов.

Ниже также есть хорошие ответы. Спасибо за все ответы.

Комментарии:

1. Вы не можете выполнять арифметику с void *. Как он узнает, сколько нужно добавить?

2. «я не могу изменить указатель void на int» почему? вы должны иметь возможность преобразовать его в int (или любой другой целочисленный тип данных, который способен удерживать указатель без потери данных), а затем преобразовать его обратно в void*

3. start_adress указывает на первый адрес в памяти, который необходимо скопировать

4. Обычным решением является приведение void* к char* .

5. void *address1 = now->start_adress i; но вы отбрасываете присвоенное значение при следующем присваивании address1 = address2; , и что в address2 = ''; этом говорит о том, что вы намерены присвоить значение char переменной указателя. Я думаю, вы пропустили несколько операторов разыменования. Рассматривали ли вы возможность использования memcpy or memmove ?

Ответ №1:

Я не совсем понимаю, что shifts a massage in memory это такое, но если вы просто хотите скопировать одну ячейку памяти в другую, вы можете

  1. Используйте memcpy или memmove (если ячейки памяти перекрываются)

      memcpy(now->start_adress, prev->start_address, now->size);
      
  2. Напишите свою собственную функцию для копирования памяти

 void *mymemcpy(void *dest, const void *src, size_t size)
{
    unsigned char *cdest = (unsigned char *)dest;  //cast for C   compiler
    const unsigned char *csrc = (unsigned char *)src;

    while(size--) *cdest   = *csrc;
    return dest;
}
  
 void *mymemcpy(void *dest, const void *src, size_t size)
{
    unsigned char *cdest = (unsigned char *)dest;  //cast for C   compiler
    const unsigned char *csrc = (unsigned char *)src;

    for(size_t index = 0; index < size; index  ) cdest[index] = csrc[index];
    return dest;
}
  
 void *mymemcpy(void *dest, const void *src, size_t size)
{
    unsigned char *cdest = (unsigned char *)dest;  //cast for C   compiler
    const unsigned char *csrc = (unsigned char *)src;

    for(size_t index = 0; index < size; index  ) *(cdest   index) = *(csrc   index);
    return dest;
}
  

Стандарт C не допускает никакой арифметики указателей на void указатели. gcc имеет расширение, которое обрабатывает указатель void * as на char, разрешающий арифметику, но не допускающий разыменования.

Комментарии:

1. в первой версии mymemcpy() вы забыли увеличить csrc

Ответ №2:

Тип void всегда является неполным типом. То есть размер объекта типа void неизвестен. Таким образом, вы не можете использовать арифметику указателя с указателем типа void * , хотя некоторые компиляторы имеют свои собственные языковые расширения, которые позволяют выполнять арифметику указателя с типом void * так же, как с указателями типа char * .

Итак, эти утверждения

 void *address2 = prev->start   i;
void *address1 = now->start   i;
  

неверны в соответствии со стандартом C, поскольку указатель start имеет тип void * .

Также кажется, что эти утверждения

 address1 = address2;

address2 = '';
  

вы делаете не то, что вы делаете.

Для начала это утверждение

 address1 = address2;
  

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

И это утверждение эквивалентно

 address2 = NULL;
  

То есть он не устанавливает указанную память с завершающим нулевым символом '' .

Если, как вы говорите, эти указатели имеют дело с сообщением (строкой), тогда вам нужно привести указатели к типу char * .

Что касается этой вашей фразы

Я вызываю функцию, которая меняет местами сдвиги массажа в памяти:

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

Если вам нужно скопировать один массив символов в другой массив символов, используйте стандартную функцию C memcpy или memmove в зависимости от того, что и как вы пытаетесь скопировать массивы.

Ответ №3:

Что касается этих предупреждений:

  warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
  214 |                void *address2 = prev->start   i;
Warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
  215 |                void *address1 = now->start   i;
  

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

Пример:

  TYPE* p = SOME_ADDRESS;  // The value of p is now SOME_ADDRESS
 p = p   1;               // The value of p is now SOME_ADDRESS   sizeof(TYPE);

 p = SOME_ADDRESS;        // The value of p is now SOME_ADDRESS
 p = p   i;               // The value of p is now SOME_ADDRESS   i * sizeof(TYPE);
  

Другими словами: чтобы добавить целочисленное значение к указателю, нам нужно знать размер элемента, на который указывает указатель.

И это ваша проблема. У вас есть указатели void, поэтому вам нужно будет знать «sizeof void», чтобы обновить указатель. Но sizeof void может быть любым и не определяется стандартом. По-видимому, ваш компилятор имеет нестандартное расширение, которое позволяет sizeof(void) . Следовательно, ваш компилятор выдает вам только предупреждение.

Что касается этой ошибки:

 error: invalid conversion from ‘void*’ to ‘char*’
  

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

Наконец: даже если арифметика указателя была действительной, ваш код ничего не делает.

     for (int i = now->size - 1; i >= 0; i--)
    {
       void *address2 = prev->start_address   i;
       void *address1 = now->start_adress   i;

       address1 = address2;  // This only change the value of the pointer
                             // It doesn't change the memory that the
                             // pointer points to
       address2 = '';
    }
  

Не совсем понятно, что вы пытаетесь сделать, но, возможно, это то, что вы ищете:

     for (int i = now->size - 1; i >= 0; i--)
    {
       char *address2 = (char*)prev->start_address   i;
       char *address1 = (char*)now->start_adress   i;

       *address1 = *address2;
       *address2 = '';
    }
  

Комментарии:

1. Не имеет значения, как хранится указатель и как указатель представлен реализацией. Арифметика указателя не зависит от этого.

2. Consequently, your compiler gives you a warning. в этом случае компилятор точно не знает, как это работает, потому что это компилятор семейства gcc.

3. @P__JsupportswomeninPoland Наверняка это не так, но обработка указателей как адресов памяти помогает объяснить, почему все так, как есть. И все современные системы работают так.

4. Указатель arthmetic определяется только для одного и того же массива. Достаточно использовать термин элемент массива, чтобы объяснить, как это работает.