检查char*指针是否为以空字符结尾的字符串的便携式方法

7

我有一个接受char*指针的C函数。其中一个函数的前提条件是指针参数必须是以空字符结尾的字符串。

void foo(char *str) {
    int length = strlen(str);
    // ...
}

如果str不是指向以null结尾的字符串的指针,那么strlen会崩溃。有没有一种可移植的方法来确保char*指针确实指向以null结尾的字符串?
我考虑使用VirtualQuery来查找str之后最低的不可读地址,如果在str的开头和该地址之间没有看到null终止符,则str不指向以null结尾的字符串。

2
我认为没有一种可移植的方式。 - Yu Hao
3
一种方法是在指针中接收第二个参数“size”,然后迭代至size-1元素以寻找空值。 - Sourav Ghosh
4
这听起来像是一个XY问题。你为什么需要这样的功能? - Jabberwocky
2
当你使用 VirtualQuery 时,你已经抛弃了可移植性。 - Yu Hao
5个回答

12

不,没有一种便携式的方式来做到这一点。一个以null结尾的字符串可以是任意长(最多SIZE_MAX字节),同样,一个没有以null结尾的char数组也可以是任意长的。如果函数接受一个char*参数,那么它无法知道它指向的有效内存块有多大(如果有的话)。一个检查操作需要遍历内存直到找到null字符,这意味着如果数组中没有null字符,它将超出其末尾,导致未定义的行为。

这就是为什么标准C库函数在接受字符串指针作为参数时会产生未定义的行为,如果该参数不指向字符串。(检查NULL指针很容易,但这只能捕获一种错误情况,代价是对有效参数执行更慢的代码)

编辑:回应你问题的标题:

检查char*指针是否为以null结尾的字符串的可移植方法

指针不能成为一个字符串。它可能是指向字符串的指针,也可能不是。


2
最好的方法是使用strn函数来限制字符串的大小上限。因此,如果您正在编写一个库调用并且不信任调用者,请记录您的调用注意事项,指出字符串不能超过特定合理大小,并进行检查:
#define MAXNAME 32

if (strnlen(sketchyName,MAXNAME)==MAXNAME) return ERROR;

2
其他答案是正确的,但这里有另一种思考方式。
如果指针指向一个包含n个char的缓冲区,其中没有一个是'\0',那么一旦你试图检查第n+1个字符,你就进入了未定义行为的领域。因此,为了扫描是否有'\0',仅知道缓冲区结束位置的某个上限是不够的,你必须确切地知道缓冲区的结束位置。
C语言没有提供一种方法来知道这一点,除了要求调用者提供它给你。VirtualQuery(假设它是可移植的)并不足够,因为缓冲区后面可能会有其他对象。虽然它在许多实现中似乎可以工作,但你依赖于未定义的行为意味着它必然是不可移植的。

2
为了证明字符串的空终止,你不仅需要证明存在一个null字符,还需要证明它存在于恰当的位置(不能晚也不能早)。要做到这一点,您需要知道字符串的预期内容或至少长度,此时非常容易进行验证...
例如考虑没有虚拟内存的设备:这意味着您可以在整个地址空间上进行迭代而不会触发任何中断。
如果您的栈位于比堆更高的地址,并且编译器将'\0'的副本放在栈上(而不仅仅是将其保留在寄存器中或使用它作为立即值),那么您突然可以保证堆上的任何字符串都是弱零终止的,因为您始终可以将验证代码放在栈上的'\0'视为零终止符。

0

正如其他人所指出的那样,没有可移植的方法来实现这一点。原因是它没有用处。

正常的语义是仅检查NULL,并假设如果传递了非NULL,则它是有效的。毕竟,在您的指针之后可能会有一个NULL 某个地方。唯一的其他可能性是遇到未映射的内存。然而,即使使用虚假指针,您也更有可能找到一个NULL。这意味着虚假的2000个字符的字符串仍将通过检查。


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