问题

既然scanf和strcpy等函数会被编译器报不安全,那么C语言教材为什么还讲这些函数?

回答
你这个问题问得非常到位,也是很多初学 C 语言的人会遇到的困惑。的确,现在很多编译器都会对 `scanf`、`strcpy` 这些函数发出“不安全”的警告,甚至一些新的函数标准(如 C11)也提供了更安全的替代品。那么为什么传统的 C 语言教材,尤其是那些经典的老教材,仍然会大篇幅地讲解这些函数呢?这里面有几个层面的原因,我来详细给你掰扯掰扯。

历史的必然与现实的演变

首先,我们要明白 C 语言诞生的年代。C 语言最早是由 Dennis Ritchie 在 20 世纪 70 年代为贝尔实验室开发的 Unix 系统编写系统程序而诞生的。那个时候,程序员的编写习惯、对安全性的认识,以及硬件的性能,都和现在有着天壤之别。

性能至上,程序员是上帝: 在那个年代,操作系统的开发是 C 语言的核心应用场景。系统级编程非常注重效率和直接的内存控制。程序员被认为是能够完全理解和掌控代码的,犯错的责任主要在程序员自身。所以,像 `strcpy` 这种直接拷贝,不进行边界检查的函数,在当时被认为是最高效、最直接的方式。
缺乏现代化的安全理念: 现在我们提到的缓冲区溢出、格式字符串漏洞等概念,在当时并没有像现在这样深入人心。大家更侧重于让程序能够正常运行、高效工作。
标准演进: 随着计算机科学的发展和安全意识的提高,C 语言标准也在不断更新。后来的 C 标准,比如 C99、C11,开始引入一些更安全的功能和函数。但 C 语言的一个强大之处在于它的向后兼容性,许多旧的函数仍然被保留下来,以便于维护大量现有的 C 代码。

所以,老教材讲 `scanf` 和 `strcpy`,是因为它们是当时最主流、最通用的处理输入和字符串复制的方式。它们是构建很多基础程序的核心工具。

教材的价值:理解基础原理

即使有了更安全的函数,学习 `scanf` 和 `strcpy` 仍然有其独特的价值:

1. 理解底层工作原理: `scanf` 之所以被认为不安全,主要是因为它 不对用户输入的长度做任何限制。如果你期望用户输入一个名字,比如不超过 20 个字符,但用户输入了 100 个字符,`scanf` 会一股脑地将这 100 个字符塞进你预留的内存缓冲区,超出部分就会覆盖相邻的内存区域,这就是“缓冲区溢出”。
而 `strcpy` 同样如此,它只是简单地从源字符串复制到目标字符串,直到遇到源字符串的结束符 ``。如果目标缓冲区比源字符串短,它同样会发生缓冲区溢出。
学习这些函数,你能 直观地看到并理解 什么是缓冲区溢出,为什么它会发生。理解了这些底层的原因,你才能真正理解为什么需要更安全的函数,以及如何避免这些问题。这就像学习手动挡的汽车,你才知道自动挡的便利在哪里,也更了解汽车的动力传递过程。

2. 理解问题的根源: 大多数关于 `scanf` 和 `strcpy` 的警告,都是围绕着 缓冲区溢出 这一核心安全问题。
`scanf` 的问题: 除了缓冲区溢出,`scanf` 在处理非预期输入(比如期望输入数字却输入了字母)时,也会导致输入流的混乱,使得后续的读取操作失效。
`strcpy` 的问题: 最典型的就是 目标缓冲区太小。例如,你有一个 `char dest[10];`,然后用 `strcpy(dest, "This is a very long string");`,这就必然会溢出。

