在C语言中解析整数的艺术:告别atoi,拥抱严格与安全

本文深入探讨了C语言标准库中整数解析函数(如atoi、strtol)的缺陷与潜在风险,并详细介绍了cURL项目为实现更严格、更安全的解析而自研的解决方案curlx_str_number,强调了在现代编程中严格解析数据的重要性。

解析C语言中的整数

在标准libc API集中,提供了多个将ASCII数字转换为整数的函数。它们使用方便,但也容易出错,并且对输入内容相当宽容,会默默“吞下”很多不符合预期的数据。

atoi

atoi() 可能是最常见和最基本的函数。它将字符串转换为有符号整数。它的同伴 atol() 则转换为 long 类型。

这些函数存在的问题包括:它们在出错时返回0而不是错误标识;没有对下溢或溢出进行检查;并且在 atol() 的情况下,long 在不同平台上有不同的大小。因此,它们都无法可靠地用于64位数字。它们也不告知数字在何处结束。

使用这些函数会使你的解析器无法检测和处理错误或异常输入。当我们避免使用这些函数时,我们可以编写更好、更严格的解析器。

strtol

这个函数及其兄弟函数 strtoul()strtoll() 等更强大。它们具有溢出检测功能,并且可以检测错误——例如,如果根本没有数字可解析。

然而,这些函数同样过于乐意地接受前导空白,并允许在数字前使用 +-。这些函数的 long 版本存在的问题是,long 并非在所有平台上都是64位的;而 long long 版本的问题则是,它并非在所有平台上都可用。

这些函数的溢出和下溢检测相当古怪,涉及 errno,并且迫使我们每次调用时都要多写几行条件代码,以确保能捕获这些情况。

curl代码实践

我认为,cURL项目以及几乎整个世界多年来已经认识到,在解析协议和数据时,严格通常比宽容并试图接受很多东西、猜测其可能含义要好。

作为这一点的直接结果,我们确保curl严格按照数据的预期形式来解析和解释数据,一旦检测到数据错误就立即报错。出于安全和稳定功能的考虑,不接受语法错误的数据。

这也意味着所有的数字解析都必须是精确的,正确处理溢出和最大允许值,并且必须检测错误。它始终支持高达64位的数字。

strparse

我之前曾写过博客,介绍我们在curl中如何实现自己的一套解析函数,其中也包括数字解析。

curlx_str_number() 是我们创建的函数中最常用的一个。它解析一个字符串并将值存储在一个64位变量中(在curl代码中,这个变量总是存在且总是64位的)。它还有一个最大值参数,如果超过该值则返回错误。当然,它也会在溢出等情况下报错。

我们的这个函数不允许任何前导空白,当然也不允许前缀的加号或减号。如果需要允许这些,周围的解析代码需要明确地允许它们。

curlx_str_number 函数可能比它替代的函数稍慢一些,但我认为差异并不大,而其带来的便利性和增加的严格性非常受欢迎。我们通过这种方式编写了更好的代码和解析器。更安全。

历史

截至昨天,即2025年11月12日,所有这些弱函数调用都已从curl源代码中清除。图中2025年初的下降是我们摆脱所有 strtol() 变体的时候。昨天,我们终于摆脱了最后的 atoi() 调用。

curlx

上述函数使用了 curlx 前缀。我们在curl代码中使用这个前缀来表示那些存在于libcurl源代码中,但也可被curl工具使用的函数——在库和命令行工具之间共享相同的代码,而无需通过libcurl API提供。

这是我们为减少代码重复并在库和命令行工具之间共享代码而采取的做法。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计