atoi函数讲解及其模拟实现

1. atoi 函数的基本介绍

atoi 函数定义<stdlib.h>头文件的一个库函数,函数定义如下:

int atoi (const char * str);

参数:

  • str:一个字符串指针,用于从中提取数据转化为整数。

参数类型:

  • const char * :是为了防止输入的字符串不被修改。

功能:

  • 从字符串中提取数字转化成 int 类型并返回。

1.1 atoi算法讲解

  1. 首先 atoi 会跳过字符串之前的所有的空白字符(比如空格、换行符或者制表符等字符)。
  2. 然后遇到‘+’字符或者‘-’字符就会判定要提取的整数的正负(如果正负符号后紧挨着的字符不是数值型字符,就会直接返回 0)。
  3. 开始识别数值性字符并且将其转化为整型数据并返回
  4. 如果从字符串中提取的整数超过 int 类型的范围,如果超过整形最大值,会发生整数截断,最后返回整形的最大值;如果小于整形的最小值,返回整形最小值
  5. 如果字符串的首元素是‘\0’,直接返回 0
  6. 如果一开始直接遇到字母字符(既不是数值性字符也不是正负号),直接返回 0

1.2 atoi测试用例

atoi 相关测试用例如下:

#include<stdio.h>
#include<stdlib.h>

int main()
{
	//情况1:一串数值型字符无其他字符
	char* str1 = "1234";
	int num1 = atoi(str1);
	printf("%d\n", num1);//1234
	 
	//情况2:含前导空格
	char* str2 = "  1234";
	int num2 = atoi(str2);
	printf("%d\n", num2);//1234
	 
	//情况3:含正负号
	char* str3 = "  -1234";
	int num3 = atoi(str3);
	printf("%d\n", num3);//-1234
	 
	//情况4:含字母
	char* str4 = "   asd1234";
	int num4 = atoi(str4);
	printf("%d\n", num4);//0

	char* str5 = "   1234asd";
	int num5 = atoi(str5);
	printf("%d\n", num5);//1234

	char* str6 = "   123as4d";
	int num6 = atoi(str6);
	printf("%d\n", num6);//123

	//情况5:正负号后的第一个位置是空格
	char* str7 = "   + 1234";
	int num7 = atoi(str7);
	printf("%d\n", num7);//0

	//情况6:整数截断
	char* str8 = "   -121212121212112121234";
	int num8 = atoi(str8);
	printf("%d\n", num8);//-2147483648

	char* str9 = "   121212121212112121234";
	int num9 = atoi(str9);
	printf("%d\n", num9);//2147483647

	return 0;
}

输出结果如下:

atoi 函数不同情况输出的输出结果

 

2. atoi的模拟实现

2.1 模拟实现代码

#include<stdio.h>
#include<assert.h>
#include<limits.h>
#include<ctype.h>

int my_atoi(const char* arr)
{
	assert(arr);

	if (*arr == '\0')
	{
		return 0;
	}

	while (isspace(*arr))
	{
		arr++;
	}

	int flag = 1;
	if (*arr == '+')
	{
		flag = 1;
		arr++;
	}
	else if (*arr == '-')
	{
		flag = -1;
		arr++;
	}

	long long int ret = 0;
	while (*arr)
	{
		if (isdigit(*arr))
		{
			ret = ret * 10 + (*arr - '0') * flag;
			

			if (ret > INT_MAX)
			{
				ret = INT_MAX;
			}
			else if (ret < INT_MIN)
			{
				ret = INT_MIN;
			}
			//考虑整数截断的情况
		}
		else
		{
			return (int)ret;//如果遇到其他字符也返回ret
		}
		arr++;
	}
	return (int)ret;//最后遇到符号串末尾退出循环,返回ret
}

int main()
{
	char* str = "-123asd23asdasd";
	int ret = my_atoi(str);
	printf("%d\n", ret);

	return 0;
}

2.2 手撕atoi模拟实现代码

2.2.1 函数定义

首先我们分析函数定义开始:

int my_atoi(const char* arr);

除函数名的区别外,参数类型、返回值类型和功能都与 atoi 函数基本一致,在此不多赘述。

我们来看函数内部代码实现:

2.2.2 所需头文件和库函数

(1) stdio.h 头文件

  • printf 函数:用于 main 函数打印最后 my_atoi 函数的返回结果。

(2) assert.h 头文件

  • aseert函数:用于在 my_atoi 函数传参时传入的地址为NULL进行报错处理。

(3) limits.h 头文件

  • INT_MAX 宏常量:代表整形的最大值(2147483647)。
  • INT_MIN 宏常量:代表整形的最小值(-2147483648)。

