理解如何创建atoi函数;字符是如何进行比较的?

8
我正在尝试提高对C++的理解,特别是指针算术。我经常使用atoi函数,但很少考虑它是如何工作的。查找了一下它的实现方式,我大部分都能理解,但有一件事情让我感到困惑。
以下是我在网上找到的一个解决方案示例:
int atoi( char* pStr ) 
{
  int iRetVal = 0; 

  if ( pStr )
  {
    while ( *pStr && *pStr <= '9' && *pStr >= '0' ) 
    {
      iRetVal = (iRetVal * 10) + (*pStr - '0');
      pStr++;
    }
  } 
  return iRetVal; 
} 

我认为我过去很难理解atoi的主要原因在于字符比较的方式。"while"语句表示只要该字符存在,该字符小于等于9,大于等于0,就执行某些操作。这个语句对我来说有两层意思:
  1. 字符可以逻辑上与其他字符进行比较(但返回值是什么?)。

在我查看这个之前,我想我下意识地知道,但我从来没有真正思考过,但是'5'字符与'6'字符相比较的方式与5小于6的方式相同,因此您可以将字符作为整数进行比较(为此目的)。

  1. 不知何故,* sPtr和* SPtr!= 0是不同的。这对我来说似乎很明显,但我发现我无法用言语表达出来,这意味着我知道这是真的,但我不明白为什么。

编辑:我不知道* pStr - '0'部分会做什么。

希望能帮助您理解这些观察结果!谢谢!


4
实际上,while(*s)while(*s != 0) 是等同的。在 C 语言中,非零整数被视为“真”。 - nneonneo
啊,好的,我误解了语句的意图。*pStr >= '0' 是为了避免负数,我想是吧? - Joshua
2
然而,while(*s != 0)while(*s != '0')并不相同,因为0是数字0,而'0'是字符'0'(其ASCII值为48)。 - nneonneo
@Joshua:请注意,通常情况下'0'!= 0*pStr <= '9' && *pStr >= '0'检查*pStr是否为数字。 - Grizzly
对于一个非常清晰明了的问题,点赞。 - David G
8个回答

4

只要字符存在

不是这样的。它的意思是“当字符不为0(或'\0')时。基本上,ASCII字符'\0'表示“C”字符串的结尾。由于您不想超出字符数组的末尾(而且确切的长度未知),因此每个字符都会被测试是否为'\0'

字符可以逻辑比较

没错。字符就是一个数字,至少在ASCII编码中是这样的。在ASCII中,例如,'0'对应于十进制值48'1'为49,'Z'为90(您可以在这里查看ASCII表)。所以,您可以像比较整数一样比较字符。

某种程度上,while (*sPtr)*sPtr != 0是不同的。

完全没有区别。十进制0是一个特殊的ASCII符号(nul),用于表示“C”字符串的结尾,就像我在开头提到的那样。您无法看到或打印(nul),但它确实存在。


好的,你可以打印空字符。printf("%c",0) ;) - nneonneo

3

*pStr - '0'将字符转换为数字值,'1' - '0' = 1 while循环检查我们是否没有到达字符串的末尾,同时我们有一个有效的数字。


2

C语言中,一个字符被表示为ASCII值。由于在ASCII码表中所有数字都是连续的(即0x30代表'0',0x39代表'9',其它数字依次类推),你可以通过范围检查来确定一个字符是否为数字,并且通过减去'0'来获取该数字的值。


1
在这里使用 isdigit 是行不通的,因为 isdigit 是依赖于语言环境的。它可能会对特定于语言环境的数字字符返回 true,但这需要理解翻译成数字的编号系统。 - JoergB
C语言确实要求数字在任何语言环境下都必须是连续的,我想是这样。 - Lightness Races in Orbit
我选择这个答案是因为它解释了如何检查字符是否为数字。这是我知道将来会非常有用的事情之一。 - Joshua

0

C风格的字符串是以空字符结尾的

因此:

while ( *pStr && *pStr <= '9' && *pStr >= '0' ) 

这个测试:

  • *pStr 表示我们还没有到字符串的结尾,等同于写成 *pStr != 0(注意没有单引号,ASCII 值为 0,或者NUL)。
  • *pStr >= '0' && *pStr <= '9' (更逻辑化)表示 *pStr 上的字符在范围 '0'(ASCII 值为 48)和 '9'(ASCII 值为 57)之间,即一个数字

