我编写了以下代码以更好地理解strnlen的行为:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char bufferOnStack[10]={'a','b','c','d','e','f','g','h','i','j'};
char *bufferOnHeap = (char *) malloc(10);
bufferOnHeap[ 0]='a';
bufferOnHeap[ 1]='b';
bufferOnHeap[ 2]='c';
bufferOnHeap[ 3]='d';
bufferOnHeap[ 4]='e';
bufferOnHeap[ 5]='f';
bufferOnHeap[ 6]='g';
bufferOnHeap[ 7]='h';
bufferOnHeap[ 8]='i';
bufferOnHeap[ 9]='j';
int lengthOnStack = strnlen(bufferOnStack,39);
int lengthOnHeap = strnlen(bufferOnHeap, 39);
printf("lengthOnStack = %d\n",lengthOnStack);
printf("lengthOnHeap = %d\n",lengthOnHeap);
return 0;
}
请注意两个缓冲区中故意缺少空终止。 根据文档,似乎长度应该 都是39:
返回值 strnlen()函数返回strlen(s),如果小于maxlen,则返回 maxlen如果第一个maxlen字符中没有空终止('\ 0') s指出。
这是我的编译行:
$ gcc ./main_08.c -o main
输出:
$ ./main
lengthOnStack = 10
lengthOnHeap = 10
这里发生了什么?谢谢!
答案 0 :(得分:3)
其次,您正在阅读数组的末尾。数组边界外的内存是未定义的,因此无法保证它不为零;在这个例子中,它是!
一般来说,这种行为是草率的 - 请参阅this answer以了解潜在后果的详细摘要
答案 1 :(得分:3)
首先,strnlen()
未由C标准定义;它是POSIX标准功能。
话虽如此,请仔细阅读文档
strnlen()
函数返回s
指向的字符串中的字节数,不包括终止空字节('\ 0'),但最多只能maxlen
。在执行此操作时,strnlen()
仅查看maxlen
处的前s
个字节,而不会超出s+maxlen
。
这意味着,在调用函数时,您需要确保为maxlen
提供的值,对于提供的字符串,数组idexing对[maxlen -1]
有效,即< em> string 中至少有maxlen
个元素。
否则,在访问字符串时,您将冒险进入未分配给您的内存位置(数组越界访问),特此调用 undefined behaviour
请记住,此函数用于计算数组的长度,上限为值maxlen
)。这意味着,提供的数组至少等于或大于边界,而不是相反。
[脚注]:
根据定义,字符串以空值终止。
引用C11
,章节§7.1.1,术语定义
字符串是由第一个空值终止并包含第一个空值的连续字符序列 字符。 [...]
答案 2 :(得分:1)
您的问题大致相当于以下内容:
我知道防盗警报应该可以防止你的房子被抢劫。今天早上离开家时,我关掉了防盗报警器。在我离开的那天的某个时候,一个窃贼闯入并偷走了我的东西。这是怎么发生的?
或者对此:
我知道您可以使用汽车上的巡航控制来帮助您避免获得超速罚单。昨天我在一条速度限制为65的道路上行驶。我将巡航控制设置为95.一名警察拉我过来,我得到了一张超速罚单。这是怎么发生的?
实际上,这些都不对。这是一个更人为的比喻:
我住在一条有10码长的车道通往街道的房子里。我训练了我的狗来取我的报纸。有一天,我确定车道上没有报纸。我把我的狗放在一个39码的皮带上,我告诉他要取新闻报道。我希望他能在39码外的皮带末端走。但相反,他只走了10码,然后停了下来。这是怎么发生的?
当然有很多答案。也许,当你的狗走到你的无报纸车道尽头时,他立即在排水沟里发现了别人的报纸。或者,也许,当皮带未能阻止他在车道尽头并继续进入街道时,他被车撞倒了。
将你的狗放在皮带上的目的是将他限制在一个安全区域 - 在这种情况下,你控制的财产。如果你把他放在一条长长的皮带上,以至于他可以走到街上,或者进入树林里,你可以通过把他放在皮带上来挫败控制他的目的。
同样地,strnlen
的整个要点是,如果在您定义的缓冲区内没有strnlen
找到的空字符,则表现得很优雅。
非空终止字符串的问题是像strlen
这样的函数(它盲目地搜索空终止符)从最后开始航行并在未定义的内存中盲目搜索,拼命地试图找到终结符。例如,如果你说
char non_null_terminated_string[3] = "abc";
int len = strlen(non_null_terminated_string);
行为未定义,因为strlen
结束了。解决此问题的一种方法是使用strnlen
:
char non_null_terminated_string[3] = "abc";
int len = strnlen(non_null_terminated_string, 3);
但是如果你把更大的数字交给strnlen
,它就会打败整个目的。你回想起当strnlen
结束时会发生什么,并且没有办法回答这个问题。
答案 3 :(得分:0)
当...... &#34; Undefined behaviour (UB)&#34; 时会发生什么?
“当编译器遇到[给定的未定义构造]时,让恶魔飞出你的鼻子是合法的”
您的标题实际上不是 UB ,因为调用strnlen("hi", 5)
是完全合法的,但您问题的细节显示它确实是UB ...... < / p>
strlen
和strnlen
都需要一个字符串,即一个以空字符结尾的char
序列。向函数提供非空终止的char
数组 UB 。
在您的情况下,该功能会读取前10个char
,找不到'\0'
,因为它没有越界它继续读取,并通过调用 UB (读取未分配的内存)。可能是你的编译器冒昧地使用'\0'
结束你的数组,可能是'\0'
之前存在......可能性仅受编译器设计者的限制。