user-0 提问: Char变量输入一个大于127的值，返回的结果问题

回复:
user: user-1
content: 计算机底层是以二进制补码形式存储的，取数时会还原成原码，char占1字节，也就是8位，最高位为符号位，把197转成二进制就是11000101，而正数的原码反码补码都是一样的，但因为这里最高位为1，机器会把他当做负数，把它转换为原码之后就是-59了

user: user-0
content: 引用 10 楼 均陵鼠侠的回复:[quote=引用 6 楼 拜你所赐 的回复:][quote=引用 5 楼 均陵鼠侠 的回复:][quote=引用 4 楼 拜你所赐 的回复:][quote=引用 3 楼 均陵鼠侠 的回复:]这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。   您好，非常感谢您的回复，我使用的编译器是VS2012。我是一个C语言的初学者，我对您的回复还有一些疑问，在您的回复中说到“在197和198这两个数值的二进制形式中，符号位似乎并不是1。”197 -->11000101      198  -->  11000110    直接转化成二进制的首位是否会直接成为符号位呢？烦请您帮忙把197到-59的过程详细说一下吗？[/quote]  对，我这两句话没说明白。  赋给 c1 和 c2 的值来自于常量 197 和 198 ，这两个常量的类型是 int 。所以，它们的最高位不是1，而是0。即，如果 int 类型的长度是 32 位，则 197 的二进制形式为 00000000000000000000000011000101 。把它截断为 char 类型的长度时，如果 char 类型的长度是 8，则截断后是 11000101 。[/quote]十分感谢您的解答，还有一个问题我没有搞明白，例如197.对于截断后的二进制11000101。它的最高位是1，即后面表示数值的二进制是“1000101”，转变成二进制之后为69。但是取其补码之后即为“0111010”它的十进制是“58”。截断之后为什么不是直接输出它的十进制而是取其补码呢？这点我有点搞不明白。[/quote] 对机器来说，截断之后，如果最高位是1，即表明它是一个负数，而负数本身就是采用补码的，不需要也不会再次转换为补码。要么是你把机器想得太复杂了，要么是一些不正确的说法影响了你的思维。  另一方面，负数有三种表示方法：符号带大小、对1的补码和对2的补码，C语言并没有限定采用哪种表示方法，这个取决于平台和编译器，只不过对2的补码是普遍的情况。无论采用哪种表示方法，从编程和运行的层面上基本上看不到，也不需要过问，毕竟它不影响输出和结果的正确性。所以，说负数采用补码，眼光过于局限了。[/quote] 谢谢老师，看了老师的回复才豁然开朗，钻了牛角尖，把机器想的太过于复杂了，十分感谢老师的解答！

user: user-2
content: 引用 6 楼 拜你所赐 的回复:[quote=引用 5 楼 均陵鼠侠 的回复:][quote=引用 4 楼 拜你所赐 的回复:][quote=引用 3 楼 均陵鼠侠 的回复:]这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。   您好，非常感谢您的回复，我使用的编译器是VS2012。我是一个C语言的初学者，我对您的回复还有一些疑问，在您的回复中说到“在197和198这两个数值的二进制形式中，符号位似乎并不是1。”197 -->11000101      198  -->  11000110    直接转化成二进制的首位是否会直接成为符号位呢？烦请您帮忙把197到-59的过程详细说一下吗？[/quote]  对，我这两句话没说明白。  赋给 c1 和 c2 的值来自于常量 197 和 198 ，这两个常量的类型是 int 。所以，它们的最高位不是1，而是0。即，如果 int 类型的长度是 32 位，则 197 的二进制形式为 00000000000000000000000011000101 。把它截断为 char 类型的长度时，如果 char 类型的长度是 8，则截断后是 11000101 。[/quote]十分感谢您的解答，还有一个问题我没有搞明白，例如197.对于截断后的二进制11000101。它的最高位是1，即后面表示数值的二进制是“1000101”，转变成二进制之后为69。但是取其补码之后即为“0111010”它的十进制是“58”。截断之后为什么不是直接输出它的十进制而是取其补码呢？这点我有点搞不明白。[/quote] 对机器来说，截断之后，如果最高位是1，即表明它是一个负数，而负数本身就是采用补码的，不需要也不会再次转换为补码。要么是你把机器想得太复杂了，要么是一些不正确的说法影响了你的思维。  另一方面，负数有三种表示方法：符号带大小、对1的补码和对2的补码，C语言并没有限定采用哪种表示方法，这个取决于平台和编译器，只不过对2的补码是普遍的情况。无论采用哪种表示方法，从编程和运行的层面上基本上看不到，也不需要过问，毕竟它不影响输出和结果的正确性。所以，说负数采用补码，眼光过于局限了。

user: user-3
content: 数值197是int类型, 赋值于char类型c1, 高位将被截去,即为11000101  当输出格式为%c: 按C定义:字符输出不会是负值, 所以结果为"?"; 当输出格式为%d: char类型变量如果没有加限定符(signed/unsigned),由机器本身规则确定是按符号数或无符号数处理.  就楼主例子, 由于对c1/c2没有用限定符,楼主机器是按符号数输出的.

user: user-4
content: 计算机是按补码来存储的，所以截断后的11000101是补码，其中最高位的1是符号为表示是负数，后7位补码1000101转为原码是0111011，十进制是59，所以就是-59