0
请注意,所发布的atoi实现并不完整。真正的atoi可以处理负值。
一些人会认为while (*sPtr)和*sPtr != 0是不同的。
这两个表达式是相同的。当作为条件使用时,当存储在地址sPtr处的值不为零时,*sPtr被视为true,而*sPtr!= 0在存储在地址sPtr处的值不为零时为true。区别在于当在其他地方使用时,第二个表达式将计算为true或false,但第一个表达式将计算为存储的值。

0

'0'在内存中的表示是0x30,而'9'的表示是0x39。这就是计算机所看到的,当它使用逻辑运算符进行比较时,它使用这些值。空终止字符表示为0x00(也称为零)。关键在于,对于机器来说,chars与任何其他int一样。

因此,while语句的意思是:

只要我们正在检查的字符有效(即不为零,因此不是空终止符),并且其值(作为机器看到的)小于0x39且大于0x30,则继续执行。

while循环体根据整数在字符串中的位置计算应添加到累加器中的适当值。然后它增加指针并再次执行。完成后,它返回累积的值。


0

这段代码使用ASCII值来累加其字母等价物的整数总数。

关于您的第一个编号,当比较任何内容时,结果都是布尔值,这似乎非常琐碎。虽然我觉得您想问编译器是否真正理解“字符”。但据我了解,这种比较是使用字符的ASCII值进行的。即 a < b 被解释为 (97 < 98)。 (请注意,当您比较 'a' 和 'A' 时,很容易看出使用了ASCII值,因为 'A' 小于 'a')

关于您的第二个编号,while 循环似乎正在检查是否有已分配的值不为 NULL(ASCII 值为 0)。and 运算符一旦遇到 false 语句就会产生 FALSE,以便您不会对 NULL 字符进行比较。至于 while 循环的其余部分,正如我在第一条中提到的那样,它正在进行 ASCII 比较。它只是检查给定字符是否对应于与数字相关的 ASCII 值。即 '0' 到 '9' 之间(或 ASCII:48 到 57 之间)。

最后,我认为(*ptr- '0')是最有趣的部分。此语句返回一个介于0和9之间的整数。如果您查看ascii表,您会注意到数字0到9是并排的。所以想象一下'3'-'0',它是51-48,产生3! :D 所以以更简单的术语说,它正在执行ascii减法,并返回相应的整数值。 :D

祝福大家,希望这能解释一些问题。


0

让我们来分解一下:

if ( pStr )

如果你传递给 atoi 一个空指针,pStr 将会是 0x00 - 这将会是 false。否则,我们有一些东西需要解析。

while ( *pStr && *pStr <= '9' && *pStr >= '0' )

好的,这里有一堆事情要做。 *pStr 意味着我们检查 pStr 指向的值是否为 0x00。如果你查看 ASCII 表,0x00 的 ASCII 是“null”,在 C/C++ 中约定字符串是以 null 结尾的(与 Pascal 和 Java 风格的字符串相反,它们告诉你它们的长度,然后有那么多个字符)。因此,当 *pStr 评估为 false 时,我们的字符串已经结束了,我们应该停止。

*pStr <= '9' && *pStr >= '0' 之所以有效,是因为 ASCII 字符 '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 的值都是连续的 - 例如,'0' 是 0x30,而 '9' 是 0x39。因此,如果 pStr 指向的值超出了这个范围,那么我们就不解析整数,应该停止。

iRetVal = (iRetVal * 10) + (*pStr - '0');

由于 ASCII 数字在内存中是连续的,所以如果我们知道我们有一个数字,*pStr - '0' 就会计算出它的数值 - '0' 为 0 (0x30 - 0x30),'1' 为 1 (0x31 - 0x30)... '9' 为 9。因此,我们将数字向上移位并滑入新位置。

pStr++;

通过将指针加一,指针指向内存中的下一个地址 - 我们要转换为整数的字符串中的下一个字符。
请注意,如果字符串没有以空值结尾,其中包含任何非数字字符(例如“-”),或者以任何方式不是ASCII,则此函数将出现问题。它并不神奇,只是依赖于这些事情是真实的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接