题目:实现atof/atoi,考虑正负号

本文详细介绍了C/C++中字符串转换为数值的方法,包括atof和atoi函数的具体实现过程及内部逻辑,通过示例代码展示了如何解析不同格式的字符串并转换为浮点数或整数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Data Conversion

函数作用
atof把字符串转换为float型数字
atoi把字符串转换为int型数字
atol把字符串转换为long型数字
__toascii把字符转换为ascii码
tolower把字符转换为小写字符
toupper把字符转换为大写字符
abs取绝对值
strtod 
strtol 

 

二.atof

其实,这种字符串转换的函数,都无非做的同一件事情--解析字符串,根据不同的解析规则,执行不同的转换方式。

atof是借助strtod来实现的,主要步骤为:

  1. 1).解析空格  isspace
  2. 2).解析正负号  ‘+’or ‘-’
  3. 3).解析整数部分数字  isdigit
  4. 4).解析小数点及其后小数部分
    1. 5).解析科学计数部分
    2. 6).解析科学计数的正负号
    3. 7).还原组合数字

2.实现

   1: inline double tAtof(const tChar *pStr)
   2: {
   3:     return tStrtod(pStr, NULL);
   4: }
   5:  
   6: inline double tStrtod(const tChar *pStr, tChar **pEndStr)
   7: {
   8:     // advance beyond any leading whitespace
   9:     while( ::isspace(*pStr) )
  10:         ++pStr;
  11:  
  12:     // check for optional '+' or '-'
  13:     unsigned short uNegative = 0;
  14:     if( *pStr == _T('-') )
  15:     {
  16:         uNegative = 1;
  17:         ++pStr;
  18:     }
  19:     else if( *pStr == _T('+') )
  20:         ++pStr;
  21:  
  22:  
  23:     double dRet = 0.0;
  24:     int e = 0;
  25:     int c = 0;
  26:  
  27:     // 解析整数部分
  28:     while((c = *pStr) != _T('/0') && isdigit(c) )
  29:     {
  30:         dRet = dRet * 10.0 + (c - _T('0')); 
  31:         ++pStr;
  32:     }
  33:  
  34:     // 解析小数部分
  35:     if( c == _T('.') )
  36:     {
  37:         ++pStr;
  38:         while((c = *pStr) != _T('/0') && isdigit(c) )
  39:         {
  40:             dRet = dRet * 10.0 + (c - _T('0'));
  41:             e -= 1;
  42:  
  43:             ++pStr;
  44:         }
  45:     }
  46:  
  47:     // 解析科学计数
  48:     if( c == 'e' || c == 'E' )
  49:     {
  50:         int nSign = 1;
  51:         int nExponet = 0;
  52:  
  53:         c = *++pStr;
  54:  
  55:         // 解析正负号
  56:         if( c == _T('+') )
  57:             c = *++pStr;
  58:         else if( c == _T('-') )
  59:         {
  60:             nSign = -1;
  61:             c = *++pStr;
  62:         }
  63:  
  64:         while( isdigit(c) )
  65:         {
  66:             nExponet = nExponet * 10 + (c - _T('0'));
  67:             c = *pStr++;
  68:         }
  69:  
  70:         e += nExponet * nSign;
  71:     }
  72:  
  73:     // 还原组合数字
  74:     while(e > 0)
  75:     {
  76:         dRet *= 10.0;
  77:         --e;
  78:     }
  79:     while(e < 0)
  80:     {
  81:         dRet *= 0.1;
  82:         ++e;
  83:     }
  84:  
  85:     return uNegative ? -dRet : dRet;
  86: }

其实,思想挺简单的,只是有点长~。具体多琢磨琢磨就更清楚了。

