首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

atoi函数的实现二: 测试各实现的正确性

  • 25-03-02 17:21
  • 3467
  • 10385
blog.csdn.net

相关文章:

  • atoi函数的实现

linux内核的atoi测试

v_JULY_v君的问题非常好(请见文章的评论)! 每次都让我思考. 现将linux内核的atoi测试代码贴出来, 为了区别了C标准库的atoi函数, 我把测试的函数名改为matoi:

  1. #include
  2. #include
  3. #include
  4. /*http://lxr.free-electrons.com/source/lib/kstrtox.h#L4*/
  5. #define KSTRTOX_OVERFLOW (1U << 31)
  6. const char *_parse_integer_fixup_radix(const char *s, unsigned int *base);
  7. unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res);
  8. /*http://lxr.free-electrons.com/source/arch/powerpc/boot/types.h#L12*/
  9. typedef int s32;
  10. typedef unsigned int u32;
  11. typedef unsigned long long u64;
  12. /*http://lxr.free-electrons.com/source/drivers/media/pci/ngene/ngene-dvb.c#L127*/
  13. static u32 overflow;
  14. /*http://lxr.free-electrons.com/source/include/linux/kernel.h#L29*/
  15. #define ULLONG_MAX (~0ULL)
  16. #define unlikely(cond) (cond)
  17. /*http://lxr.free-electrons.com/source/lib/kstrtox.c#L23*/
  18. const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
  19. {
  20. if (*base == 0) {
  21. if (s[0] == '0') {
  22. if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
  23. *base = 16;
  24. else
  25. *base = 8;
  26. } else
  27. *base = 10;
  28. }
  29. if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
  30. s += 2;
  31. return s;
  32. }
  33. /*http://lxr.free-electrons.com/source/lib/kstrtox.c#L47*/
  34. /*
  35. * Convert non-negative integer string representation in explicitly given radix
  36. * to an integer.
  37. * Return number of characters consumed maybe or-ed with overflow bit.
  38. * If overflow occurs, result integer (incorrect) is still returned.
  39. *
  40. * Don't you dare use this function.
  41. */
  42. unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p)
  43. {
  44. unsigned long long res;
  45. unsigned int rv;
  46. int overflow;
  47. res = 0;
  48. rv = 0;
  49. overflow = 0;
  50. while (*s) {
  51. unsigned int val;
  52. if ('0' <= *s && *s <= '9')
  53. val = *s - '0';
  54. else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
  55. val = _tolower(*s) - 'a' + 10;
  56. else
  57. break;
  58. if (val >= base)
  59. break;
  60. /*
  61. * Check for overflow only if we are within range of
  62. * it in the max base we support (16)
  63. */
  64. if (unlikely(res & (~0ull << 60))) {
  65. if (res > ULLONG_MAX - val/base)
  66. overflow = 1;
  67. }
  68. res = res * base + val;
  69. rv++;
  70. s++;
  71. }
  72. *p = res;
  73. if (overflow)
  74. rv |= KSTRTOX_OVERFLOW;
  75. return rv;
  76. }
  77. /*http://lxr.free-electrons.com/source/lib/vsprintf.c#L44*/
  78. /**
  79. * simple_strtoull - convert a string to an unsigned long long
  80. * @cp: The start of the string
  81. * @endp: A pointer to the end of the parsed string will be placed here
  82. * @base: The number base to use
  83. *
  84. * This function is obsolete. Please use kstrtoull instead.
  85. */
  86. unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
  87. {
  88. unsigned long long result;
  89. unsigned int rv;
  90. cp = _parse_integer_fixup_radix(cp, &base);
  91. rv = _parse_integer(cp, base, &result);
  92. /* FIXME */
  93. cp += (rv & ~KSTRTOX_OVERFLOW);
  94. if (endp)
  95. *endp = (char *)cp;
  96. return result;
  97. }
  98. /*http://lxr.free-electrons.com/source/lib/vsprintf.c#L83*/
  99. /**
  100. * simple_strtoul - convert a string to an unsigned long
  101. * @cp: The start of the string
  102. * @endp: A pointer to the end of the parsed string will be placed here
  103. * @base: The number base to use
  104. *
  105. * This function is obsolete. Please use kstrtoul instead.
  106. */
  107. unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
  108. {
  109. return simple_strtoull(cp, endp, base);
  110. }
  111. /*http://lxr.free-electrons.com/source/drivers/staging/tidspbridge/rmgr/dbdcd.c#L950*/
  112. /*
  113. * ======== atoi ========
  114. * Purpose:
  115. * This function converts strings in decimal or hex format to integers.
  116. */
  117. static s32 matoi(const char *psz_buf)
  118. {
  119. char *pch = psz_buf;
  120. s32 base = 0;
  121. while (isspace(*pch))
  122. pch++;
  123. if (*pch == '-' || *pch == '+') {
  124. base = 10;
  125. pch++;
  126. } else if (*pch && tolower(pch[strlen(pch) - 1]) == 'h') {
  127. base = 16;
  128. }
  129. return simple_strtoul(pch, NULL, base);
  130. }
  131. void test(const char* str) {
  132. printf("%s : %d\n", str, matoi(str));
  133. }
  134. int main() {
  135. test("2147483647");
  136. test("2147483648");
  137. test("-2147483648");
  138. test("-2147483649");
  139. test("10522545459");
  140. test("-10522545459");
  141. return 0;
  142. }

