【问题标题】:C void pointer arithmeticC void 指针算术
【发布时间】:2013-03-05 16:13:54
【问题描述】:

我认为在 gcc 中,void * 和 char * 在指针运算方面的处理方式相同,即 void * “指向”内存中的单个字节,所以下面的代码

void *p;
p = malloc(sizeof(void));
printf("%p %p\n",p,p+1);

确实返回0x984a008 0x984a009。类似地,void ** 指向一个指针,因此递增 1 实际上意味着递增 4 个字节(在 32 位操作系统上),即

void **p;
p = (void **) malloc(sizeof(void *));
printf("%p %p\n",p,p+1);

返回0x984a008 0x984a00c。但是,下面的代码让我很困惑

void **p, *p1;
p = (void **) malloc(sizeof(void *));
p1 = (void **) p;
printf("%p %p\n",p1,p1+1);

因为它再次返回0x984a008 0x984a009。这里发生了什么?

【问题讨论】:

  • 也许您的意思是输入 void **p, **p1; 而不是 void **p, *p1;
  • sizeof(void) 为 1 或至少给出警告,没用。你不这样做。 void 类型表示什么?只有void*
  • 我的意思是 void **p, *p1。我知道这段代码可以编译,但我不明白为什么如果 p 最初声明为 void **,p+1 对应于增量 4 个字节,而如果 p1 被声明为 void * 并随后重铸为 void **,算法只为 p1+1 增加 1 个字节
  • 为什么,void ** 的演员表被忽略了?
  • @Ivan:是的,显然它被忽略了。这是一个身份转换(表达式p 已经有类型void**)。身份转换被忽略,除了对值类别有一些影响(例如,转换的结果是一个右值)

标签: c pointers


【解决方案1】:

暂时忽略void 指针算法可能存在的未定义行为...

p1 的类型是void *

您不能通过为变量分配不同类型的值来更改变量的类型。 p1 将永远停留在void *

分配给它的任何不同类型的表达式都将被隐式转换为void *(如果不能,则给出错误)。

因此它与第一个示例基本相同。

编辑:

据我所知,从一种指针类型转换为另一种实际上并没有做任何事情,它的主要目的是进行类型检查。

指针只是一个内存地址,一个数字,所以基本上内存看起来像:(赋值后)

  p1       p2
void *   void** <- these types are fixed and known during compilation
------   ------
|1234|   |1234|         at address 1234 = the 4 bytes from malloc
------   ------
  ^
  |
this value is the only thing that will change by assigning p1 to a different value

【讨论】:

  • 在某些架构上,从一种指针类型转换为另一种确实会改变一些东西。但是,在典型的 x86(_64) 架构上,您可以期望所有指针具有相同的表示形式,并且强制转换是无操作的。
【解决方案2】:

您应该使用char * 而不是void *,因为指向void 的指针的算术运算是gcc 扩展。

char *p1 = /* ... */;

printf("%p %p\n", p1, p1+1);

无论指向pp 上的指针运算使用char * 类型(不是char **)。

如果你写:

char *p1 = /* ... */;

printf("%p %p\n", p1, (char**)p1+1);

指针运算使用char **

【讨论】:

    【解决方案3】:

    使用void *时,增量为1。使用void **时,为指针大小。

    在使您感到困惑的操作中,您将void * 转换为void ** 被隐式地重新转换为void *。就好像你这样做了:

    long a, b, c;
    c = a + (int) b;
    

    您将b 转换为int,但是您想使用long 进行操作,所以它被转换回了。

    【讨论】:

    • 这很好,但是编译器为什么要重铸回来呢? void *void ** 在堆栈上占用 4 个字节,据我所知,所有指针运算都是在编译时完成的。那么为什么编译器坚持要求 p1 被强制回退呢?
    • 这样想:将void ** 转换为void * 是有意义的——任何指针都可以变成void *。另一方面,反之亦然。并非所有指针都是双指针。编译器对您请求的操作执行敏感操作:它以唯一的方式理解它。
    【解决方案4】:

    void 指针不能递增。这是未定义的行为。

    相关问题: Increment void pointer by one byte? by two?

    【讨论】:

    • 好的,但是如果您将所有类型更改为char 指针,问题仍然存在(尽管在这种情况下 GCC 会给您一个警告)
    【解决方案5】:

    我知道我要在一年多之后发帖,但我碰巧遇到了这个问题,这让我很感兴趣。

    我同意@Dukeling 的观点,即您不能仅通过强制转换来更改变量的类型。但这似乎取决于编译器认为void 是什么。以这个示例程序为例,看看结果输出。请注意,vpvp2 之间的唯一区别是 malloc()sizeof() 部分。

    编译于:gcc (Debian 4.7.2-5) 4.7.2
    编译行:gcc -o void_test void_test.c

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char **argv) {
      void *vp, *vp2;
    
      printf("sizeof(void)   = %d\n", sizeof(void));
      printf("sizeof(void *) = %d\n", sizeof(void *));
      printf("sizeof(char)   = %d\n", sizeof(char));
      printf("sizeof(char *) = %d\n\n", sizeof(char *));
    
      vp = (void *) malloc(sizeof(void));
      vp2 = (void *) malloc(sizeof(void *));
    
      printf("vp    = %p\n", vp);
      printf("vp+1  = %p\n", vp+1);
      printf("vp2   = %p\n", vp);
      printf("vp2+1 = %p\n", vp2+1);
    
      return 0;
    }
    

    给出以下输出:

    $ ./void_test 
    sizeof(void)   = 1
    sizeof(void *) = 8
    sizeof(char)   = 1
    sizeof(char *) = 8
    
    vp    = 0x1ee3010
    vp+1  = 0x1ee3011
    vp2   = 0x1ee3010
    vp2+1 = 0x1ee3031
    

    【讨论】:

      猜你喜欢
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 2014-04-09
      • 2021-04-29
      • 1970-01-01
      • 2012-07-27
      相关资源
      最近更新 更多