【问题标题】:Why strcmp returns int but not char?为什么 strcmp 返回 int 而不是 char?
【发布时间】:2015-05-27 16:10:01
【问题描述】:

据我所知,charint 等变量类型之间的唯一区别是它们占用的内存量。我猜他们在调节他们持有的变量代表什么方面没有任何作用。如果这是真的,在here 中,我看到了strcmp 的以下内容:

strcmp 函数将字符串 s1 与 s2 进行比较,返回一个 与第一个之间的差具有相同符号的值 不同的字符对(解释为 unsigned char 对象, 然后提升为 int)。

我想问为什么结果会提升为int?由于正在比较chars,因此它们的差异在所有情况下都适合char。那么不是将结果提升到int 只是在结果末尾附加一堆 0 吗?那么,为什么要这样做呢?

【问题讨论】:

  • 为什么会返回char?它返回字符串之间的差异。
  • 是的,但它不会在看到第一个不匹配的字符时停止吗?那么结果必须适合一个字符。有错吗?
  • 两个 8 位字符之间的差异将在 -255 和 255 之间,并且该范围不适合一个字符。
  • @wonkorealtime 我知道。我只是指出,假设两个字符之间的差异可以存储在一个字符中,OP 是错误的。

标签: c++ c


【解决方案1】:

char 可以签名也可以不签名。 strcmp 必须返回有符号类型,这样如果差为负,则可以为负。

更一般地,int 更适合用于传递和返回简单数值,因为它被定义为此类值的“自然”大小,并且在某些平台上,它比较小的类型更有效地处理。

【讨论】:

  • 关于表示负值的要求:我猜该函数可能已经返回,例如,0 表示负数,1 表示相等,2 表示更大,因为据我所知,该函数没有给出关于返回值的任何保证将代表第一对不匹配字符之间的差异。如果是这种情况,该函数可能已返回 char 对吗?所以我想知道是否还有更根本的原因?
  • @Utku:是的,它可以以不同的方式对结果进行编码,并且不需要签名类型。但无论如何它可能会返回int,因为这是函数返回值的自然大小。并且编码可能比if (c1 != c2) return c1 - c2;的明显实现效率低
  • @Utku 在数学上a < b implies strcmp(a,b) < 0a < b implies strcmp(a,b) = 1 更可靠
  • “自然大小”int 是 CPU 设计的结果(请参阅stackoverflow.com/questions/2331751/…)。编译器编写者普遍尊重的 CPU 设计考虑的一个方面是“字对齐”(参见stackoverflow.com/questions/381244/purpose-of-memory-alignment)。这种对“字对齐”的考虑将指导“堆栈框架”的实现(参见stackoverflow.com/questions/10057443/…)。
  • 因此,为了提高 CPU 效率和可靠编译(更不用说根据平台实现或编译器设置避免任何意外升级或非升级的机会),库编写者明确将结果返回到int。无论如何,这将是我的猜测。
【解决方案2】:

当然,尽管其他人提到了溢出的可能性,它只 需要 能够返回,例如-1、0 或 1 - 很容易放入有符号字符中。造成这种情况的真正历史原因是,在 1970 年代 C 的原始版本中,函数无法返回一个 char,而任何这样做的尝试都会返回一个 int。

在这些早期的编译器中,int 也是默认类型(许多情况,包括下面 main 中看到的函数返回值,允许您在不实际使用 int 关键字的情况下将某些内容声明为 int),因此定义任何不需要特别返回与返回 int 不同的类型的函数。

即使是现在,char 返回也只是简单地将值符号扩展到 int 返回寄存器(pdp11 上的 r0,x86 上的 eax),无论如何。将其视为 char 不会有任何性能优势,而允许它成为实际差异而不是将其强制为 -1 或 1 确实具有很小的性能优势。 axiac 的回答也说明了一个很好的观点,即对于比较运算符,无论如何都必须将其提升回 int。这些提升的原因也是历史性的,顺便说一句,编译器不必为 char 和 int 的每种可能组合实现单独的运算符,特别是因为许多处理器上的比较指令无论如何都只适用于 int。


