这个自定义的 toupper() 函数是如何工作的?
How this custom toupper() function works?
我见过以下使用自定义 toupper()
函数的程序。
#include <stdio.h>
void my_toUpper(char* str, int index)
{
*(str + index) &= ~32;
}
int main()
{
char arr[] = "geeksquiz";
my_toUpper(arr, 0);
my_toUpper(arr, 5);
printf("%s", arr);
return 0;
}
这个函数具体是怎么工作的?我无法理解其背后的逻辑。如果有人简单地解释它会很好。
在 ASCII table 之后,要将字母从小写转换为大写,您需要从小写字母的 ASCII 值中减去 32
。
对于表示小写字母的ASCII值,减去32,等于ANDing ~32
。这就是
中所做的
*(str + index) &= ~32;
它从str
中取第index
个成员的值,减去32(与[=12=按位与],清除特定位值)并将其存储回相同的索引。
FWIW,这是 "resetting" 一个特定位的特例,用于获得实际减去 32 的结果。这个 "subtraction" 在这里工作基于小写字母 ASCII 值的特定位表示.如评论中所述,这不是一般的减法方式,因为此 "resetting" 逻辑不适用于 any 减法值。
关于使用的operators,
&=
是按位AND 赋值
~
是按位非。
注意:此自定义函数缺少对 str
中存在的(无效)有效值的错误检查。你需要照顾好它。
要理解这一点,我们必须查看字母的 ASCII 表示。在基数 2 中执行此操作最简单。
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100
... ...
X 01011000 x 01111000
Y 01011001 y 01111001
Z 01011010 z 01111010
注意大写字母都是010
开头,小写字母都是011
开头。请注意,对于同一字母的大写和小写版本,低位都是相同的。
所以:要将一个小写字母转换成对应的大写字母,我们只需要把011
改成010
,也就是关掉00100000
位。
现在,关闭某个位的标准方法是对一个掩码进行按位与操作,其中在您要关闭的位的位置为 0,在其他位置为 1。所以我们要的mask是11011111
。我们可以将其写为 0xdf
,但本例中的程序员选择通过编写 ~32
来强调它是 00100000
的补充掩码。 32 的二进制是 00100000
.
这项技术工作正常,除了它会对非字母做一些奇怪的事情。例如,它会将 '{'
变成 '['
(因为它们分别具有 ASCII 代码 01111011
和 001011011
)。它会将星号 '*'
变成换行符 '\n'
(00101010
变成 00001010
)。
将 ASCII 中的大写字母转换为小写字母的另一种方法是减去 32。这也会将 'a'
转换为 'A'
(97 到 65,十进制),但是如果还可以将 'A'
转换为 '!'
。在这种情况下,按位与技术实际上是有利的,因为它将 'A'
转换为 'A'
(这是转换为大写例程应该做的)。
最重要的是,无论您是用 ~32 与还是减去 32,在一个适当安全的函数中,您还必须检查要转换的字符是否是正确的开头字母。
此外,值得注意的是,此技术绝对假定 7 位 ASCII 字符集,并且不适用于其他字符集(例如 ISO-8859 或 Unicode)的重音或非罗马字母。 (EBCDIC 将是另一回事。)
我见过以下使用自定义 toupper()
函数的程序。
#include <stdio.h>
void my_toUpper(char* str, int index)
{
*(str + index) &= ~32;
}
int main()
{
char arr[] = "geeksquiz";
my_toUpper(arr, 0);
my_toUpper(arr, 5);
printf("%s", arr);
return 0;
}
这个函数具体是怎么工作的?我无法理解其背后的逻辑。如果有人简单地解释它会很好。
在 ASCII table 之后,要将字母从小写转换为大写,您需要从小写字母的 ASCII 值中减去 32
。
对于表示小写字母的ASCII值,减去32,等于ANDing ~32
。这就是
*(str + index) &= ~32;
它从str
中取第index
个成员的值,减去32(与[=12=按位与],清除特定位值)并将其存储回相同的索引。
FWIW,这是 "resetting" 一个特定位的特例,用于获得实际减去 32 的结果。这个 "subtraction" 在这里工作基于小写字母 ASCII 值的特定位表示.如评论中所述,这不是一般的减法方式,因为此 "resetting" 逻辑不适用于 any 减法值。
关于使用的operators,
&=
是按位AND 赋值
~
是按位非。
注意:此自定义函数缺少对 str
中存在的(无效)有效值的错误检查。你需要照顾好它。
要理解这一点,我们必须查看字母的 ASCII 表示。在基数 2 中执行此操作最简单。
A 01000001 a 01100001
B 01000010 b 01100010
C 01000011 c 01100011
D 01000100 d 01100100
... ...
X 01011000 x 01111000
Y 01011001 y 01111001
Z 01011010 z 01111010
注意大写字母都是010
开头,小写字母都是011
开头。请注意,对于同一字母的大写和小写版本,低位都是相同的。
所以:要将一个小写字母转换成对应的大写字母,我们只需要把011
改成010
,也就是关掉00100000
位。
现在,关闭某个位的标准方法是对一个掩码进行按位与操作,其中在您要关闭的位的位置为 0,在其他位置为 1。所以我们要的mask是11011111
。我们可以将其写为 0xdf
,但本例中的程序员选择通过编写 ~32
来强调它是 00100000
的补充掩码。 32 的二进制是 00100000
.
这项技术工作正常,除了它会对非字母做一些奇怪的事情。例如,它会将 '{'
变成 '['
(因为它们分别具有 ASCII 代码 01111011
和 001011011
)。它会将星号 '*'
变成换行符 '\n'
(00101010
变成 00001010
)。
将 ASCII 中的大写字母转换为小写字母的另一种方法是减去 32。这也会将 'a'
转换为 'A'
(97 到 65,十进制),但是如果还可以将 'A'
转换为 '!'
。在这种情况下,按位与技术实际上是有利的,因为它将 'A'
转换为 'A'
(这是转换为大写例程应该做的)。
最重要的是,无论您是用 ~32 与还是减去 32,在一个适当安全的函数中,您还必须检查要转换的字符是否是正确的开头字母。
此外,值得注意的是,此技术绝对假定 7 位 ASCII 字符集,并且不适用于其他字符集(例如 ISO-8859 或 Unicode)的重音或非罗马字母。 (EBCDIC 将是另一回事。)