strncmp(s1, s2, 0)返回的是未定义行为吗?(即,最后一个参数为零)?

7

从标准中并不清楚strncmp(来自string.h)的作用。

int strncmp(const char *s1, const char *s2, size_t n);

如果第三个参数n0,则应返回。

根据C17标准草案7.24.4.4:

strncmp函数最多比较n个字符(不比较跟随空字符的字符)[...]。

strncmp函数根据指向s1的数组与指向s2的数组的大小关系返回大于零、等于零或小于零的整数。

strncmp(s1, s2, 0)应该返回什么?或者标准对strncmp的最后一个参数为0的情况保持沉默吗?

我的直觉告诉我,返回值为0是最有意义的:

  • 0是最“对称”的答案(负或正的返回值意味着不对称,与未进行任何比较不一致)。
  • 0符合一个模型,即在发现差异之前假定0,进行n次比较或到达字符串结尾。

但上述推理是哲学性的。

标准似乎在技术上并没有宣布关于这种情况的任何内容。我认为最好的方式是:

  • 明确定义结果(如0或者
  • 禁止它。

就其价值而言,使用以下编译器标志时,glibc为我提供了一堆简单测试用例的0(无警告或错误),例如strncmp("abc", "def", 0)

-Wall -Wextra -std=c90 -pedantic
-Wall -Wextra -std=c17 -pedantic
2个回答

8

来自C11标准(7.23.1字符串函数约定)

2 如果一个声明为size_t n的参数指定了函数数组的长度,则在调用该函数时,n可以具有值零。除非在本子句中特定函数的描述中明确说明,否则在这种调用上的指针参数仍应具有有效值,如7.1.4中所述。在这样的调用中,定位字符的函数找不到出现,比较两个字符序列的函数返回零,复制字符的函数复制零个字符。

以下C标准包括C23标准都是相同的。

这是逻辑一致的。参数n指定范围的大小。当n等于0时,这意味着该范围为空。两个空集不能相互大于或小于。它们都是空集,因此彼此相等。


"指定函数数组的长度" -- 真尴尬,我没能找到这个措辞(尽管我确实浏览了一下文档)。但是我不得不说我不喜欢这个措辞:(1)这里涉及到不止一个数组(“the” 意味着上下文唯一性),以及(2)措辞含糊不清。 - Lover of Structure
1
@LoverofStructure FYI:有“7.1.4使用库函数”,非常有用。 - pmor

2
作为一个旁注,查看GLIBC的源代码,strncmp()是:
int
STRNCMP (const char *s1, const char *s2, size_t n)
{
  unsigned char c1 = '\0';
  unsigned char c2 = '\0';

  if (n >= 4)
    {
      size_t n4 = n >> 2;
      do
        {
          c1 = (unsigned char) *s1++;
          c2 = (unsigned char) *s2++;
          if (c1 == '\0' || c1 != c2)
            return c1 - c2;
          c1 = (unsigned char) *s1++;
          c2 = (unsigned char) *s2++;
          if (c1 == '\0' || c1 != c2)
            return c1 - c2;
          c1 = (unsigned char) *s1++;
          c2 = (unsigned char) *s2++;
          if (c1 == '\0' || c1 != c2)
            return c1 - c2;
          c1 = (unsigned char) *s1++;
          c2 = (unsigned char) *s2++;
          if (c1 == '\0' || c1 != c2)
            return c1 - c2;
        } while (--n4 > 0);
      n &= 3;
    }

  while (n > 0)
    {
      c1 = (unsigned char) *s1++;
      c2 = (unsigned char) *s2++;
      if (c1 == '\0' || c1 != c2)
        return c1 - c2;
      n--;
    }

  return c1 - c2;
}

以上代码显示返回。如果参数为<0>,则函数将返回<'\0' - '\0'> = <0>。

2
这是一个“语言律师”问题,意味着答案应该由标准的语言来确定。特定实现的行为并不能证明标准所要求的内容,除非这些行为可以揭示歧义或解释。实现通常会扩展 C 标准所要求的行为,因此实现提供某些行为并不表示标准要求它。 - Eric Postpischil
4
@EricPostpischil 我完全同意你的观点。这就是为什么我在我的回答中用“顺便说一下”开始:提供实现示例。顺便说一下,这也证实了OP给出的一些GLIBC示例(他问题的最后一部分) 。 - Rachid K.

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