【问题标题】:What is the difference between float pointer and int pointer address?float 指针和 int 指针地址有什么区别?
【发布时间】:2013-04-03 15:01:51
【问题描述】:

我试图运行这段代码,

int *p;
float q;
q = 6.6;
p = &q;

虽然这将是一个警告,但我认为&qp 的大小相同,所以p 的地址可以是q。但是当我打印&qp 时,我得到了不同的输出。 这是我的输出

*p =  6.600000 
 q = 0.000000, p = 0x40d33333, &q = 0x7fffe2fa3c8c 

我错过了什么? 当指针和变量类型相同时,p&q 相同。

我的完整代码是

#include<stdio.h>
void main()
{   
    int *p;
    float q;
    q = 6.6;
    p = &q;
    printf("*p =  %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);
}

【问题讨论】:

  • 请显示您用于获取该输出的代码。
  • 这是int main(void),而不是void main()void main() 主要用于检测不太了解 C 的作者所写的书籍。
  • 您在 x86_64 上运行?那么printf("*p = %f \n q = %f, p = %p, &amp;q = %p \n",*p,q,p,&amp;q); 中可能发生的事情是推定的int *p 在通用寄存器中传递,doubleq 的提升值)在浮点寄存器中传递,两个指针再次在通用寄存器中。
  • 然后printf读取第一个浮点寄存器进行第一次%f转换,第二个浮点寄存器进行第二次%f[正好包含0],第一个通用寄存器-包含float 6.6 的位 - 打印p,第二个通用寄存器(包含p 的值)打印&amp;q。包含q 地址的寄存器仍未读取。
  • @RohitO:如果你知道int main(void)应该一直被使用,那么我建议你一直使用它;至少,它会避免像我这样的人的抱怨。至于为什么int*float* 是不同的类型,我只是在我的答案中添加了几段。

标签: c pointers


【解决方案1】:

您需要更认真地对待编译器警告。

C 不要求编译器拒绝无效程序,它只需要对规则违规进行“诊断”。诊断可以是致命错误消息或警告。

不幸的是,编译器对不兼容的指针类型的赋值发出警告是很常见的。

void main()

这是错误的;它应该是int main(void)。你的编译器可能会让你侥幸逃脱,它可能不会导致任何明显的问题,但没有正确编写它是没有意义的。 (不是相当那么简单,但已经足够接近了。)

int *p;
float q;
q = 6.6;

没关系。

p = &q;

pint* 类型; &amp;q 的类型为 float*。将一个分配给另一个(没有强制转换)是违反约束。最简单的看待它的方式就是它是非法的。

如果你真的想做这个作业,你可以使用演员表:

p = (int*)&q; /* legal, but ugly */

但很少有充分的理由这样做。 p 是指向int 的指针;它应该指向一个int 对象,除非你有一个非常很好的理由让它指向别的东西。在某些情况下,转换本身可能具有未定义的行为。

printf("*p =  %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q);

%f 格式需要 double 参数(在这种情况下,float 参数被提升为 double,因此 float 可以)。但是*p 的类型是int。使用错误类型的参数调用 printf 会导致程序的行为未定义。

%p 需要void* 类型的参数,而不仅仅是任何指针类型。如果你想打印一个指针值,你应该把它转换成void*

printf("&q = %p\n", (void*)&q);

它可能在没有演员表的情况下工作,但同样,行为是未定义的。

如果您在编译程序时收到任何警告,请不要费心运行它。先修复警告。

关于你标题中的问题,int*float* 类型的指针属于不同类型。一个int* 应该指向一个int 对象; float* 应该指向 float 对象。您的编译器可能会让您混合使用它们,但这样做的结果是实现定义或未定义。 C 语言,尤其是许多 C 编译器,会让你摆脱很多没有多大意义的事情。

它们是不同类型的原因是(尝试)防止或至少检测它们的使用错误。如果你声明一个int* 类型的对象,你是说你打算让它指向一个int 对象(如果它不是一个空指针)。在int* 对象中存储float 对象的地址几乎可以肯定是一个错误。强制类型安全允许尽早检测到此类错误(当您的编译器打印警告时,而不是当您的程序在为重要客户端演示期间崩溃时)。

int*float* 很可能(但不能保证)大小相同并且具有相同的内部表示。但是int* 对象的含义不是“包含虚拟地址的 32(或 64)位的集合”,而是“指向 int 对象的东西”。

【讨论】:

  • 既然你把书扔给他,读*p就违反了严格的别名,所以不管你用这个值做什么,它本身就是UB。
  • p = (int *) &amp; q; 并不总是合法的。 C 2011 (n1570) 6.3.2.3 7 表示如果结果未正确对齐,则行为未定义。它将在典型的当前平台上,因此如果您正在相应地规划您的目标,这是可以的,但如果您正在规划标准 C 或想要了解 C 规范的语义,则不是。
  • @EricPostpischil:正确——只是不清楚在这种情况下“合法”是什么意思(标准从不使用这个词)。我已经更新了我的答案。
  • @KeithThompson int*float* 的大小何时会有所不同?我认为地址将始终是固定大小的,无论数据类型如何。或者是否存在int存储在不同内存中而float存储在不同内存中的任何情况?
  • @RohitO:int*float* 的大小很可能相同——但 C 标准不需要它。另一个例子:有些系统是字寻址的,所以机器地址指向一个完整的字(比如 4 或 8 个字节)——但 char* 由机器地址加上字内的偏移量组成。您不太可能遇到这样的系统——但您也不太可能需要关心int*float* 的相对大小。如果您需要 int* 的大小,只需编写 sizeof (int*)(或者,更可能的是 sizeof some_pointer_object)。
【解决方案2】:

你得到了未定义的行为,因为你将错误的类型传递给printf。当你告诉它期待一个浮点数时,它实际上期待一个double - 但你传递了一个int

因此它会打印错误的信息,因为printf 完全依赖格式字符串来访问您传递给它的参数。

【讨论】:

    【解决方案3】:

    除了teppic所说的,

    考虑一下,

    int a = 5;
    int *p = &a;
    

    在这种情况下,我们向编译器表明p 将指向一个整数。所以众所周知,当我们在运行时做类似 *p 的事情时,没有。将读取等于 int 大小的字节数。

    如果将占用 x 字节数的数据类型的地址分配给声明为保存字节数少于 x 的数据类型地址的指针,则在使用间接运算符时读取错误的字节数.

    【讨论】:

    • 谁说floatint 大?这是允许的,但可能不是。
    • @Steve 是的,我同意并非总是如此。但有些系统就是这样。更大的意思是占用更多的字节。也许我的例子不是最好的,我的观点。我只是想表明我们定义指针的类型是有原因的[就像我们必须在使用间接寻址之前对 void 指针进行类型转换]
    • 我建议大小不是重点。我曾在sizeof (int) == sizeof (double) 的系统上工作过;在这样的系统上,程序的行为同样未定义。如果大小恰好匹配,您可能会认为混合这样的指针类型是可以的。而p 特别需要指向一个int,而不仅仅是任何整数。
    • 实际上,我想指出不匹配指针类型和它指向的数据类型是不安全的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-04
    • 2010-12-17
    • 2013-07-12
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    相关资源
    最近更新 更多