3.测试

   1: tChar    *str = NULL;
   2: double  value = 0;
   3:  
   4: // An example of the atof function
   5: // using leading and training spaces.
   6: str = _T("  3336402735171707160320 ");
   7: value = CY_CRT::tAtof( str );
   8: cout << value << endl;
   9:  
  10: // Another example of the atof function
  11: // using the 'd' exponential formatting keyword.
  12: str = _T("3.1412764583d210");
  13: value = CY_CRT::tAtof( str );
  14: cout << value << endl;
  15:  
  16: // An example of the atof function
  17: // using the 'e' exponential formatting keyword.
  18: str = _T("-2309.12E-15");
  19: value = CY_CRT::tAtof( str );
  20: cout << value << endl;
  21:  
  22:  
  23: str = _T("-0.123e-10");
  24: value = CY_CRT::tAtof( str );
  25: cout << value << endl;

三、atoi

原理如下:

  1. 1)解析空格  tIsspace
  2. 2)判断正负号  ‘-’or  ’+’
  3. 3)strtol判断转换进制  16进制、8进制、10进制
  4. 4)解析数字字符
  5. 5)对每个字符转换为相应数字  tIsdigit  tIsalpha
  6. 6) 判断转换后的数字是否超过最大值
  7. 7)累加,基于转换进制
  8. 8)如果超过最大值则出错处理,负数就返回相反数
  9. 9)返回转换后的数字

1.实现

   1: inline int tAtoi(const tChar *pStr)
   2: {
   3:     return tStrtol(pStr, NULL, 10);
   4: }
   5:  
   6: inline long tStrtol(const tChar *pStr, tChar **pEndPtr, int nBase)
   7: {
   8:     /*
   9:         Skip white space and pick up leading +/- sign if any.
  10:         If nBase is 0, allow 0x for hex and 0 for octal, else assume decimal
  11:         If nBase if already 16, allow 0x
  12:     */
  13:  
  14:     const tChar *pStrTmp = pStr;
  15:     int c = 0;
  16:  
  17:     
  18:     do 
  19:     {
  20:         c = *pStrTmp++;            /* skip whitespace */
  21:     } while (tIsspace(c));
  22:  
  23:     int nNegative = 0;            /* remember minus sign */
  24:     if( c == _T('-') )
  25:     {
  26:         nNegative = 1;
  27:         c = *pStrTmp++;
  28:     }
  29:     else if( c == _T('+') )
  30:     {
  31:         c = *pStrTmp++;            /* skip sign */
  32:     }
  33:  
  34:     if( nBase < 0 || nBase == 1 || nBase > 36) 
  35:     {
  36:         /* bad base! */
  37:         if( pEndPtr )
  38:             /* store beginning of string in endptr */
  39:             *pEndPtr = const_cast(reinterpret_cast<const tChar *>(pStr));
  40:         return 0L;              /* return 0 */
  41:     }
  42:  
  43:     // 判断当前是否转换为16进制
  44:     if( (nBase == 0 || nBase == 16) &&
  45:         (c == _T('0')) &&
  46:         (*pStrTmp == _T('x') || *pStrTmp == _T('X')) )
  47:     {
  48:         c = pStrTmp[1];
  49:         pStrTmp += 2;
  50:         nBase = 16;
  51:     }
  52:     // 是否转换为8进制
  53:     if( nBase == 0 )
  54:         nBase = (c == _T('0') ? 8 : 10);
  55:  
  56:  
  57:     /* if our number exceeds this, we will overflow on multiply */
  58:     unsigned long ulCutOff = 0;
  59:     int nCutLim = 0;
  60:  
  61:     ulCutOff    = nNegative ? -(unsigned long)(LONG_MIN) : LONG_MAX;
  62:     nCutLim        = ulCutOff % static_cast<unsigned long>(nBase);
  63:     ulCutOff    /= static_cast<unsigned long>(nBase);
  64:  
  65:  
  66:     unsigned long acc =0;
  67:     int any = 0;
  68:     for(acc = 0, any = 0; ; c = *pStrTmp++)
  69:     {
  70:         /* convert c to value */
  71:         if( tIsdigit(c) )
  72:             c -= _T('0');
  73:         else if( tIsalpha(c) )
  74:             c -= tIsupper(c) ? _T('A') - 10 : _T('a') - 10;
  75:         else
  76:             break;         /* exit loop if bad digit found */
  77:  
  78:         if( c >= nBase )
  79:             break;
  80:  
  81:         /*    we now need to compute number = number * base + digval,
  82:             but we need to know if overflow occured.  This requires
  83:             a tricky pre-check. 
  84:         */
  85:         if( any < 0 || acc > ulCutOff || acc == ulCutOff && c > nCutLim )
  86:             any = -1;
  87:         else    
  88:         {
  89:             any = 1;
  90:             acc *= nBase;
  91:             acc += c;
  92:         }
  93:     }
  94:  
  95:     if( any < 0 )
  96:     {
  97:         /* we would have overflowed */
  98:         acc = nNegative ? LONG_MIN : LONG_MAX;
  99:         //throw std::overflow_error("LONG_MIN or LONG MAX");
 100:     }
 101:     /* negate result if there was a neg sign */
 102:     else if( nNegative )
 103:         acc = -acc;
 104:  
 105:     if( pEndPtr != 0 )
 106:         *pEndPtr = const_cast(reinterpret_cast<const tChar *>(any ? pStrTmp - 1: pStr));
 107:  
 108:     return acc;
 109: }

