【问题标题】:Arithmetics with void pointer带空指针的算术
【发布时间】:2021-02-15 07:48:27
【问题描述】:

我调用一个函数来交换内存中的消息:

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

           address1 = address2;
           address2 = '\0';
        }

所以基本上我有两个地址,一个指向第一个起始位置,另一个指向必须粘贴内容的第二个起始位置。 问题是我找到的唯一解决方案是添加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;

支持信息: 我有一个链表(充满了段描述符)和一个“内存”,它是一个简单的数组 []。类似于 malloc 的工作原理。

typedef struct segmentdescriptor

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

开始指针指向数组[]中分配空间的开始。

更新: 最简单的方法是使用类型转换对 void 指针进行算术运算,如果您知道它们的大小,例如:

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

如果你不知道类型,那是不可能的,例如: char *pointer 指向一个字节的内存,如果你写 pointer++ 去 到下一个字节。 int *pointer 可以说是四个再见。如果你写pointer ++ 则转到四个字节之后的四个字节。

下面也有很好的答案。 谢谢大家的回答。

【问题讨论】:

  • 你不能对 void * 进行算术运算。它怎么知道要加多少?
  • “我无法将 void 指针更改为 int”为什么?您应该能够将其转换为int(或任何其他能够保存指针而不会丢失数据的整数数据类型),然后将其转换回void*
  • start_adress 指向内存中第一个需要复制的地址
  • 通常的解决方案是将void* 转换为char*
  • void *address1 = now->start_adress + i; 但是你在下一个赋值address1 = address2; 中丢弃了赋值,而address2 = '\0'; 它建议你打算将一个char 值赋给一个指针变量。我认为您错过了一些取消引用运算符。您是否考虑过使用memcpymemmove

标签: c pointers memory-address


【解决方案1】:

我不太明白 shifts a massage in memory 是什么,但如果您只想将一个内存位置复制到另一个内存位置,您可以

  1. 使用memcpymemmove(如果内存位置重叠)

     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 * 视为指向 char 的指针,允许进行算术运算,但不允许取消引用。+

【讨论】:

  • mymemcpy() 的第一个版本中,您忘记增加csrc
【解决方案2】:

void 类型始终是不完整类型。也就是说void 类型的对象的大小是未知的。因此,尽管某些编译器有自己的语言扩展,允许使用 void * 类型的指针算术与使用 char * 类型的指针相同的方式,但您可能不会将指针算术与 void * 类型的指针一起使用。

所以这些陈述

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

根据 C 标准是不正确的,因为指针 start 的类型为 void *

似乎也有这些说法

address1 = address2;

address2 = '\0';

没有做你想做的事。

首先是这个声明

address1 = address2;

将一个指针分配给另一个指针,尽管看起来实际上您希望将一个指针指向的值分配给另一个指针指向的内存。

而这个语句等价于

address2 = NULL;

也就是说,它没有用终止零字符'\0'设置指向的内存。

如果如您所说,这些指针处理消息(字符串),那么您需要将指针转换为 char * 类型。

至于你的这句话

我调用一个函数来交换内存中的消息:

那么完全不清楚您要使用 for 循环做什么,因为在任何情况下循环都没有意义。

如果您需要将一个字符数组复制到另一个字符数组,请使用标准 C 函数 memcpymemmove,具体取决于您尝试复制数组的内容和方式。

【讨论】:

    【解决方案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;
    

    在任何现代计算机上,指针的值都是内存地址(通常是虚拟内存地址)。当你给一个指针加 1 时,它的值(也就是内存地址)会增加所指向类型的大小。

    例子:

     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 = '\0';
        }
    

    目前尚不完全清楚您要做什么,但也许这就是您要寻找的:

        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 = '\0';
        }
    

    【讨论】:

    • 指针的存储方式和实现方式的表示方式都无关紧要。指针算术不依赖它。
    • Consequently, your compiler gives you a warning. 在这种情况下编译器不知道它是如何工作的——因为它是 gcc 系列编译器。
    • @P__JsupportswomeninPoland 当然它不会,但将指针视为内存地址有助于解释为什么事情是这样的。所有现代系统都是这样工作的。
    • 指针算法只为同一个数组定义。使用术语数组元素来解释它的工作原理就足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    相关资源
    最近更新 更多