C 空指针算法

C 空指针算法,c,pointers,C,Pointers,我认为在gcc中,当涉及指针运算时,void*和char*的处理方式是相同的,即void*指向内存中的单个字节,因此下面的代码 void *p; p = malloc(sizeof(void)); printf("%p %p\n",p,p+1); 确实返回0x984a008 0x984a009。类似地,void**指向一个指针,因此1的增量实际上意味着4字节的增量(在32位操作系统上),即 返回0x984a008 0x984a00c。然而,下面的代码让我感到困惑 void **p, *p1;

我认为在gcc中,当涉及指针运算时,void*和char*的处理方式是相同的,即void*指向内存中的单个字节,因此下面的代码

void *p;
p = malloc(sizeof(void));
printf("%p %p\n",p,p+1);
确实返回
0x984a008 0x984a009
。类似地,void**指向一个指针,因此1的增量实际上意味着4字节的增量(在32位操作系统上),即

返回
0x984a008 0x984a00c
。然而,下面的代码让我感到困惑

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

因为它再次返回
0x984a008 0x984a009
。这是怎么回事

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

相关问题:

使用
void*
操作时,增量为1。当您使用
void**
时,它是指针的大小

在让您感到困惑的操作中,您的
void*
强制转换为
void**
将被隐式转换回
void*
。就好像你这样做了:

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

您将
b
强制转换为
int
,但随后您希望使用
进行操作,因此它是强制转换的。

暂时忽略
无效
指针算术的可能未定义行为

p1
的类型是
void*

不能通过为变量指定不同类型的值来更改变量的类型<代码>p1将始终保持
无效*

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

因此,它本质上与第一个示例相同

编辑:

据我所知,从一个指针类型转换到另一个指针类型实际上没有任何作用,它的主要目的是进行类型检查

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

p1p2

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

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

printf("%p %p\n", p1, p1+1);
无论什么点
p
p
上的指针算法都使用
char*
类型(而不是
char**

如果你写:

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

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

指针算法使用
char**

我知道我在一年后发布了这篇文章,但我偶然发现了这个问题,它引起了我的兴趣

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

编译日期:gcc(Debian 4.7.2-5)4.7.2 编译行:
gcc-o void\u test void\u 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**p,**p1而不是
无效**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**
)。除了对值类别的某些影响(例如,强制转换的结果是右值)外,标识强制转换将被忽略。OK,但是如果您将所有类型更改为
char
pointers,问题仍然存在(尽管在这种情况下,GCC会给您一个警告),这很好,但是编译器为什么要重铸呢?
void*
void**
都在堆栈上占用4个字节,据我所知,所有指针算法都是在编译时完成的。那么,为什么编译器坚持p1被强制转换?请这样想:将
void**
强制转换为
void*
——任何指针都可以变成
void*
。另一方面,用另一种方法来浇铸是不可能的。并非所有指针都是双指针。编译器对您请求的操作执行敏感的操作:这是它唯一能理解的方法。在某些体系结构上,从一种指针类型转换到另一种指针类型确实会改变某些东西。但是,在典型的x86(_64)体系结构上,您可以期望所有指针都具有相同的表示形式,并且强制转换是不可操作的。
char *p1 = /* ... */;

printf("%p %p\n", p1, (char**)p1+1);
#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