C 在同一变量中强制转换void指针会导致编译错误

C 在同一变量中强制转换void指针会导致编译错误,c,pointers,C,Pointers,我正在测试一个问题,我在同一个变量中将void指针转换为不同类型时遇到了这个问题。问题似乎是在尝试对强制转换指针使用相同的变量时。下面我有一段代码来测试这一点。有人能告诉我为什么程序不能针对这种情况编译吗 #include <stdlib.h> typedef struct test { int a; }test; int main() { void * check; test * check2; check = malloc(sizeof(test));

我正在测试一个问题,我在同一个变量中将void指针转换为不同类型时遇到了这个问题。问题似乎是在尝试对强制转换指针使用相同的变量时。下面我有一段代码来测试这一点。有人能告诉我为什么程序不能针对这种情况编译吗

#include <stdlib.h>

typedef struct test {

  int a;

}test;

int main() {

  void * check;
  test * check2;
  check =  malloc(sizeof(test));

  check = ((test *) check);
  check2 = (test *)check;


  check->a = 2; //this raises an error (dereferencing void pointer) when compiling
  check2->a = 2; //this works
  ((test *)check)->a = 2; //this also works
}
#包括
类型定义结构测试{
INTA;
}试验;
int main(){
作废*支票;
测试*检查2;
检查=malloc(sizeof(test));
检查=((测试*)检查);
检查2=(测试*)检查;
check->a=2;//这会在编译时引发错误(取消引用void指针)
选中2->a=2;//这是可行的
((test*)check)->a=2;//这同样有效
}

投射指针只是对编译器撒谎

void * ptr = malloc(sizeof(test));
((test *)ptr)->a = 5;
在第二行中,我们告诉编译器“我知道我将ptr声明为(void*),但我比你聪明,相信我,它实际上是(test*)”。没有任何变化,ptr仍然只是一个指针,一个内存位置的地址,但编译器假定它指向特定的东西

  check->a = 2; //this raises an error (dereferencing void pointer) when compiling
每当您希望编译器将您的变量视为与您声明的变量不同的东西时,必须强制转换您的变量

一个更有趣的场景来解释如何使用强制转换指针

// struct with two ints, a and b
typedew struct testAB {
  int a;
  int b;
} testAB;

// struct with two ints, named in reverse order
typedef struct testBA {
  int b;
  int a;
} testBA;

int main(void) {
  // construct a testAB struct
  void *ptr = malloc(sizeof(testAB));
  ((testAB *)ptr)->a = 0
  ((testAB *)ptr)->b = 1

  // treat ptr as a testBA struct
  if ( ((testBA *)ptr)->a == 1) {
    printf("this shouldn't happen, 'a' was set to 0");
  }
}
如果运行上述代码,您将发现printf语句将被执行。即使我们将“a”设置为0,并且if语句检查a==1

这是因为结构非常简单。在上面的示例中,结构只是两个紧挨着的int。非常类似于两个整数的数组,如下所示:

int array[2] = {0, 1};
void *ptr = &array; 
即使使用这种表示法,我们也可以对编译器撒谎,我们可以强迫编译器将这个“数组”视为我们的结构之一

if ( ((testAB *)array)->a == 0 )
  printf("a is 0");
这是因为,在后台,编译器将结构中的命名变量视为仅从结构所在位置提供的变量

// the following two lines have the same affect
((testAB *)array)->a = 0;
array[0] = 0;

// the following two lines have the same affect
((testAB *)array)->b = 2;
array[1] = 2;
如果我们告诉编译器它是a(testAB*),那么“a”表示第一个int,而“b”表示第二个int。如果我们告诉编译器它是a(testBA*),那么“a”是第二个,而“b”是第一个


在编译代码中,所有变量名都将丢失。编译器将结构赋值减少为“将结构的第二个int设置为2”。或者更具体地说,如果我们处理的是32位整数,则将结构的字节5、6、7和8设置为0000、0000 0000 0010(二进制)(或者,如果我们是为一个小的endian CPU编译,则可能是相反的顺序)

您不能取消引用(即,将运算符
*
或运算符
->
应用于)空指针。您认为
检查=((测试*)检查);
更改
检查的类型
?@MooingDuck为什么不更改检查的类型?因为对象的类型由其声明决定,而不是由以后可能分配给它的任何值决定。此:
检查=((测试*)检查)
显式地将
check
的值从
void*
转换为
test*
,然后通过赋值隐式地将结果转换回
void*
。类似地,
int n;n=2.5;
不会导致
n
能够保存小数;它仍然是
int
e> check=((test*)check);什么也不做。您的编译器可能会对其进行优化