sizeof('a') == sizeof(char) == 1
。这个很容易理解,因为'a'
是一个字符字面值,而sizeof(char) == 1
是由标准定义的。但在C语言中,
sizeof('a') == sizeof(int)
。也就是说,C语言中的字符字面值实际上是整数。有没有人知道为什么呢?我能找到很多关于这个C语言怪癖的提及,但没有解释它存在的原因。sizeof('a') == sizeof(char) == 1
。这个很容易理解,因为'a'
是一个字符字面值,而sizeof(char) == 1
是由标准定义的。sizeof('a') == sizeof(int)
。也就是说,C语言中的字符字面值实际上是整数。有没有人知道为什么呢?我能找到很多关于这个C语言怪癖的提及,但没有解释它存在的原因。关于同一主题的讨论
"更具体地说是整数提升。在K&R C中,几乎不可能使用字符值而不首先将其提升为int,因此在第一次将字符常量设置为int时,就可以消除这一步骤。仍然存在多字符常量,例如'abcd'或适合int的任意数量."
char
变量不是int
,所以使字符常量成为int
是一种特殊情况。而且很容易在不提升它的情况下使用字符值:c1 = c2;
。但是,c1 = 'x'
是一个向下转换。最重要的是,sizeof(char) != sizeof('x')
,这是一种严重的语言错误。至于多字节字符常量:它们是原因,但已经过时了。 - Jim Balter最初的问题是“为什么?”
原因在于字面字符的定义已经演变和改变,而同时又试图保持与现有代码的向后兼容性。
在早期C语言的黑暗时代,根本没有任何类型。当我第一次学习C语言编程时,已经加入了类型,但函数没有原型来告诉调用者参数类型。相反,规定所有传递的参数都会作为int的大小(包括所有指针),或者它将是一个double。
这意味着,在您编写函数时,所有不是double的参数都将以int的方式存储在堆栈中,无论您如何声明它们,编译器都会为您处理这些代码。
这使得事情有些不一致,因此当K&R编写他们著名的书时,他们制定了规则,即在任何表达式中,字符字面值将始终提升为int,而不仅仅是作为函数参数。
当ANSI委员会首次标准化C语言时,他们更改了这个规则,因此字符字面值将简单地成为int,因为这似乎是实现相同目标的更简单方法。
在设计C++时,要求所有函数具有完整的原型(尽管这在C语言中仍不是必需的,但被普遍认为是良好实践)。正因为如此,决定将字符字面值存储在char中。在C++中的优点是,带有char参数的函数和带有int参数的函数具有不同的签名。这种优势在C语言中并不适用。
这就是它们不同的原因。演化...
void f(unsigned char)
和 void f(signed char)
。 - Peter Kf('a')
,你很可能希望重载决议为该调用选择 f(char)
而不是 f(int)
。正如你所说的那样,int
和 char
的相对大小并不相关。 - zwol我不知道为什么在C中,字符字面量的类型是int。但在C++中,有一个很好的理由不采用这种方式。考虑以下情况:
void print(int);
void print(char);
print('a');
你会期望调用print函数选择第二个版本,即使用char类型作为参数。但是,如果字符字面值是int类型,这将变得不可能。请注意,在C++中,具有多个字符的字面值仍然具有int类型,尽管它们的值是实现定义的。因此,'ab'
的类型是 int
,而 'a'
的类型是 char
。
在我的 MacBook 上使用 gcc,我尝试执行以下操作:
#include <stdio.h>
#define test(A) do{printf(#A":\t%i\n",sizeof(A));}while(0)
int main(void){
test('a');
test("a");
test("");
test(char);
test(short);
test(int);
test(long);
test((char)0x0);
test((short)0x0);
test((int)0x0);
test((long)0x0);
return 0;
};
运行时会产生以下结果:
'a': 4
"a": 2
"": 1
char: 1
short: 2
int: 4
long: 4
(char)0x0: 1
(short)0x0: 2
(int)0x0: 4
(long)0x0: 4
在C语言诞生之时,PDP-11的MACRO-11汇编语言具备了以下特点:
MOV #'A, R0 // 8-bit character encoding for 'A' into 16 bit register
MOV #"AB, R0 // 16-bit character encoding for 'A' (low byte) and 'B'
这提供了一种方便的方式,将两个字符加载到16位寄存器的低字节和高字节中。然后,您可以将它们写入其他位置,更新一些文本数据或屏幕内存。
因此,字符被提升为寄存器大小的想法非常正常和可取。但是,假设您需要将'A'放入寄存器中,而不是作为硬编码操作码的一部分,而是从包含以下内容的主存储器的某个位置获取:
address: value
20: 'X'
21: 'A'
22: 'A'
23: 'X'
24: 0
25: 'A'
26: 'A'
27: 0
28: 'A'
int r;
char buffer[1024], *p; // don't use in production - buffer overflow likely
p = buffer;
while ((r = getc(file)) != EOF)
{
*(p++) = (char) r;
}
int
定义为机器的本地字长,并且任何小于 int
的值都需要被拓展到 int
大小才能被传递给函数、从函数中返回或在按位、逻辑或算术表达式中使用,因为这是底层硬件工作的方式。int
小的数据类型都会被提升为 int
。出于类似的历史原因,C 编译器也可以使用补码运算以代替二进制补码运算。八进制字符转义和常量是第一类公民,而相比之下十六进制并不如此,也是因为那些早期的 DEC 迷你计算机具有可被三字节块整除而不是四位半字节的字长。char
恰好是3个八进制数字长。 - Antti Haapala -- Слава Україні我不确定,但我猜这样实现起来更容易,而且并不重要。直到C++出现后,类型才能决定调用哪个函数,这时才需要修复它。
这是正确的行为,称为“整数提升”。在其他情况下也可能发生(主要是二元运算符,如果我记得正确的话)。
编辑:为了确保,我检查了我的《Expert C Programming: Deep Secrets》副本,并确认字符字面量不以类型int开头。它最初是char类型,但当它在表达式中使用时,它会被提升为int类型。以下摘自该书:
字符字面量的类型为int, 它们通过遵循从char类型到int类型的提升规则而获得。 这在K&R 1的第39页上过于简略地介绍了: 表达式中的每个char都转换为int....请注意, 表达式中的所有float都转换为double....由于函数参数是一个表达式, 当参数传递给函数时也进行类型转换:特别是,char和short变成int, float变成double。
整数字符常量的类型为int
”。要么“Deep Secrets”是错误的,要么你只是误解了它。 - Lundin