修改的地方在第75行, 原来的代码为:

if (res > div_u64(ULLONG_MAX - val, base))

而div_u64调用的div_u64_rem函数中包含汇编代码编译不过(原因尚未可知, 有待进一步研究), 所以我把这段程序去掉了.

程序的输出结果(很显然, 对于溢出的情况, 程序没有处理):

2147483647 : 2147483647
2147483648 : -2147483648
10522545459 : 1932610867
-2147483648 : -2147483648
-2147483649 : -2147483647
-10522545459 : 1932610867

Nut/OS的atoi测试

以下是测试代码(在ubuntu 10.4.1, gcc 4.4.3上编译通过, 为了区别于C标准库的函数, 函数名strtol更改为mstrtol, atoi更改为matoi2):

  1. #include
  2. #include
  3. #include
  4. #include
  5. #define CONST const
  6. long mstrtol(CONST char *nptr, char **endptr, int base)
  7. {
  8. register CONST char *s;
  9. register long acc, cutoff;
  10. register int c;
  11. register int neg, any, cutlim;
  12. /*
  13. * Skip white space and pick up leading +/- sign if any.
  14. * If base is 0, allow 0x for hex and 0 for octal, else
  15. * assume decimal; if base is already 16, allow 0x.
  16. */
  17. s = nptr;
  18. do {
  19. c = (unsigned char) *s++;
  20. } while (isspace(c));
  21. if (c == '-') {
  22. neg = 1;
  23. c = *s++;
  24. } else {
  25. neg = 0;
  26. if (c == '+')
  27. c = *s++;
  28. }
  29. if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) {
  30. c = s[1];
  31. s += 2;
  32. base = 16;
  33. }
  34. if (base == 0)
  35. base = c == '0' ? 8 : 10;
  36. /*
  37. * Compute the cutoff value between legal numbers and illegal
  38. * numbers. That is the largest legal value, divided by the
  39. * base. An input number that is greater than this value, if
  40. * followed by a legal input character, is too big. One that
  41. * is equal to this value may be valid or not; the limit
  42. * between valid and invalid numbers is then based on the last
  43. * digit. For instance, if the range for longs is
  44. * [-2147483648..2147483647] and the input base is 10,
  45. * cutoff will be set to 214748364 and cutlim to either
  46. * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
  47. * a value > 214748364, or equal but the next digit is > 7 (or 8),
  48. * the number is too big, and we will return a range error.
  49. *
  50. * Set any if any `digits' consumed; make it negative to indicate
  51. * overflow.
  52. */
  53. cutoff = neg ? LONG_MIN : LONG_MAX;
  54. cutlim = cutoff % base;
  55. cutoff /= base;
  56. if (neg) {
  57. if (cutlim > 0) {
  58. cutlim -= base;
  59. cutoff += 1;
  60. }
  61. cutlim = -cutlim;
  62. }
  63. for (acc = 0, any = 0;; c = (unsigned char) *s++) {
  64. if (isdigit(c))
  65. c -= '0';
  66. else if (isalpha(c))
  67. c -= isupper(c) ? 'A' - 10 : 'a' - 10;
  68. else
  69. break;
  70. if (c >= base)
  71. break;
  72. if (any < 0)
  73. continue;
  74. if (neg) {
  75. if ((acc < cutoff || acc == cutoff) && c > cutlim) {
  76. any = -1;
  77. acc = LONG_MIN;
  78. errno = ERANGE;
  79. } else {
  80. any = 1;
  81. acc *= base;
  82. acc -= c;
  83. }
  84. } else {
  85. if ((acc > cutoff || acc == cutoff) && c > cutlim) {
  86. any = -1;
  87. acc = LONG_MAX;
  88. errno = ERANGE;
  89. } else {
  90. any = 1;
  91. acc *= base;
  92. acc += c;
  93. }
  94. }
  95. }
  96. if (endptr != 0)
  97. *endptr = (char *) (any ? s - 1 : nptr);
  98. return (acc);
  99. }
  100. int matoi2(CONST char *str)
  101. {
  102. return ((int) mstrtol(str, (char **) NULL, 10));
  103. }
  104. int mgetline(char* buf, size_t n) {
  105. size_t idx = 0;
  106. int c;
  107. while (--n > 0 && (c = getchar()) != EOF && c != '\n') {
  108. buf[idx++] = c;
  109. }
  110. buf[idx] = '\0';
  111. return idx;
  112. }
  113. #define MAX_LINE 200
  114. int main() {
  115. char buf[MAX_LINE];
  116. while (mgetline(buf, MAX_LINE) >= 0) {
  117. if (strcmp(buf, "quit") == 0) break;
  118. printf("matoi2=%d\n", matoi2(buf));
  119. }
  120. return 0;
  121. }

