首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么printf()允许通过指针传递这个双值?

为什么printf()允许通过指针传递这个双值?
EN

Stack Overflow用户
提问于 2015-09-07 20:26:18
回答 2查看 555关注 0票数 10

一对printf()调试语句显示,当在接收端取消引用时,指向双值的指针将以不同的值出现--但仅在Microsoft (Version9.0)下出现。这些步骤非常简单:

代码语言:javascript
复制
    double rho=0;       /* distance from the Earth */
    /* ... */
    for (pass = 0; pass < 2; pass++) {
        /* ... */
        rho = sqrt(rsn*rsn+rp*rp-2*rsn*rp*cpsi*cos(ll));
        printf("\nrho from sqrt(): %f\n", rho);
        /* ... */
    }
    /* ... */
    cir_sky (np, lpd, psi, rp, &rho, lam, bet, lsn, rsn, op);
    /* ... */
}
/* ... */
static void
cir_sky (
/* ... */
double *rho,        /* dist from earth: in as geo, back as geo or topo */
/* ... */)
{
    /* ... */
    printf("\nDEBUG1: *rho=%f\n", *rho);

整个C文件都在这里:

https://github.com/brandon-rhodes/pyephem/blob/9cd81a8a7624b447429b6fd8fe9ee0d324991c3f/libastro-3.7.7/circum.c#L366

我原以为第一个printf()中显示的值将与第二个显示的值相同,因为传递指向double的指针不应该导致不同的值。事实上,在GCC的领导下,他们的价值总是一样的。在Visual 32位编译下,它们总是相同的.但是当这段代码是在64位架构下用Visual编译时,这两个双值是不同的!

https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.18/job/4xu7abnl9vx3n770#L573

代码语言:javascript
复制
rho from sqrt(): 0.029624

DEBUG1: *rho=0.000171

这太令人不安了。我想知道:计算rho的位置和指针最终传递的位置之间的代码是否通过糟糕的指针算法破坏了值?因此,我添加了最后一个printf(),就在cir_sky()调用的前面,看看这个值是否已经被那个点改变了,或者它是否在调用过程中被改变了:

代码语言:javascript
复制
    printf("\nrho about to be sent: %f\n", rho);
    cir_sky (np, lpd, psi, rp, &rho, lam, bet, lsn, rsn, op);

下面是整个文件上下文中的一行:

https://github.com/brandon-rhodes/pyephem/blob/28ba4bee9ec84f58cfffabeda87cc01e972c86f6/libastro-3.7.7/circum.c#L382

猜猜发生了什么?

添加printf() 的修复了错误--传递给 rho 的指针现在可以被取消引用到正确的值!

从这里可以看到:

https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.19/job/s3nh90sk88cpn2ee#L567

代码语言:javascript
复制
rho from sqrt(): 0.029624

rho about to be sent: 0.029624

DEBUG1: *rho=0.029624

我很困惑。

我在这里遇到了什么C标准的边缘情况?为什么仅在此函数的顶层范围中使用值rho会迫使Microsoft编译器正确地保留其值?rho是在块内部设置和使用的问题,而Visual由于我从未完全内化过的C标准的怪癖,没有屈尊将它的值保存在该块之外吗?

您可以在上面的AppVeyor链接上看到整个构建输出。如果问题可能是如何调用Visual或编译选项,则此C文件的特定编译步骤是:

代码语言:javascript
复制
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\Bin\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -Ilibastro-3.7.7 -IC:\Python27-x64\include -IC:\Python27-x64\PC /Tclibastro-3.7.7\circum.c /Fobuild\temp.win-amd64-2.7\Release\libastro-3.7.7\circum.obj
circum.c
libastro-3.7.7\circum.c(126) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data 
libastro-3.7.7\circum.c(127) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data
libastro-3.7.7\circum.c(139) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data 
libastro-3.7.7\circum.c(140) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data 
libastro-3.7.7\circum.c(295) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data 
libastro-3.7.7\circum.c(296) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data
libastro-3.7.7\circum.c(729) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data 
libastro-3.7.7\circum.c(730) : warning C4244: '=' : conversion from 'double' to 'float', possible loss of data

据我所见,所有这些警告都不是针对这个特定谜题所涉及的代码的--即使它们是这样的,它们也意味着浮点值可能变得不那么精确(从十进制精度的大约15位数到7位数),而不是它可能会完全改变。

在这里,两个编译和测试运行的输出是否成功,其中第一个运行失败,第二个输出由于printf()而成功:

https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.18/job/4xu7abnl9vx3n770

https://ci.appveyor.com/project/brandon-rhodes/pyephem/build/1.0.19/job/s3nh90sk88cpn2ee

根据AppVeyor的说法,两者的架构是完全相同的:

代码语言:javascript
复制
Environment: PYTHON=C:\Python27-x64, PYTHON_VERSION=2.7.x, PYTHON_ARCH=64, WINDOWS_SDK_VERSION=v7.0
EN

回答 2

Stack Overflow用户

发布于 2016-02-08 19:38:29

我快速查看了这段代码,没有发现任何明显的错误或问题。但是,当printf解决了这个问题时,这就意味着存在一些不确定性.让我们分析可能的原因:

  1. 并发性-数据竞争:最常见的,但你说它是单线程的。
  2. 未初始化的内存:rho是在这里初始化的,但是,也许其他地方没有初始化,而且它正在搞砸事情。我会在Linux和AdressSanitizer以及其他的杀菌剂(也可以在clang和gcc的视窗上使用)上运行val研( Linux),看看他们是否想出了什么东西。
  3. 野生指针和其他不受限制的访问:我们在这里看到的代码中没有任何内容,但是它正在调用其他函数。再来一次,用缬草和消毒剂。
  4. 如果前面的步骤很短,那么下一个最有可能的候选是MSVC缺陷。MSVC因在一些复杂的代码中搞砸事情而臭名昭著,这有点复杂。很多时候,我重新整理代码只是为了让MSVC开心。有时关闭优化是有帮助的,但有时却没有帮助。有时有一个更新/修补程序可以帮助,有时没有。我建议查看调试器中的反汇编程序,但您说您无法访问Windows机器。这里最好的办法是简化代码--使函数变得更小,减少参数的数量。
  5. 还有其他可能的原因。例如,可能是由于某种原因--可能是在与Python运行时交互时--堆栈出现了混乱。尝试将其构建和运行为“常规”C代码,而不是Python扩展。消除对其他函数的调用(如果它扰乱了计算,没关系,您只是想找出问题)。

无论如何,我建议您将手放在Windows计算机上并对其进行调试。根据我的经验,这是了解这些问题的最好方法。

票数 1
EN

Stack Overflow用户

发布于 2015-09-07 21:05:23

这是(错误的)优化的结果吗?

关闭任何优化(调试?)看看你能不能得到同样的效果。

当然,如果您发现它是优化器,那么您就在crick上,并且只能做一些事情来欺骗它,例如什么都不做的sprintf。

此外,您还可以打印出指针("%16x“、(long)和rho),而不是我认为它是不正确的,而是作为一个sanity子句,以防我们缺少和。此外,大多数随机比特双倍的结果通常都在E+/-317范围内,因此0.000171的结果有点太合理,不能完全怀疑。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32445624

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档