不安全地使用void *指针吗?

时间:2018-09-27 10:24:43

标签: c

例如下面的代码:

#include <stdio.h>

int main()
{
  char i = 0;
  char j = 0;
  char *p = &i;
  void *q = p;
  int *pp = q;
  printf("%d %d\n", i, j);
  *pp = -1;
  printf("%d %d\n", i, j);

  return 0;
}

如果我使用gcc版本8.1.0 x64(Ubuntu 8.1.0-5ubuntu1〜16.04)进行编译,则输出为:
0 0
-1 -1
现在,我使用强制转换为int *

#include <stdio.h>

int main()
{
  char i = 0;
  char j = 0;
  char *p = &i;
  void *q = p;
  int *pp = (int *)q;
  printf("%d %d\n", i, j);
  *pp = -1;
  printf("%d %d\n", i, j);

  return 0;
}

结果与之前的结果相同。
使用clang-6.0 x64时,输出为:
0 0
-1 0
代码显然是缓冲区溢出吗?
我希望我已经清楚地解释了。

2 个答案:

答案 0 :(得分:3)

您实际上有两个问题:

首先是您打破strict aliasing并拥有undefined behavior

第二个问题是,在大多数现代平台上,int的大小为 4 个字节,而指针pp仅指向单个< / em>字节。因此,您对*pp进行的分配将超出范围,并且 也会导致未定义的行为。

答案 1 :(得分:0)

强制转换或不强制转换,实际上是在尝试使用不兼容的类型,从而导致违反严格的别名规则。

根据C11,第6.5节,

  

一个对象的存储值只能由具有以下之一的左值表达式访问   以下类型:88)

     

-与对象的有效类型兼容的类型,

     

-与对象的有效类型兼容的类型的限定版本,

     

-一种类型,它是与有效类型相对应的有符号或无符号类型   对象

     

-一种类型,是与标准版本对应的有符号或无符号类型   对象的有效类型,

     

-聚合或联合类型,其中包括上述类型之一   成员(包括递归地包含子集合或所包含的联盟的成员),或

     

-字符类型。

但是,您尝试通过char类型访问为int分配的内存。这违反了严格的别名。

也就是说,该标准保证char的大小为1个字节,int的大小为>= char。因此,访问将超出范围,并导致不确定的行为。