源于《剑指offer》经典面试题---库函数atoi如何使用及模拟实现

简介: 要求如果遇到空格,则根据需要丢弃可能多的空格字符,直到找到第一个非空字符。

本文将详细的讲解源于《剑指offer》种一道经典面试题--库函数atoi如何使用,以及它的模拟实现。


1.atoi函数的功能


将字符串转化为整数


e5109d63bcaf4732bcf487dbd80c57ae.png


2.使用atoi函数的要求


b39c53a7412f498b9c74c24414658be1.png


比如:


#include <stdlib.h>
int main()
{
  const char* p = "   666";//常量字符串是不可以修改的
  //所以用const修饰*p,使指针p指向的内容不能修改
  int ret = atoi(p);//参数是指向要转化的字符串的指针
  printf("%d", ret);
  return 0;
}


结果:


4c46689e64fd4a0ab2625e55c7ac7567.png


3.模拟实现atoi库函数


3.1 atoi函数的模拟要求:


bca810bb11c74e45a5c539311f208c78.png


不为空指针


if (p == NULL)
  {
    return 0;//如果为空指针则直接返回0
  }


不为空字符串


  if (*p =='\0')
  {
    return 0;//如果为空字符串则直接返回0
  }


遇到非数字字符


1.要求如果遇到空格,则根据需要丢弃可能多的空格字符,直到找到第一个非空字符。


  while (isspace(*s))//如果是空格则返回非0,如果不是则返回0
  {
    s++;
  }


2.然后从该字符开始,取一个可选字符 + 号或者 - 号


if (*p == '+')
  {
    flag = 1;//定义一个标签,如果为+号,则flag为1
    s++;
  }
  else if (*p == '-')
  {
    flag = -1;//如果为-号,则flag为-1;
    s++;
  }


超出范围


1.这时就是处理数字字符的转换了


  int n = 0;
  while (isdigit(*s))//如果为数字则返回非0,如果不是则返回0
  {
    n = n * 10 + flag*(*p - '0');
    //一个数字字符-'0'=数字;  比如'8'-'0'=8;
    //flag如果是-1,则最后答案也是负数,如果flag为1则最后答案为正数。
    s++;
  }


2.处理完数字字符转换,我们就要考虑,这个数字字符是否会超出整形的范围呢。


整形的最大值为 INT_MAX


最小值为INT_MIN


还有要注意这里的变量 n是有整形 int 定义的,数字字符再怎么超值,也会因为截断放进整形里面的,所以应该用更大的数long long来定义n


long long n = 0;
  while (isdigit(*s))
  {
    n = n * 10 + flag*(*s - '0');
    if (n > INT_MAX || n < INT_MIN)//超出int类型的范围
    {
      return 0;
    }
    s++;
  }


判断是否合法


上面这个判断是否为数字的while循环,会因为什么原因结束呢?


1.正常遇到字符串最后一个’\0’


如果遇到’\0’,则表明后面没有字符了,数字字符也全部转换了,可以直接返回 n


2.不是数字,循环结束


如果前面是数字字符,后面突然又不是数字字符了,则循环会停止下来,atoi函数要求 字符串可以在构成整数的字符之后包含其他字符,这些字符将被忽略,并且对此函数的行为没有影响。


所以最后也可以直接返回 n。


这两种情况都是合法的


不过你看到现在有没有什么迷惑呢?


我们将不合法的都直接返回0了,这样可以吗?


答案是不可以的。


因为我们不知道返回0是因为非法返回0还是就数字字符转换的0,所以我定义一个枚举变量state


它可能是非法的,也可能是合法的。因为大多数情况下是非法的,只有两种情况是合法的所以我们将它先定义为非法。


当发现是非法时,枚举变量不用改,然后直接返回0,那这个0就是在非法情况下返回的0,


当发现是合法时,我们再将枚举变量state改成合法的,那在这情况下返回的就是合法的。


enum State
{
  INVALE,// 0,默认是非法的
  VALE//1,默认是合法的
}state;
     state = INVALE;//因为大多数情况是非法的,所以一开始让state默认为非法的
  /* 设置成全局变量,整体都可以访问*/


最后我们再根据state是否合法来打印n


这样做的好处还有可以将所有的非法情况都直接用一个state =INVALE来表示了,因为还有其他情况我没有讨论,比如 " aa6665",数字前面有字母的没有列举但因为默认是非法的,只有合法是state才表示VALE,所以也没有关系。


最终模拟代码:


