指针类型转换和取消引用

时间:2018-07-16 14:47:13

标签: c pointers

我实际上不确定printf语句的工作方式。

char *po;    
int y=9999;    
po = &y;    
printf("\n%d", *(int *)po);

我首先创建一个char指针,为它分配一个整数地址,然后在类型转换后将其打印回来。 我的猜测是(int *)po将po转换为整数类型,然后*(int *)po检索此整数类型指针所指向的值。 虽然不确定。 有人可以更好地解释吗?

如果po仍然是char *但y是具有多个不同成员(intfloatchar等的结构)怎么办?

4 个答案:

答案 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 charunsigned char或其中一个但未指定的{{1} }。 因此可以保证确定。 不过要小心。 C没有指定平台是大端还是小端,因此无法预先知道要指向char的哪个字节-最高,最低有效或其他(在某些晦涩的平台上)

将任何数据类型的指针强制转换为字符指针的规则适用于所有intstructdouble-整个数据类型的射击比赛。

现在,如果将其投射回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