使用double类型的sprintf语句出现错误(缓冲区溢出?)

4
我遇到了一个sprintf语句错误。我添加了一个printf命令来帮助调查,似乎我的某个double类型的变量并未被正确理解(printf输出的是一串无意义的数字,而%3.1f应该显示的是一个浮点数)。然而,在printf语句中第一次调用该double类型变量时,它被正确解释了。将名称从120增加到320,段错误确实消失了。但是这个double类型变量仍然没有被正确解释,即仍然输出一串无意义的数字,而不是简单的%3.1f。我无法弄清楚我做错了什么。有什么想法吗?下面是代码的最小测试版本和错误信息。
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <sstream>
    #include <strstream>
    #include <vector>

    using namespace std;


    void Back_Subt_beta()
    {

        int resonances = 4;
        char name[320];
        double rpos[66];
        double rbinmin[66];
        double rbinmax[66];

        ifstream binedgein;
        binedgein.open("binedges.dat");
        if (binedgein.is_open()) {
            cout << "data file opens" << endl;
            }
        for (int vline=1; vline<=4; vline++)
        {
            binedgein >> var1 >> var2 >> var3;
            rpos[vline-1] = var1;
            rbinmin[vline-1] = var2;
            rbinmax[vline-1] = var3;
        }
        binedgein.close();


        for (int m=2; m<=7; m++)
        {
            for (int j=0; j<resonances; j++)
            {
                printf("resonance%0#7.2feV/gammas_%3.1feV_Mcl%i", rpos[j],rpos[j],m);
                sprintf(name,"resonance%0#7.2feV/gammas_%3.1feV_Mcl%i",rpos[j],rpos[j],m);
            }
        }
        exit();
    }

文件 binedges.dat

16.2      16.0      16.5
38.75     38.25     39.25
44.5      43.5      45.5
55.25     54.75     55.75

同时出现了以下错误:

Processing Back_Subt_beta.C...
data file opens

 *** Break *** segmentation violation
resonance0016.20eV/gammas_917241681885348612676436160464141677586357964289319457240620564649334534999701390133785258335880600276911524435084428436805391368574132924760441246552362332456319675531264.0eV_Mcl16(no debugging symbols found)
Using host libthread_db library "/lib/tls/libthread_db.so.1".
Attaching to program: /proc/7689/exe, process 7689
[Thread debugging using libthread_db enabled]
[New Thread -1208284352 (LWP 7689)]
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.
(no debugging symbols found)...done.

0x006dd7a2 in _dl_sysinfo_int80 () from /lib/ld-linux.so.2
#1  0x014d3533 in __waitpid_nocancel () from /lib/tls/libc.so.6
#2  0x0147c869 in do_system () from /lib/tls/libc.so.6
#3  0x00962b8d in system () from /lib/tls/libpthread.so.0
#4  0x00bebc8e in TUnixSystem::Exec () from /usr/local/root/lib/libCore.so
#5  0x00be6dfb in TUnixSystem::StackTrace () from /usr/local/root/lib/libCore.so
#6  0x00be5c53 in TUnixSystem::DispatchSignals () from /usr/local/root/lib/libCore.so
#7  0x00bebf4d in SigHandler () from /usr/local/root/lib/libCore.so
#8  0x00be0590 in sighandler () from /usr/local/root/lib/libCore.so
#9  <signal handler called>
#10 0x014b1d0a in strcmp () from /lib/tls/libc.so.6
#11 0x003033be in G__searchvariable () from /usr/local/root/lib/libCint.so
#12 0x002f9514 in G__getvariable () from /usr/local/root/lib/libCint.so
#13 0x0021de97 in G__getitem () from /usr/local/root/lib/libCint.so
#14 0xbfeb89dc in ?? ()
#15 0x0021c633 in G__getexpr () from /usr/local/root/lib/libCint.so
#16 0x00000048 in ?? ()
#17 0x002e9bc8 in G__letvariable () from /usr/local/root/lib/libCint.so
#18 0xbfeb987c in ?? ()
Root > Function Back_Subt_beta() busy flag cleared

2
你确定问题不是访问无效的数组位置吗? - user195488
我不确定你的意思是什么。但是我使用cout语句确定了segfault发生在那个sprintf行之前,而不是在尝试获取sprintf命令中指定的文件名后发生。所以我在之前插入了printf语句来查看sprintf的运行情况。 - neverskipbreakfast
可能是重复的问题:Sprintf Segmentation Fault - user195488
字符串应该不超过40个字符。我以为这没问题...然而:将大小增加到name[320]确实可以防止段错误。但是双精度仍然无法正确解释,即它仍然输出一串无意义的数字,而简单的%3.1f应该是正确的。 - neverskipbreakfast
1
你真的应该使用snprintf,这样它只会复制你需要的内容。可能是字符数组已经填满了。 - user195488
3个回答

2

我认为你的问题在于char数组name只有120个字节,但是你使用sprintf语句溢出了这个缓冲区。将char缓冲区的大小从120个字节增加到更大的值。更好的方法是使用snprintf()而不是sprintf()


将其增加到320,段错误停止了,但sprintf的结果仍然是乱码。我从未使用过snprintf,但现在我会去研究一下... - neverskipbreakfast
我本想使用snprintf,但不幸的是,在我的系统上找不到适当的头文件(而且我不是管理员)。我能够通过将sprintf拆分为两个不同的函数,然后再将它们组合起来,暂时解决了这个问题。虽然很丑陋,但这是我立即可用的唯一有效解决方案。我会看看是否能找到那些头文件以备将来之需。 - neverskipbreakfast
@neverskipbreakfast:你尝试过 #include <cstdio> 吗? - user195488

1
我想知道那个数字是什么,所以我做了这个:
$ python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09) 
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = 917241681885348612676436160464141677586357964289319457240620564649334534999701390133785258335880600276911524435084428436805391368574132924760441246552362332456319675531264.0
>>> import struct
>>> struct.pack('<d', x)
'resonanc'

所以最初的错误肯定是由于字符串覆写缓冲区引起的。如果您得到了不同的垃圾值,请尝试查看实际的比特,也许您会找出原因。


有趣!所以,即使我将char字符串的长度增加到name[500],当我cout name时仍然得到相同的垃圾值,但没有segfault。可能500不够用吗?我是否没有理解sprintf的限制?也许有趣的是,我在字符串中得到的最后一个变量也不正确。我应该得到类似resonance0016.00eV/gammas_16.0eV_Mcl2的东西,但实际上我得到的是resonance0016.00eV/gammas_(garbage)eV_Mcl16。 - neverskipbreakfast

0

你正在溢出仅有 120 个字符的 name 字符数组。更好的方法是使用 std::string namestr,然后在填充 name 时使用 str.c_str()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接