`memchr()`函数为什么使用`char`类型的参数时要用`int`类型?

7
以下函数使用int作为第二个参数类型,
memchr(const void *buf, int ch, size_t count);

虽然它用于字符类型。为什么函数定义要使用int作为char类型的参数?这是否有特殊原因?


2
你可能也想阅读这篇文章,它基本上是相同的问题。https://dev59.com/m2025IYBdhLWcg3wl3Am - tangrs
4个回答

8
这是因为这是一个非常“古老”的标准函数,它存在于C语言演变的早期时期。
旧版本的C语言没有像“函数原型”这样的东西。函数要么被留空声明,要么使用“未知”的参数列表进行声明,例如:
void *memchr(); /* non-prototype declaration */

调用这种函数时,所有参数都会自动进行“参数提升”,这意味着这些函数永远不会接收到类型为“char”或“short”的参数值。这样的参数总是被调用者隐式提升为“int”类型,实际上函数本身接收的是一个“int”。(对于没有原型声明的函数,即像上面展示的那样声明的函数,在现代C语言中仍然适用。)
随着C语言发展到引入了“原型”函数声明的地步,重要的是将新的声明与标准函数的传统行为以及已编译的遗留库对齐。
这就是为什么您永远不会在遗留函数声明的参数列表中看到这些类型,例如“char”或“short”。出于同样的原因,您也不会在那里使用“float”类型。
这也意味着,如果由于某种原因必须为某个以K&R风格定义的现有遗留函数提供原型声明,则必须记住在原型中指定已提升的参数类型。例如,对于定义为:
int some_KandR_function(a, b, c)
char a;
short b;
float c;
{
}

正确的原型声明实际上是:
int some_KandR_function(int a, int b, double c);

但不包括

int some_KandR_function(char a, short b, float c); // <- Incorrect!

1
所有涉及字符的标准函数都这样做。我认为原因部分是历史原因(在某些非标准版本的C中,函数不能使用char或unsigned char参数,就像varargs参数不能具有字符类型一样),部分是为了保持所有此类函数的一致性。
有一些字符处理函数必须使用int才能允许EOF的可能性,但memchr不是其中之一。

你真的是指“EOL”吗? - Keith Thompson
1
不是,它是 EOF,一个(必然为负数,通常为-1)值,用于在带内信号结束文件。这就是为什么例如 fgetc 返回 int,或者 isalnum 只需要一个:两者都使用 unsigned charEOF,后者不能表示为 unsigned char - Masklinn

0

C语言不会将小于int的参数传递给函数。将函数参数定义为char或short总是升级为int,将unsigned char或unsigned short参数定义为无符号int。
如果无符号与有符号char是问题,他们会使用short(我不记得有一个平台short是1个字节长)。
当然,使用int处理无符号与有符号char的可能性,因为memchr仅比较相等性。


这不是真的。如果你打开godbolt并将默认函数(square)的类型从“int”更改为“char”,你会发现汇编代码从复制DWORD到BYTE,然后有两个“带符号扩展移动”,以便从字节填充扩展寄存器,然后执行“imul”。使用-O也是同样的情况,“int”版本自我乘以edi,而“char”版本将dil(DI的低8位)进行符号扩展复制到EAX中,然后进行自我乘法。 - Masklinn

0

因为 charsigned charunsigned char 是三种不同的类型。在特定的实现中,你不知道 char 是有符号还是无符号的。

除了一些罕见的系统(参见 Keith 的注释),类型 int 包含这三种类型可以拥有的所有值。


在大多数系统上,如果 sizeof (int) == 1(意味着 CHAR_BIT >= 16),那么 int 可能无法容纳所有 unsigned char 类型的值。这样的系统很少见。 - Keith Thompson
@pmg 这就是为什么我用 EOF 完成了 "char、signed char 和 unsigned char"。 - user529758

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