程序的测试结果:

10522545459
matoi2=2147483647
-10522545459
matoi2=-2147483648

程序貌似对溢出的处理是正确的, 真的吗? 请注意代码的第79和第89行. 现在我把测试数据换成"10522545454", 与"10522545459"区别在于最后一个字符.

10522545454
matoi2=1932610862
-10522545454
matoi2=-1932610862

bingo! 正中下怀! 对于字串"10522545454", 在读取最后的数字字符'4'时, 整数1052254545已经大于2147483647/10了, 说明已经溢出, 不应该再判断字串的最后一位4是否大于2147483647%10, 所以第79行应该改为(89行修改方法类似):

            if (acc < cutoff || (acc == cutoff && c > cutlim)) {

修改过后的代码测试正常:

10522545459
matoi2=2147483647
-10522545459\
matoi2=-2147483648
10522545454
matoi2=2147483647
-10522545454
matoi2=-2147483648
quit

关于此bug, 我已经邮件通知En-Nut-Discussion.

以下为邮件回复的截图, Uwe Bonnes说: 可以打个补丁到分支. 不过他把单词reasonable给拼错了.


References:

Linux Cross Reference

Nut/OS API

文章知识点与官方知识档案匹配,可进一步学习相关知识
C技能树函数与程序结构函数的声明与定义156665 人正在系统学习中
注:本文转载自blog.csdn.net的MJN的文章"http://blog.csdn.net/njnu_mjn/article/details/9104143"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top