证明:如果我在 Unix V6 上为 PDP-11 编写一个测试程序,char 类型会被静默忽略,并返回一个超出范围的整数值:

char foo() {
    return 257;
}

main() {
    printf("%d\n", foo());
    return 0;
}

# cc foo.c
# a.out
257

【讨论】:

  • ...为什么你有一个 PDP-11 可以用来测试东西?
  • @immibis 当然是模拟的。 V7 确实处理得当;但我的观点是,转换它需要额外的指令,还要确保正确处理超出范围的值,并且返回一个字符并没有真正的好处。
【解决方案3】:

AFAIK,标准 C 库没有一个函数可以接受或返回 char 类型的值。它具有char*const char* 类型的参数和返回类型,但不是普通的char

例如在int isalpha(int c); 寻找一个更令人震惊的例子。

我不知道为什么,但我能猜到。也许是由于 ABI。在我知道的任何 ABI 中,char 类型的任何参数或返回值无论如何都会在内部提升为 int,所以这样做没有意义。它实际上会使代码 less 高效,因为您需要在每次使用函数时进行截断。

【讨论】:

  • @interjay:这可能适用于getchar,但对于isalpha 是没有用的。看起来他们说:“看,我们可以在这里接受 EOF 并输入 isalpha(getchar())”。
  • 这实际上比无用更糟糕,因为它使将char 传递给isalpha 错误(您需要先将其转换为unsigned char)。
  • @interjay:如果它是 char-as-char,你只需要转换它。如果你有一个适当的 char-as-integer,那很好:'char x=getchar(); isalpha(x);`错了! int x=getchar(); isalpha(x);" ok! Curiously, isalpha('ñ')` 在 C 中可以,但在 C++ 中未定义!
  • @rodrigo 我认为这不是真的 - 'ñ' 是 C 中的一个 int,是的,但它仍然具有相同的负值。
  • @Random832:嗯,我刚刚检查过,你是对的。现在我想知道为什么使用 GCC 和 Latin-1 编码,'ñ'-15 而不是 241
【解决方案4】:

strcmp() 将其返回的值提升为int 的一个可能原因是在调用代码中保留了一条处理器指令。

通常(总是?)strcmp() 返回的值与comparison operator 一起使用。

让我们看看比较运算符的操作数会发生什么。

常用算术转换

以下算术运算符的参数经过隐式转换以获得公共实数类型,即执行计算的类型:

  • 二进制算术*,/,%,+,-
  • 关系运算符<><=>===!=
  • 二进制位算术&, ^, |
  • 条件运算符?:

...

4) 否则,两个操作数都是整数。在这种情况下,

首先,两个操作数都经过整数提升。
...

(来源:http://en.cppreference.com/w/c/language/conversion#Usual_arithmetic_conversions

整数促销

整数提升是将等级小于或等于 int 等级或 _Bool、int、signed int、unsigned int 类型位字段的任何整数类型的值隐式转换为 int 类型的值或unsigned int

(来源:http://en.cppreference.com/w/c/language/conversion#Integer_promotions

返回strcmp()

从上面的引用中可以看出,strcmp() 返回的可能的char 值无论如何都会提升为int

为什么 C 的创建者选择返回 int

出于一个非常简单的原因:因为促销无论如何都会发生并且因为(至少)需要一条处理器指令来执行促销,所以将这条指令添加到 strcmp() 的代码中更方便(即在一个地方)而不是在任何地方调用strcmp() 函数。

在 70 年代,内存和 CPU 都是非常宝贵的资源。现在看起来微不足道的优化(在代码中可能在几十个地方保存了几个字节的内存)在当时更为重要。

更新:

再想一想,我认为this answerthis answer提供的历史原因比我的更准确。

【讨论】:

  • 所以,原因是算术运算符总是对 int 进行操作,而算术运算符的操作数的任何其他类型总是隐式转换为 int?
  • @Utku 本身并不总是,只有当它是 char 或 short 时。
猜你喜欢
  • 2013-09-23
  • 1970-01-01
  • 1970-01-01
  • 2021-10-12
  • 2015-03-03
  • 1970-01-01
  • 2011-08-20
  • 2011-07-01
  • 1970-01-01
相关资源
最近更新 更多