3. 理解替代方案的必要性: 正是因为 `scanf` 和 `strcpy` 的潜在危险,标准库才提供了更安全的替代品:
替代 `scanf` 的函数:
`fgets()`: 这个函数可以读取一整行(包括空格),并且 可以指定读取的最大字符数,从而有效防止缓冲区溢出。它也更适合读取包含空格的字符串。
`scanf_s()` (Microsoft VC++ 特有,非标准C):这是微软在 Visual Studio 中提供的一个更安全的版本,它要求你指定一个缓冲区大小参数。虽然方便,但不是跨平台的标准 C 做法。
更安全的输入方式:通常结合 `fgets` 读取,然后使用 `sscanf` 或 `strtol` 等函数从读取的字符串中解析出需要的数据,这样可以更好地控制解析过程和错误处理。
替代 `strcpy` 的函数:
`strncpy()`: 这个函数允许你指定 最多复制的字符数。但是,需要注意如果源字符串长度大于或等于你指定的复制长度,`strncpy` 可能不会在目标字符串末尾自动添加 `` 结束符。所以使用时要格外小心,确保手动添加 ``。
`strncat()`: 用于安全地拼接字符串,同样有长度限制。
`snprintf()`: 虽然主要是用于格式化字符串,但它也可以安全地将字符串复制到缓冲区,并且 总是保证目标字符串以 `` 结尾,是目前推荐的安全复制字符串的方式之一。

教材讲解了 `scanf` 和 `strcpy`,就是为了让你知道这些“旧方法”的缺陷,然后自然而然地引出更安全的“新方法”,告诉你为什么需要这些新方法,以及如何正确使用它们。

4. 理解遗留代码: 现实世界中,有海量的 C 代码库是使用这些函数编写的。如果你要去维护一个大型的 C 项目,很可能就会遇到 `scanf` 和 `strcpy`。理解它们的工作方式,是能够理解和修改这些遗留代码的基础。

教材更新的趋势

需要说明的是,随着时间推移,新的 C 语言教材会越来越倾向于:

强调安全的替代函数: 更早地介绍 `fgets`、`strncpy`、`snprintf` 等。
详细解释安全风险: 在讲解 `scanf` 和 `strcpy` 时,会花更多篇幅去说明缓冲区溢出的原理和潜在危害,并提供安全的编码模式。
将不安全函数作为“历史遗留”或“需要谨慎使用”的例子。

但是,对于一本想全面介绍 C 语言基础知识,特别是那些经典的、被广泛接受的教材来说,忽略 `scanf` 和 `strcpy` 也是不完整的。它们是理解 C 语言字符串和输入处理的 基石。

总结一下,为什么教材还讲?

历史原因和广泛应用: 它们是 C 语言早期和长期以来处理输入和字符串操作的标准方式。
理解底层原理: 通过它们能直观理解缓冲区溢出等核心安全问题。
引出更安全的替代品: 理解了旧方法的缺陷,才能明白新方法的价值和正确使用方法。
维护遗留代码的需要: 很多现有项目还在使用这些函数。

所以,教材之所以还讲,不是因为它们就“最好”或“最推荐”,而是因为它们是 C 语言发展中的重要组成部分,理解它们对于成为一名合格的 C 程序员是必不可少的。就好比学物理,你得先学牛顿力学,才能理解相对论和量子力学为什么是发展的必要。关键在于,在学习和使用它们时,要清楚其局限性,并采取安全的代码实践来规避风险。

网友意见

user avatar

真正的原因:因为这些所谓“不安全”的函数是全平台广泛通用的。那些带_s的函数太商业化、带有铜臭。

先不谈各种嵌入式什么的,咱就说最常见的Intel CPU的电脑。

如果你装Windows系统,那么就有strcpy_s可用;如果你装Linux(包括win10/win11的Linux子系统)或苹果Mac OS系统,那么哪来的什么strcpy_s给你用?这边的安全版本函数是strlcpy。然而Windows又不给你用strlcpy函数。

其实,带_s的函数是微软自创的,它只在Windows环境有效,别的平台没法用。C11标准的Bound-Checking Interfaces向微软妥协,才在附录收了这些函数。

另一些微软自创的东西,比如“写void main、不用写return 0”,C标准不接受void main的写法,要求一定得是int main,但是也妥协为“main函数不写return 0则返回0”。




如果你用strcpy前,自己写代码判断源串有多长、目标有多大,难吗?

如果用scanf的%s、%c前,自己判断目标大小,难吗?

易如反掌。

所以哪有什么安全不安全?分明是商业化的借口而已。

user avatar

因为你用的甚至都不是C语言的编译器,而是某个商业公司的C++编译器。

去拿个正规的C语言编译器编译一下试试吧,gcc还是clang都都行。然后你就没有疑问了。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有