(4) ctype.h 头文件

  • isspace 函数:用于判断空白字符(比如空格、换行符和制表符等字符)。
  • isdigit 函数:用于判断数值型字符。

2.2.2 代码分析

2.2.2.1 assert 判定参数
	assert(arr);

首先是 assert 断言函数,在函数传参传入字符串首地址 arr 之后,判断 arr 是不是空指针 NULL,如果是就报错(assert 函数的参数部分如果为假就会报错)。

2.2.2.2 字符串首元素判定 
if (*arr == '\0')
	{
		return 0;
	}

这里的代码代表如果参数字符串首地址 arr 如果指向了 ‘\0’ (字符串的结束标志),直接返回 0。

2.2.2.3 跳过空白字符 
	while (isspace(*arr))
	{
		arr++;
	}

这里我们用 isspace 函数判断地址 arr 指向的字符是不是空白字符,如果是空白字符,让 arr 指向下一个字符(也就是让 arr 直接跳过字符串开头的所有空白字符,直到 arr 指向非空白字符)。

2.2.2.4 正负值的判断
    int flag = 1;
	if (*arr == '+')
	{
		flag = 1;
		arr++;
	}
	else if (*arr == '-')
	{
		flag = -1;
		arr++;
	}

这里我们创建一个 int 类型的整形变量 flag 作为状态变量用于代表所提取的整形数据到底是正数,还是负数。

在 arr 跳过之前的空白字符之后,我们根据arr遇到的第一个字符是不是正负号来判断要提取的整形数据是正数还是负数

  • 如果是正数,flag 值为 
  • 如果是负数,flag 值为 -1
  • 如果没遇到正负号,flag 默认为 1.

flag 的值判定为 1 或 -1 的原因会在下面的数值转换中会提到。

 2.2.2.5 数值转换和返回
    long long int ret = 0;
	while (*arr)
	{
		if (isdigit(*arr))
		{
			ret = ret * 10 + (*arr - '0') * flag;
			

			if (ret > INT_MAX)
			{
				ret = INT_MAX;
			}
			else if (ret < INT_MIN)
			{
				ret = INT_MIN;
			}
			//考虑整数截断的情况
		}
		else
		{
			return (int)ret;//如果遇到其他字符也返回ret
		}
		arr++;
	}
	return (int)ret;//最后遇到符号串末尾退出循环,返回ret
}

这里我们要定义一个变量用于返回并命名 ret ,一开始 ret 的类型我们定义为 long long int 类型(因为long long int 类型的数值范围比 int 类型要大得多),因为可能出现提取的字符超出整形范围的情况,如果直接定义成 int 类型,会出现错误截断,在这里为了顺利还原 atoi 函数的数值截断情况我们先把 ret 的类型定义为 long long int 类型

假设我们输入的字符串是 “12”

flag 此时为 1 (也就是代表我们要提取的数是正数)。

首先 arr 此时指向字符 ‘1’,然后此时 ret 为 0 ,ret 乘以 10 结果仍为 0,再让此时 arr 指向的字符 ‘1’ 与字符 ‘0’ 进行 ASCII 码值的整形相减运算(字符 ‘0’ 对应的 ASCII 码值是 48,字符 ‘1’ 对应的 ASCII 码值是 49,char 类型也是特殊的 int 类型),最后相减得出的结果是 1 ,再乘以 flag( flag 此时为1),最后相乘结果是 1 ,然后二者相加并赋值给 ret,ret 变为 1。

然后进行数值判定,发现没有超过整形数据的范围,ret 的值仍为 1 。

紧接着 arr++,arr 指向紧挨着的第二个字符也就是字符 ‘2’

此时 ret 为 1 ,ret 乘以 10 结果为 10,再让此时 arr 指向的字符 ‘2’ 与字符 ‘0’ 进行 ASCII 码值的整形相减运算(字符 ‘2’ 对应的 ASCII 码值是 50),最后相减得出的结果是 2 再乘以 flag( flag 此时为1),最后相乘结果是 2 ,然后二者相加并赋值给 ret,ret 变为 12。

然后进行数值判定,依旧没有超过整形数据的范围,ret 的值仍为 12 。

紧接着 arr++,arr 指向紧挨着的第二个字符也就是字符 '\0', 此时退出循环,并返回 ret。

关于atoi函数及其模拟实现,就讲到这里关于其他情况的逻辑分析,请读者自行探索,本文不再进行深入讲解追述太多,希望各位能够喜欢并且点赞本篇文章,如果错误请多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值