Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 指针类型转换和取消引用_C_Pointers - Fatal编程技术网

C 指针类型转换和取消引用

C 指针类型转换和取消引用,c,pointers,C,Pointers,实际上,我不确定printf语句是如何工作的 char *po; int y=9999; po = &y; printf("\n%d", *(int *)po); 我首先创建一个字符指针,给它分配一个整数地址,然后在类型转换后打印回来。 我猜int*po将po转换为整数类型,然后*int*po检索这个整数类型指针所指向的值。 不过我不确定。 有人能更好地解释一下吗 如果po仍然是一个char*但y是一个具有多个不同成员的结构,int、float、char等,该怎

实际上,我不确定printf语句是如何工作的

char *po;    
int y=9999;    
po = &y;    
printf("\n%d", *(int *)po);
我首先创建一个字符指针,给它分配一个整数地址,然后在类型转换后打印回来。 我猜int*po将po转换为整数类型,然后*int*po检索这个整数类型指针所指向的值。 不过我不确定。 有人能更好地解释一下吗


如果po仍然是一个char*但y是一个具有多个不同成员的结构,int、float、char等,该怎么办?

编译器不会在内存中跟踪指针的类型。您可以将该指针强制转换为任何类型的指针作为输入,并且在处理对printf的调用时,它不会对编译器产生任何影响,printf也不会收到任何类型信息,如果您传入错误的类型,它会安全地失败

有一个叫做varargs的C工具,它是编译器、ABI和C标准库之间的一种协议。您可以在此处阅读:

简而言之,printf使用varargs工具迭代传递到函数中的参数。同时,它对格式字符串求值,以解释应该传入何种类型的参数。然后,它根据需要强制转换varargs结果,以格式化输出数据

这就是为什么printf格式字符串和参数列表之间的不匹配是一个巨大的安全漏洞。有些编译器甚至会解释格式字符串,并警告您参数与格式不匹配

我首先创建一个字符指针,并分配一个整数地址

不,您创建一个指向char的指针,并为其分配一个整数的地址

整数地址不是什么东西,整数与字符的地址空间也不会有神奇的不同

指向整数的指针是一个基本上没有类型的地址,可以指向与某个类型耦合的任何数据类型。存储在该地址的东西是一个整数


将int*转换为char*时,地址不变。您只是选择对编译器撒谎,隐瞒存储在该地址的类型,原因您自己最清楚

我猜int*po将po转换为整数类型

当您将po强制转换回int*时,地址仍然保持不变,并且仍然是原始整数的地址。您刚刚向编译器承认它实际上不是存储在那里的字符

强制转换为整数类型意味着intpo,这不是您所做的。您似乎混淆了指针的类型和它指向的对象的类型

然后*int*po检索该整数类型指针所指向的值。不过我不确定

是的,没错。这与取消对任何其他指向整数的指针的引用相同,您可以得到它所指向的整数的值。您可以将表达式拆分为

int *pi = (int*)po;
int i = *pi;
然后打印出来。您还可以使用打印指针的地址,以确认所有内容都是您所期望的,或者只是在调试器中检查这些值

char *po;
int *pi;
int i;
int y=9999;    
po = (char *)&y;   
pi = (int *)po;
i = *pi;

printf("y=%d\n &y=%p\n po=%p\n pi=%p\n i=%d\n &i=%p\n",
       y, (void*)&y, (void*)po, (void*)pi, i, (void*)&i);
如果。。。y是具有多个不同成员的结构体

你只是在问,当你把一个指向X的指针转换成指向Y的指针,然后再转换回指向X的指针时,会发生什么

很好。您只是在讲述指向类型的故事,但地址从未更改


如果要通过指向Y的指针访问X,则需要读取

指针是一个数字,指示内存段的开头。 例如,你可以写

void* ptr = 0xaa3156bc;
现在,您有了这个地址,您想在其中读取数据,但是如何解释其中的内容呢

您可以告诉编译器通过转换为int来读取4个字节作为int,或者通过转换为双精度数来读取8个字节,或者通过转换为char来读取一个字节

int vali = *(int*)ptr;
double vals = *(double*)ptr; // valid operation in C but can fail/have unexpected consequences
char valc = *(char*)ptr;
char *valstr = (char*)ptr;

正常情况下,你可以读/写任意的内存地址,所以你需要一些有效内存的地址:一个现有变量,用Malc或新C++分配的东西。 这种限制在现代处理器上是正确的,但在Arduino上,例如,在地址空间中读取/写入任意位置时,不应出现访问冲突

您可以使用任何作为中间指针,因为它只是编译器的一个提示。指针类型仅在以下原因下才重要:对开发人员的提示、指针算法、指针引用:

// given
void *pvoid = 0x0000aa10; // just as an example, do not do this in practice.
int *pi = (int*)pvoid;
char *pc = (char*)pvoid;
StructOfSize9bytes *ps = (StructOfSize9bytes *)pvoid;
// then
pvoid++; // compiler error;
pi++; // pi= 0x0000aa14;
pc++; // pc= 0x0000aa11;
ps++; // ps= 0x00aaaa19;