#include <ctype.h>
#include <stdio.h>
#define INT_MAX       2147483647
#define INT_MIN     (-2147483647 - 1)
enum State
{
  INVALE,//默认是非法的
  VALE//默认是合法的
}state;
     state = INVALE;//因为大多数情况是非法的,所以一开始让state默认为非法的
  /* 设置成全局变量,整体都可以访问*/
int digit_change(char* s)
{
  int flag = 1;//正数默认是1
  if (s == NULL)//1.空指针
  {
    return 0;//非法情况下的返回0
  }
  if (*s =='\0')//2.空白字符串
  {
    return 0;//非法情况下的返回0
  }
  while (isspace(*s))//如果是空格则返回非0,如果不是则返回0
  {
    s++;
  }
  if (*s == '+')//3.非法符号+  -
  {
    flag = 1;
    s++;
  }
  else if (*s == '-')
  {
    flag = -1;
    s++;
  }
  long long n = 0;
  while (isdigit(*s))//处理数字字符转换
  {
    n = n * 10 + flag*(*s - '0');
    if (n > INT_MAX || n < INT_MIN)
    {
      return 0;
    }
    s++;
  }
  /*跳出来有两种情况,一种是找到\0,另一种是发现不是数字字符,但都是合法的*/
  state = VALE;//把state改为合法的
  return (int)n;
}
int main()
{
  char* s = "   -112a";
  int ret=digit_change(s);
  if (state == VALE)
  {
    printf("合法的%d", ret);
  }
  else
  {
    printf("非法的%d", ret);
  }
  return 0;
}


结果:


96a52521767a4387b71f4799f58af9f0.png

相关文章
|
3天前
|
SQL Oracle 关系型数据库
[Oracle]面试官:你举例几个内置函数,并且说说如何使用内置函数作正则匹配
本文介绍了多种SQL内置函数,包括单行函数、非空判断函数、日期函数和正则表达式相关函数。每种函数都有详细的参数说明和使用示例,帮助读者更好地理解和应用这些函数。文章强调了字符串操作、数值处理、日期计算和正则表达式的使用方法,并提供了丰富的示例代码。作者建议读者通过自测来巩固学习成果。
10 1
[Oracle]面试官:你举例几个内置函数,并且说说如何使用内置函数作正则匹配
|
3月前
|
机器学习/深度学习
【机器学习】如何判断函数凸或非凸?(面试回答)
文章介绍了如何判断函数是凸函数还是非凸函数,包括凸函数的定义、几何意义、判定方法(一元函数通过二阶导数判断,多元函数通过Hessian矩阵的正定性判断),以及凸优化的概念和一些经典的凸优化问题。
144 1
【机器学习】如何判断函数凸或非凸?(面试回答)
|
3月前
|
JavaScript
【Vue面试题八】、为什么data属性是一个函数而不是一个对象?
这篇文章解释了为什么在Vue中组件的`data`属性必须是一个函数而不是一个对象。原因在于组件可能会有多个实例,如果`data`是一个对象,那么这些实例将会共享同一个`data`对象,导致数据污染。而当`data`是一个函数时,每次创建组件实例都会返回一个新的`data`对象,从而确保了数据的隔离。文章通过示例和源码分析,展示了Vue初始化`data`的过程和组件选项合并的原理,最终得出结论:根实例的`data`可以是对象或函数,而组件实例的`data`必须为函数。
【Vue面试题八】、为什么data属性是一个函数而不是一个对象?
|
4月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
57 8
|
3月前
|
安全 编译器 C++
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
|
4月前
|
Android开发 Kotlin
Android面试题之kotlin中怎么限制一个函数参数的取值范围和取值类型等
在Kotlin中,限制函数参数可通过类型系统、泛型、条件检查、数据类、密封类和注解实现。例如,使用枚举限制参数为特定值,泛型约束确保参数为Number子类,条件检查如`require`确保参数在特定范围内,数据类封装可添加验证,密封类限制为一组预定义值,注解结合第三方库如Bean Validation进行校验。
66 6
|
6月前
【一刷《剑指Offer》】面试题 23:从上往下打印二叉树
【一刷《剑指Offer》】面试题 23:从上往下打印二叉树
|
6月前
【一刷《剑指Offer》】面试题 22:栈的压入、弹出系列
【一刷《剑指Offer》】面试题 22:栈的压入、弹出系列
|
6月前
|
算法
【一刷《剑指Offer》】面试题 21:包含 main 函数的栈
【一刷《剑指Offer》】面试题 21:包含 main 函数的栈
|
6月前
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵
【一刷《剑指Offer》】面试题 20:顺时针打印矩阵