从 scanf 读取 C 样式字符串时出现分段错误

Posted

技术标签:

【中文标题】从 scanf 读取 C 样式字符串时出现分段错误【英文标题】:Segmentation fault when reading C-style string from scanf 【发布时间】:2014-02-27 19:04:01 【问题描述】:

我有以下简单代码:

#include <cstdio>
#include <queue>
#include <iostream>

struct Pacient 
int ill_state;
int ev_num;
bool operator==(const Pacient& other) const 
    return ill_state == other.ill_state && ev_num == other.ev_num;

bool operator<(const Pacient& other) const 
    return (ill_state < other.ill_state) || (ill_state == other.ill_state && ev_num > other.ev_num); // má menšiu prioritu, ak čaká kratšie (vyššie číslo na kartičke pri vstupe do ambulancie

bool operator>(const Pacient& other) const 
    return (ill_state > other.ill_state) || (ill_state == other.ill_state && ev_num < other.ev_num);

;

int main() 
char* ccmd;
std::priority_queue<Pacient> ps;
int ev_num, ill_state;
while (std::scanf("%s", ccmd)) 
    std::string cmd(ccmd);
    if (cmd == "dalsi") 
        if (ps.empty()) 
            std::printf("-1\n");
         else 
            std::printf("%d\n", ps.top().ev_num);
            ps.pop();
        
     else if (cmd == "pacient") 
        std::scanf("%d%d\n", &ev_num, &ill_state);
        Pacient new_ps;
        new_ps.ev_num = ev_num;
        new_ps.ill_state = ill_state;
        ps.push(new_ps);
     else if (cmd == "koniec") 
        break;
    

return 0;

编译并输入一些内容到标准输入后,我有以下段错误:

       Program received signal SIGSEGV, Segmentation fault.
__strlen_sse2_pminub () at ../sysdeps/x86_64/multiarch/strlen-sse2-pminub.S:38
38  ../sysdeps/x86_64/multiarch/strlen-sse2-pminub.S: No such file or directory.

我使用的是 Ubuntu 13.10 64 位。 有人可以解释一下,是什么导致了这个问题?

注意:我使用的是 scanf 而不是 cin,因为我有使用 scanf、printf 而不是 cin、cout 的具体说明(来自学校)。否则我不会使用它。

【问题讨论】:

当您使用“%s”时,scanf 需要 char *。也就是说,使用std::stringoperator&gt;&gt;std::getline @Aashish 是的,我可以,但我对此代码有使用 scanf 和 printf 的具体说明。 @chris 我已经在使用它了。 你的标题完全不符合标准:你不是使用scanf读入std::string,你正试图读入char *是一个 c 风格的字符串. @crashmstr 谢谢,已解决。 【参考方案1】:

声明一些存储空间供scanf写入:

char ccmd[1000];

否则,*ccmd 是一个随机指针,(可能)没有足够的存储空间来写入要写入的字符。请注意,scanf 不会对该空间进行间接处理。

另外,ccmd 已经是一个指针,所以不需要额外的&amp;(地址):

 std::scanf("%s", ccmd)

【讨论】:

防止堆栈溢出:std::scanf("%999s", ccmd) @MaximYegorushkin:已更新为地址(更优雅一些)。 您确定* 允许您指定最大字段宽度吗?此外,%s 的字段宽度不应包含\0 的空格,即sizeof ccmd - 1 @MaximYegorushkin:哦,你说的很对。已收回。 @wallyk 我希望它确实允许这样做(。【参考方案2】:

你不需要使用std::scanf来读取字符串,你可以使用安全的C++:

std::string cmd;
while(std::cin >> cmd) 
    // ...

【讨论】:

我有具体的指令(来自学校)在这段代码中使用 scanf 和 printf。 @Benji 他们要求你使用 C++ 和 C 风格的输入/输出,这很奇怪。 听说是因为这个程序的输入会很大,scanf应该比cin快。 @Benji 哦不,浪费了几毫秒!说真的,他们应该教你正确的惯用 C++ 代码,然后担心性能。 @Benji 请注意scanf 系列函数很容易将堆栈溢出引入您的应用程序,就像scanf("%s") 肯定会这样做,因为您没有指定要读取的最大字符串长度。换句话说,scanf 系列函数更难正确使用。【参考方案3】:

你没有初始化指针ccmd

char* ccmd;

并且没有在你要读取字符串的地方分配内存

while (std::scanf("%s", &ccmd)) 

所以程序有未定义的行为。

完全不清楚为什么您不想将运算符 >> 与流 std::cin 一起使用,而是使用不安全的函数 scanf。

您还需要包含标题&lt;string&gt;。否则,如果您尝试使用其他编译器编译您的程序,您可能会收到错误消息。

正如 @Maxim Yegorushkin 所指出的,即使函数 scanf 的调用也不正确。而不是

std::scanf("%s", &ccmd)

应该有

std::scanf("%s", ccmd)

【讨论】:

请您说得更具体些吗?例如。究竟如何初始化该指针? @Benji 它必须引用分配的内存,您将在其中读取字符串。现在这个指针有一些任意值了。 您将char** 转换为%s 格式,这是不正确的。 但是你可以做sscanf("%ms", &amp;ccmd),它会在使用glibc时为你分配一个缓冲区。这是一个非标准的扩展。

以上是关于从 scanf 读取 C 样式字符串时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

访问填充了从管道读取的数据的结构时出现分段错误

从堆栈读取时出现分段错误

访问共享内存时出现分段错误

从 C 调用汇编函数时出现分段错误错误

在 Python Pandas 中使用 read_parquet 从 AWS S3 读取镶木地板文件时出现分段错误

运行 3 个线程时出现分段错误