我们一行一行走吧

char *po;   //po is a pointer to character.
int y=9999; //y is an int initialised to 9999.  
po = &y;    //po is assigned to point to the first character (i.e. byte) of y.
printf("\n%d", *(int *)po); //po is cast back to a pointer to int, dereferenced and printed.
在C语言中有一个明确的规则,可以将指向数据类型的任何指针强制转换为指向字符类型的指针—有符号字符、无符号字符或其中一个,但未指定字符。 所以这是可以保证的。 不过要小心。C没有指定平台是big-endian还是little-endian,因此无法预先知道所指向的int的哪个字节——在一些模糊的平台上是最重要的、最不重要的还是其他的

将任何数据类型指针强制转换为char指针的规则适用于struct、double、float等所有数据类型

现在,如果您将它转换回int并尝试打印它,您可能会遇到麻烦。 如果您正在处理一个结构,并且它的第一个成员是int,那么您仍然可以。 否则你会直接进入未定义的行为。
在某些机器上,假设您指向的对象至少是sizeofint long,您将得到一个值,该值是存储为int的值,但在某些机器上,地址可能没有正确对齐。如果对象不够大,那么如果po仍然是一个char*但y是一个具有多个不同成员的结构,int、float、char等等,会怎么样。?如果您从一个浮点开始,然后尝试通过int*访问它,那将是一种违规行为。这个问题真的是一个复制品,而不是C++。请删除冗余标记。int*po将变量po转换为指向int的指针,而不是指向int的指针。星号*表示指针。所以*int*po所做的是将变量po强制转换为指向int的指针,然后取消对指针的引用,以获得int*po.char*所指向的实际int值,而不必使用严格的别名。这样来回转换指针类型有什么原因吗?有时有很好的理由这样做,有时这是合法的,但其他时候,这只会让事情变得不必要的混乱。如果你有一个int,为什么不使用int*来指向它呢?我曾经认为,对于编译器来说,解释格式字符串并警告不匹配是一件非常疯狂的事情,这是一件很遥远的事情。但今天,这显然是必要的。任何不使用这种编译器的人都在为自己设置未捕获的bug,而大多数人都会说,他们的行为非常不负责任,或者至少是不专业。你可以将该指针转换为任何类型的指针作为输入,这对printf没有任何影响。否。打印F%p。。。应为空*并且可能允许字符指针。其他类型的指针,特别是指向函数的指针未指定为printf%p…可接受。。。。最好使用显式强制转换:printf%p,void*some\u对象_pointer@chux当然printf关心指针实际指向什么,但是printf无法知道您实际传入的是什么类型。这就是我的意思。当你将int*转换成char*时,地址不变->转换创建了指向相同内存的指针。然而,指向int的指针和指向char的指针可能编码不同,甚至不需要相同的宽度。这两种特性都是不常见的,但都受到C语言的支持。例如,我知道数据指针和函数指针可能不同。但是别名规则要求int*和char*指向相同的内存位置,所以我认为它们都是相同的地址,即使它们的编码不同。当然,任何转换都必须是可逆的。@untible:任何转换都必须是可逆的,这是错误的。根据C 2011 N1570 6.3.2.3 7,虽然指向对象类型的指针可能会转换为指向不同对象类型的指针,“如果生成的指针未与引用类型正确对齐,则行为未定义。”如果要向人们教授C规则,请遵守规则。在这个答案中有许多有问题的陈述。Re“否,您创建了一个指向char的指针,并为其分配了一个整数的地址”:OP对其响应的语句可能措词粗糙,但英语足够宽容,它有一个正确的解释。“Re”本质上是无类型的地址,可以指向任何数据类型:“这在C语言中通常是不正确的;编写该标准是为了允许不正确的体系结构。Re”…整数不存在于与字符神奇不同的地址空间中”:这取决于实现。Re“当您将int*转换为char*时,地址不变”:这取决于实现。Re“您只是选择向编译器隐瞒存储在该地址的类型”:这是错误的。根据C 2011 N1570 6.2.6,从指向int的指针转换的char地址中有字节,程序可以通过指向char的指针访问字节。void*ptr=0xaa3156bc;是一个问题,因为void*0xaa3156bc不是一个合法的地址:UB 1。double vals=*double*ptr;其他字节很容易使UB2失败,即使ptr是无效的一个合法的地址,对于对象类型double它可能没有正确对齐。我对文本做了一些修改,现在应该更好了。虽然我同意它不合法,但它是一个有效的C操作,例如在实模式下运行的处理器。根据我的经验,C初学者如果理解指针只是一个数字,就可以更容易地理解这个概念。
char *po;   //po is a pointer to character.
int y=9999; //y is an int initialised to 9999.  
po = &y;    //po is assigned to point to the first character (i.e. byte) of y.
printf("\n%d", *(int *)po); //po is cast back to a pointer to int, dereferenced and printed.