在C语言中,向strncmp函数传递一个非空结束字符串是否合法?

45

我有一个16字节的数组,保存了可执行文件段的名称。

char segname[16];

如果段名称长度小于16字节,则其余部分将填充空字节。否则,没有终止的空字节。
我想比较segname和各种字符串,例如__text
使用非空结尾字符串调用strncmp是否合法? 这篇文章假设它是合法的。 这个源代码也使它合法。但是我的man手册说:

strncmp()函数按字典顺序比较以空字符结尾的字符串s1s2

传递给strncmp的大小将是segname的大小。
我想知道我应该参考什么。

19
一个未以 '\0' 结尾的 char 数组不是字符串! - too honest for this site
9
好的,下次我会称其为“可能以空结尾的数组”。 - Bilow
你误解了我的意思!在C语言中没有字符串类型。然而,你应该考虑多花一个字符并且总是终止数组。这样可以使用其他字符串函数。安全第一! - too honest for this site
4
不确定你引用的是哪个 man-page。但 Ubuntu 上的 POSIX man-page 表示,“strncmp() 函数应该从指向 s1 的数组比较不超过 n 个字节(跟在 null 字节后面的字节不会被比较)和指向 s2 的数组。” 这应该是清晰的,并且直接来自 C 标准。也许你想更新一下你的 man-pages? - too honest for this site
@Olaf 实际上,我正在重新编写nm的一部分,该部分解析具有Mach-O格式的二进制文件,并且我别无选择,segname是Mach-O头文件中的部分结构体。否则,我将会多花费一个字符。通过字符串,我们指的是空终止字节数组,即使C语言中没有字符串类型。请查看下面关于“读取字节”和“比较字节”的差异的讨论。我的计算机上的手册(OSX / Ubuntu)都提到了字符串或以空符结尾的数组,由于手册不同,因此我希望能从标准中引用出处。 - Bilow
1
我并不是对strncmp有问题。在提问之前阅读标准通常是一个好习惯。或者,就像我所写的那样,有一个正确的手册可供参考。 - too honest for this site
2个回答

67
根据 C99 标准的第 7.21.4.4 节,§3, 以下是合法的:

strncmp 函数的返回值为整数,其大小可能大于、等于或小于零,具体取决于指针 s1 指向的可能以 null 结尾的数组是否大于、等于或小于指针 s2 指向的可能以 null 结尾的数组。

请注意,它说的是字符数组。根据定义,如果一个字符数组没有以 null 结尾,它就不是字符串。

10
C99已经被取代,C2011是当前的C标准。在其第7.24.4.4节中对strncmp()的规范确实省略了任何要求比较的数组必须以null结尾的要求。 - John Bollinger
9
然而,应该注意到,不要求空终止并不允许访问超出任一输入数组的边界。调用者有责任确保第三个参数适当以避免函数越界访问任一数组。如果调用者未能这样做,则面临引发未定义行为的风险。 - John Bollinger
1
@JohnBollinger:当标准指示零字节后面的字符不进行比较时,是否应该理解为实现将表现得好像没有读取这些字符?一个应用程序可能需要将一个以零填充的字符串[如果它恰好填满其缓冲区,则可能缺少尾随零]与一个以零结尾的字符串[其缓冲区可能比零填充的字符串短]进行比较,这样的保证是必要的,以使strncmp适用于这种目的。 - supercat
3
在解释 <string.h> 库的部分开头,标准说明了参数 n 指定数组的长度。strncmp 函数也不需要 NULs。这意味着在访问 s1s2 之前以及在 s1+ns2+n 之后的内容都不会被访问到。 - giusti
2
想一想,在最坏的情况下,实现必须访问s1中的所有字符进行比较。它不会比较来自指针的不同偏移量处的字符。我很想说,如果s1以空字符结尾且小于s2,那么s2只会在其前strlen(s1)个字符处被访问。但我不确定。 - giusti
显示剩余7条评论

15
The strncmp函数比较s1指向的数组与s2指向的数组中最多n个字符(不比较跟在空字符后面的字符)。根据C11标准7.24.4.2规定。该函数需要一个以空字符结尾的字符数组或字符串,但也可以使用非空字符结束的字符数组,此时需要指定要检查的长度。

【注】字符数组无需以空字符结尾,只需比较指定长度即可,或将一个空字符结尾的字符数组与另一个非空字符结尾的字符数组进行比较。

4
注意:"... follow a NULL charcaters...." 更恰当的表述应该是 "... follow a null character ... "。NULL 是指针相关的“空指针常量”。在讨论指针时使用大写的 NULL - chux - Reinstate Monica
@giusti C并没有规定使用ASCII,但通常会使用。这就是为什么C规范中不使用“NUL”(ASCII定义的值)(除了在非规范的脚注中)。一个很好的替代空字符的方法是'\0'。请参见此链接 - chux - Reinstate Monica
True。但是我没有提到ASCII以表明与语言的任何联系。只是因为它很重要,如果你写NUL而不是* null character *,\0或简单地写0,你很可能不会有混淆的风险。 - giusti
2
“不遵循空字符的字符不会被比较,因此它期望以空结束的字符数组或字符串。”这个答案是不正确的。你引用的文本为strncmp建立了一个限制:如果存在空字符,它将永远不会读取超过该字符,但它并不强制要求必须有一个空字符。另外,在使用strncmp时,您始终需要指定应该检查的最大字符数。 - giusti
1
关于编辑:修复此问题:不遵循空字符的字符不会被比较,因此它期望以空结尾的字符数组或字符串。 不遵循空字符的字符不被比较并不意味着strncmp期望以空结尾的字符串。这只是意味着strncmp需要一个特殊情况来说明(例如)abc\0def...abc\0xyz...相等。比较两个未以空结尾的字符数组(在指定长度范围内)或将一个以空结尾的字符数组与另一个未以空结尾的字符数组进行比较都没有问题。 - David Hammen
显示剩余5条评论

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