user: user-0
content: 引用 1 楼 user-5 的回复:百度“负数、补码” %c问号的原因是因为找不到对应的ASCII字符 我知道%c是找不到字符  就是不是很理解他那个%d输出的原理

user: user-0
content: 引用 5 楼 均陵鼠侠 的回复:[quote=引用 4 楼 拜你所赐 的回复:][quote=引用 3 楼 均陵鼠侠 的回复:]这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。   您好，非常感谢您的回复，我使用的编译器是VS2012。我是一个C语言的初学者，我对您的回复还有一些疑问，在您的回复中说到“在197和198这两个数值的二进制形式中，符号位似乎并不是1。”197 -->11000101      198  -->  11000110    直接转化成二进制的首位是否会直接成为符号位呢？烦请您帮忙把197到-59的过程详细说一下吗？[/quote]  对，我这两句话没说明白。  赋给 c1 和 c2 的值来自于常量 197 和 198 ，这两个常量的类型是 int 。所以，它们的最高位不是1，而是0。即，如果 int 类型的长度是 32 位，则 197 的二进制形式为 00000000000000000000000011000101 。把它截断为 char 类型的长度时，如果 char 类型的长度是 8，则截断后是 11000101 。[/quote]十分感谢您的解答，还有一个问题我没有搞明白，例如197.对于截断后的二进制11000101。它的最高位是1，即后面表示数值的二进制是“1000101”，转变成二进制之后为69。但是取其补码之后即为“0111010”它的十进制是“58”。截断之后为什么不是直接输出它的十进制而是取其补码呢？这点我有点搞不明白。

user: user-5
content: 百度“负数、补码” %c问号的原因是因为找不到对应的ASCII字符

user: user-2
content: 引用 4 楼 拜你所赐 的回复:[quote=引用 3 楼 均陵鼠侠 的回复:]这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。   您好，非常感谢您的回复，我使用的编译器是VS2012。我是一个C语言的初学者，我对您的回复还有一些疑问，在您的回复中说到“在197和198这两个数值的二进制形式中，符号位似乎并不是1。”197 -->11000101      198  -->  11000110    直接转化成二进制的首位是否会直接成为符号位呢？烦请您帮忙把197到-59的过程详细说一下吗？[/quote]  对，我这两句话没说明白。  赋给 c1 和 c2 的值来自于常量 197 和 198 ，这两个常量的类型是 int 。所以，它们的最高位不是1，而是0。即，如果 int 类型的长度是 32 位，则 197 的二进制形式为 00000000000000000000000011000101 。把它截断为 char 类型的长度时，如果 char 类型的长度是 8，则截断后是 11000101 。

user: user-0
content: 引用 3 楼 均陵鼠侠 的回复:这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。   您好，非常感谢您的回复，我使用的编译器是VS2012。我是一个C语言的初学者，我对您的回复还有一些疑问，在您的回复中说到“在197和198这两个数值的二进制形式中，符号位似乎并不是1。”197 -->11000101      198  -->  11000110    直接转化成二进制的首位是否会直接成为符号位呢？烦请您帮忙把197到-59的过程详细说一下吗？

user: user-2
content: 这是一个简单而又复杂的问题。  首先， %c 和 %d 都要求对应的变参是 int 类型。唯一不同的是，%c 是将这个 int 类型的变参转换为 unsigned char 类型的字符，然后予以显示，而 %d 则是将它转换为有符号十进制格式的数字字符予以显示。  其次，程序是没有问题的。尽管传入的变参是 char 类型的，但依照默认实参转换（default argument conversion）的规则，它是被转换为 int 类型之后传递给 printf 的。  最后，对于 printf 来说， %c 是将传入的值按 int 类型（的长度）取出，然后转换（缩小）为 unsigned char 类型（的长度），将它视为字符编码，然后打印这个字符； %d 是将传入的值按 int 类型（的长度）取出，拆分数位并转换为有符号十进制数字字符打印输出。这里的关键是， %d 要求这个转换过程必须区分数值的正负。这很好办，只需要判断其二进制形式的最高位（符号位）就可以了：为0是正数，为1是负数。然而，在197和198这两个数值的二进制形式中，符号位似乎并不是1。这就回到了开始：在将 char 类型的值转换为 int 类型并传递给 printf 时，是如何转换的？再往前，两个常量197和198的类型都是 int ，将它们赋给变量 c1 和 c2 时，需要从 int 转换为 char ，这个转换过程又是怎样的？  这就涉及到数值转换规则。首先，将一个整数类型的值转换为另一个除_Bool之外的整数类型时，如果这个值能够用新类型来表示，则转换后的值不变。  来看 char c1, c2; c1 = 197; c2 = 198;  在这里，如果 char 等价于 unsigned char ，那么，在主流的机器上，它可以表示的数值范围是 0到255，所以能够容纳，且 printf 不可能打印两个负值。显然，在楼主的环境下，char实际上是signed char。在主流的机器上，它可以表示的数值范围是-127到+128，故无法表示 197 和 198 ，需要转换。  但是，如果新类型是有符号的并且无法表示这个值，转换的结果要依赖于具体的编译器，甚至可能触发信号机制。即，C语言没有规定如何转换。  在很多平台上（显然包括楼主所在的机器环境），是直接把长度为 int 的 197 截断为 char 类型的长度。所以，最终它表示有符号数-59。