我实际上不确定printf语句的工作方式。
char *po;
int y=9999;
po = &y;
printf("\n%d", *(int *)po);
我首先创建一个char指针,为它分配一个整数地址,然后在类型转换后将其打印回来。
我的猜测是(int *)po
将po转换为整数类型,然后*(int *)po
检索此整数类型指针所指向的值。
虽然不确定。
有人可以更好地解释吗?
如果po仍然是char *
但y是具有多个不同成员(int
,float
,char
等的结构)怎么办?
答案 0 :(得分:2)
我们逐行走。
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中有一条明确的规则,您可以将指向数据类型的任何指针转换为指向字符类型的指针(signed char
,unsigned char
或其中一个但未指定的{{1} }。
因此可以保证确定。
不过要小心。 C没有指定平台是大端还是小端,因此无法预先知道要指向char
的哪个字节-最高,最低有效或其他(在某些晦涩的平台上)
将任何数据类型的指针强制转换为字符指针的规则适用于所有int
,struct
,double
-整个数据类型的射击比赛。
现在,如果将其投射回float
并尝试打印,可能会遇到麻烦。
如果您要处理的是int
,并且它的第一个成员是struct
,那么您还是可以的。
否则,您将直接进入未定义的行为。
在某些计算机上,假设您要指向的内容至少长int
,您将获得一个值,该值存储为解释为sizeof(int)
的值,但在某些计算机上,地址可能不正确对齐。如果对象不够大(int
),并且您可能会遇到一些保护故障,甚至可能是“ nextaligned int up”的值。
在某些非常晦涩的体系结构上,您可能会遇到“陷阱表示”并中止。
答案 1 :(得分:1)
指针是一个数字,指示存储部分的开始。 例如,您可以编写
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;
通常,您只能读/写到任意内存地址,因此您将需要一些有效内存的地址:一个现有变量,它是使用malloc或new(C ++)分配的。 此限制在现代处理器上是正确的,但例如在Arduino上,您不应在地址空间内读取/写入任意位置时出现访问冲突。
float x = 10.0f;
void *ptr = (void*)&x;
int*ptrf = (int*)(void*)&x;
MyStructWithManyFields *ptrstruct = (MyStructWithManyFields *)(void*)&x;
// you can still do conversions:
float valf = *(float*)ptrstruct; // valf = 10.0f
int vali = *(int*)ptrstruct; // vali=1092616192
您可以将any用作中间指针,因为这只是对编译器的提示。指针类型仅由于以下原因才很重要:向开发人员提示,指针算术,指针引用:
// 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;
答案 2 :(得分:0)
编译器未在内存中跟踪指针的“类型”。您可以将该指针转换为想要作为输入的任何类型的指针,并且在处理对printf的调用时,它不会对编译器产生任何影响,也不会在您通过passf时接收到任何会使它安全失败的类型信息“错误”类型。
有一个名为varargs的C工具,它是编译器,ABI和C标准库之间的一种协议。您可以在这里阅读有关内容:https://www.gnu.org/software/libc/manual/html_node/Variadic-Functions.html
简而言之,printf使用varargs工具来迭代传递给函数的参数。同时,它评估格式字符串以解释应该传递什么样的参数。然后根据需要强制转换varargs结果,以格式化输出数据。
这就是为什么printf格式字符串和参数列表之间的不匹配是一个巨大的安全漏洞。甚至有些编译器甚至会解释格式字符串,并警告您参数与格式之间的不匹配。
答案 3 :(得分:0)
我首先创建一个分配整数地址的char指针
不,您创建一个指向字符的指针,并为其分配地址 一个整数。
整数地址不是问题,整数也不存在与字符不同的神奇地址空间。
指针到整数是一个地址(本质上是无类型的,可以指向任何数据类型),该地址与类型(存储在该地址的对象是整数)耦合。 >
将int*
转换为char*
时,地址不变。您出于对自己最了解的原因,只是选择向编译器说谎该地址上存储的类型。
我的猜测是(int *)po将po转换为整数类型
当您将po
强制转换回int*
时,地址仍然保持不变,仍然是原始整数的地址。您只是向编译器承认,它不是“真正”存储在其中的字符。
强制转换为“整数类型”表示(int)po
,这不是您要做的。您似乎将指针的类型与其指向 at 的东西的类型混淆了。
然后
*(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,则需要阅读strict aliasing rules