测试代码为

   1: tChar    *str = NULL;
   2: int     value = 0;
   3:  
   4: // An example of the atoi function.
   5: str = _T("  -2309 ");
   6: value = CY_CRT::tAtoi( str );
   7: wcout << value << endl;
   8:  
   9: // Another example of the atoi function.
  10: str = _T("31412764");
  11: value = CY_CRT::tAtoi( str );
  12: wcout << value << endl;


<think>好的,我现在需要为用户准备一些关于结构体(struct)、字符串转换和数据类型相关的题目。首先,我需要确保自己对这些概念有清晰的理解,这样才能设计出有效的考题。 首先,结构体struct是C或C++中用于组合不同数据类型的一种方式,允许将多个变量组合成一个单一的类型。用户可能需要理解如何定义结构体、访问其成员以及结构体的内存布局。例如,可以出题关于结构体的大小计算,考虑对齐问题。 然后是字符串转换。这部分可能涉及字符串与其他数据类型(如整数、浮点数)之间的转换函数,比如atoi、itoa、sprintf等。同时需要注意错误处理和不同编程语言中的差异,比如C语言和C++中的方法可能不同。题目可能包括编写转换函数或分析现有代码的问题。 数据类型方面,需要涵盖基本类型(int, float, double, char等)、它们的范围、存储方式以及类型转换的规则,包括隐式转换和显式转换。题目可能涉及类型提升、溢出问题,或者不同数据类型之间的转换结果。 接下来,我需要考虑用户可能的水平。如果是初学者,题目应该基础一些,比如结构体的定义和使用;如果是进阶用户,可能需要涉及内存对齐、指针与结构体的交互等。同样,字符串转换的题目可以从简单的函数使用到手动实现转换算法。数据类型的题目可能包括sizeof运算符的结果,或者类型转换中的陷阱。 然后,我需要确保题目的多样性,涵盖理论题和编程题。例如,理论题可能问结构体对齐的原因,编程题可能要求编写一个处理结构体数组的函数。字符串转换的编程题可以要求将字符串转换为整数,并处理非法输入。数据类型的问题可能涉及不同环境下数据类型的范围差异,比如在32位和64位系统上long的大小。 还要注意常见的错误点,比如结构体传递时的按值传递与指针传递的区别,字符串转换中的缓冲区溢出,以及数据类型转换时的精度丢失。这些都可以作为题目的考察点。 现在具体设计题目: 结构体部分: 1. 定义一个包含int、char、double的结构体,计算其大小并解释原因。 2. 结构体作为函数参数传递的方式有哪些?哪种更高效? 3. 编写一个函数,初始化结构体数组并按某个成员排序。 字符串转换: 1. 实现atoi函数,考虑正负号和非法字符。 2. 使用sprintf将浮点数格式化为字符串,需要注意什么? 3. 分析一段代码中字符串转换可能导致的缓冲区溢出问题。 数据类型: 1. 给定几个表达式,判断类型转换的结果,如int和unsigned int相加时的提升规则。 2. 解释sizeof(int)在不同系统下的可能值。 3. 代码中有隐式类型转换,指出可能的精度丢失问题。 最后,需要为每个题目准备参考答案和解析,帮助用户理解。例如,结构体对齐的问题需要说明编译器如何根据成员的对齐要求调整结构体大小。字符串转换中的atoi实现需要注意处理前导空格、正负号以及溢出检查。数据类型题目需要解释整数提升和常见陷阱。 总结下来,设计这些题目时需要覆盖基本概念、常见错误和应用场景,帮助用户全面理解相关知识点。</think>### 题目练习:结构体、字符串转换、数据类型 以下题目分三个模块,附参考答案及解析,适合自测基础知识点掌握程度。 --- #### **一、结构体 (struct)** 1. **基础题** ```c struct Student { int id; char name[20]; float score; }; ``` **问题**:用 `sizeof(struct Student)` 计算结构体大小时,结果是否等于 `sizeof(int) + sizeof(char[20]) + sizeof(float)`?为什么? <details> <summary>参考答案</summary> **答案**:不等于。 **解析**:结构体内存对齐规则会导致填充字节(padding)。例如,`char name[20]` 可能从4字节对齐的位置开始存储,而结构体总大小是最大成员对齐值的整数倍。 </details> --- 2. **应用题** **问题**:以下代码的输出是什么? ```c #include <stdio.h> typedef struct { short a; int b; } Data; int main() { printf("%zu", sizeof(Data)); return 0; } ``` (假设 `sizeof(short)=2`, `sizeof(int)=4`) <details> <summary>参考答案</summary> **答案**:8 **解析**:`int b` 需要4字节对齐,因此 `short a` 后会有2字节填充,总大小为 `2 + 2(padding) + 4 = 8`。 </details> --- #### **二、字符串转换** 1. **基础题** **问题**:在C语言中,如何将字符串 `"123.45"` 转换为浮点数?写出代码片段。 <details> <summary>参考答案</summary> ```c #include <stdlib.h> char str[] = "123.45"; float num = atof(str); ``` **注意**:更安全的做法是使用 `strtof` 并检查错误: ```c char *endptr; float num = strtof(str, &endptr); if (*endptr != '\0') { /* 转换失败 */ } ``` </details> --- 2. **陷阱题** **问题**:以下代码有什么问题? ```c char buffer[10]; int x = 1000; sprintf(buffer, "%d", x); ``` <details> <summary>参考答案</summary> **答案**:缓冲区溢出。 **解析**:`x=1000` 转换为字符串是 `"1000"`(4字节+结尾 `\0` 共5字节),未溢出。但若 `x` 更大(如 `1234567890`),会导致溢出。应用 `snprintf(buffer, sizeof(buffer), "%d", x)`。 </details> --- #### **三、数据类型** 1. **隐式转换题** **问题**:以下代码的输出是什么? ```c #include <stdio.h> int main() { unsigned int a = 10; int b = -5; printf("%d\n", a + b); return 0; } ``` <details> <summary>参考答案</summary> **答案**:输出取决于编译器(未定义行为)。 **解析**:`a` 是 `unsigned int`,`b` 会被转换为 `unsigned int`。`-5` 转换为无符号数是一个极大值(如 `4294967291`),相加结果为 `10 + 4294967291 = 4294967301`。但用 `%d` 输出时,会解释为有符号整数,导致溢出(未定义行为)。 </details> --- 2. **类型范围题** **问题**:32位系统中,`long` 和 `long long` 的范围分别是多少? <details> <summary>参考答案</summary> - **`long`**:通常4字节,范围 `-2,147,483,648 到 2,147,483,647` - **`long long`**:8字节,范围 `-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807` **注意**:具体实现可能不同,可用 `<limits.h>` 中的宏(如 `LONG_MAX`)验证。 </details> --- ### 综合练习建议 1. 手动实现 `atoi` 函数(处理负数、溢出、非数字字符)。 2. 设计一个结构体存储复数,并实现加法函数。 3. 分析类型转换在混合表达式中的优先级(如 `char + float * int`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值