五藏 2024-12-30 13:45 采纳率: 100%
浏览 20
已结题

在C++中 strcpy()函数是安全的吗?

学习C/C++时知道strcpy()函数是不安全的,当目标字符数组长度不足时会出现缓冲区溢出现象,但我的验证结果似乎不是这样的。

程序代码如下:

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char s1[] = "Hello,world!";
    char s2[] = "Hello,everyone!!!";

    cout << "==> Before strcpy():" << endl;
    cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
        << "s2: " << s2 << ",\t" << strlen(s2) << endl;
    cout << endl;

    cout << "==> Run strcpy(s1,s2) ..." << endl;
    strcpy(s1,s2);
    cout << endl;

    cout << "==> After strcpy():" << endl;
    cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
        << "s2: " << s2 << ",\t" << strlen(s2) << endl;
}

在Linux GCC 4.8.5 环境中:
环境信息:

img

编译运行结果如下:

img

在Windows 10+VSCode+MinGW64环境下(g++版本 14.2.0)编译运行会是同样结果。

根据上面结果,字符数组s1被自动扩充了,这是什么原因?

  • 写回答

2条回答 默认 最新

  • 道友老李 JWE233286一种基于机器视觉的水表指针读数识别及修正的方法 专利发明者 2024-12-30 13:54
    关注
    让【道友老李】来帮你解答,本回答参考通义千问大模型编写,并整理提供,如果还有疑问可以点击头像关注私信或评论。
    如果答案让您满意,请采纳、关注,非常感谢!
    你的观察结果可能是因为编译器的优化或运行环境的一些特定行为导致的,但实际上`strcpy()`函数本身并不检查目标缓冲区的大小,因此在目标字符数组长度不足时确实会导致缓冲区溢出。为了更好地解释这一点,让我们一步步来分析你的代码,并提供一个更加明确的例子来展示`strcpy()`的安全问题。

    代码分析

    你提供的代码如下:

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main() {
        char s1[] = "Hello,world!";
        char s2[] = "Hello,everyone!!!";
        cout << "===> Before strcpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
        cout << endl;
        cout << "===> Run strcpy(s1,s2) ..." << endl;
        strcpy(s1, s2);
        cout << endl;
        cout << "===> After strcpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
    }
    

    问题分析

    1. 字符串初始化

      • s1 是一个固定大小的字符数组,其大小为 "Hello,world!" 的长度(包括空终止符)。
      • s2 是一个更大的字符串。
    2. 使用 strcpy()

      • strcpy(s1, s2)s2 的内容复制到 s1 中,但 s1 的大小不足以容纳 s2 的内容。
    3. 潜在的缓冲区溢出

      • 由于 s1 的大小不足,strcpy() 会写入超出 s1 边界的内存,导致未定义行为。这可能会覆盖相邻的内存区域,甚至可能导致程序崩溃。

    为什么没有看到预期的缓冲区溢出?

    • 编译器优化:某些编译器和运行环境可能会对栈上的小数组进行一些优化,使得看起来像是 s1 被自动扩充了。但这并不是 strcpy() 的标准行为。
    • 栈布局:在某些情况下,栈上分配的内存可能有足够的空间来容纳 s2,即使 s1 的声明大小不够。但这仍然是未定义行为,不应该依赖于此。

    示例代码展示缓冲区溢出

    为了更清楚地展示 strcpy() 的不安全性,我们可以编写一个更简单的示例,并使用 valgrind 工具来检测内存错误。

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main() {
        char s1[10];  // 明确指定 s1 的大小
        char s2[] = "This is a long string that will overflow";
    
        cout << "===> Before strcpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
        cout << endl;
        cout << "===> Run strcpy(s1,s2) ..." << endl;
        strcpy(s1, s2);  // 这里会发生缓冲区溢出
        cout << endl;
        cout << "===> After strcpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
    
        return 0;
    }
    

    使用 Valgrind 检测内存错误

    在 Linux 系统上,可以使用 valgrind 来检测内存错误。首先安装 valgrind

    sudo apt-get install valgrind
    

    然后编译并运行你的程序:

    g++ -o test test.cpp
    valgrind --leak-check=full ./test
    

    valgrind 会输出内存错误信息,显示哪里发生了缓冲区溢出。

    安全替代方案

    为了避免缓冲区溢出,可以使用 strncpy() 或 C++ 标准库中的 std::string

    使用 strncpy()

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    int main() {
        char s1[10];
        char s2[] = "This is a long string that will overflow";
    
        cout << "===> Before strncpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
        cout << endl;
        cout << "===> Run strncpy(s1,s2, sizeof(s1)-1) ..." << endl;
        strncpy(s1, s2, sizeof(s1) - 1);
        s1[sizeof(s1) - 1] = '\0';  // 确保字符串以空终止符结尾
        cout << endl;
        cout << "===> After strncpy():" << endl;
        cout << "s1: " << s1 << ",\t" << strlen(s1) << endl
             << "s2: " << s2 << ",\t" << strlen(s2) << endl;
    
        return 0;
    }
    

    使用 std::string

    #include <iostream>
    #include <string>
    using namespace std;
    
    int main() {
        string s1 = "Hello,world!";
        string s2 = "Hello,everyone!!!";
    
        cout << "===> Before assignment:" << endl;
        cout << "s1: " << s1 << ",\t" << s1.size() << endl
             << "s2: " << s2 << ",\t" << s2.size() << endl;
        cout << endl;
        cout << "===> Run s1 = s2 ..." << endl;
        s1 = s2;
        cout << endl;
        cout << "===> After assignment:" << endl;
        cout << "s1: " << s1 << ",\t" << s1.size() << endl
             << "s2: " << s2 << ",\t" << s2.size() << endl;
    
        return 0;
    }
    

    通过这些方法,你可以避免缓冲区溢出的风险,并确保代码的安全性和可靠性。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 1月7日
  • 已采纳回答 12月30日
  • 创建了问题 12月30日