为什么printf适用于非终止字符串

我想知道printf()如何确定何时停止打印字符串,即使我没有在字符串末尾放置终止字符? 我用malloc做了一个10字节内存的实验,并在其中放了10个字符,不知何故,printf仍然可以打印出这些字符而不会超出限制,为什么呢?

字符串后面的一个字符很可能是NULL,因此printf在那里停止,此外,在您使用malloced的内存后,非NULL的字符可能不是可打印字符,因此您不会在终端中注意到它们。

是因为你有幸运,而你的malloc字符串的下一个字节是0字节。

您可以通过以下方式确认:

 const char* digits = "0123456789"; char* buff = (char*)malloc(10); memcpy(buff, digits, 10); printf("%s, %d\n", buff, (int)*(buff + 10)); 

你的程序必须打印:

0123456789 0

并且那个0是你没有malloc’ed的NULL,但它就在那里。 请注意,此行为是UNDEFINED,因此您无法信任这些内容。 正如我之前所说,因为你运气不好! 在这种情况下发生的好事是SIGSEGV。

假设您确实使用了malloc 10个字符,并且您确实使用除null(’\ 0’)之外的值设置了每个字符,那么在哪里可以保证您没有分配的字符在内存中紧跟其后是’偶然的?

你可能已经使用了一些足够聪明的函数调用中的一个来将最后一个字符设置为null,即使你传递了足够的信息,可能“set”是非null,但是由于细节很少,我们将永远不会知道

不受限制的字符串往往不会导致小程序出现问题。

在大多数OS /处理器上,malloc将分配四舍五入为4或8字节的倍数(取决于处理器的内存对齐要求),因此在字符串末尾经常(但不总是)有几个备用字节。

通常,当malloc需要更多内存时,OS会为其分配一个或多个虚拟页面(通常为4k)。 出于安全原因,如果页面最后被不同的进程使用(或者自热复位以来还没有使用过),则必须擦除这些页面。

因此,因为有很多零(在分配区域和后面都有),很有可能非终止字符串不会在启动时或在小型,短期运行的程序中引起问题(具有讽刺意味的是包括大多数测试程序) )但是当malloc重新使用释放的块时会显示出来。

为了防止出现这类问题,开发和测试版本应该使用efence和EF_FILL选项之类的东西来将malloc内存设置为非零值。

类似地,将堆栈初始化为非零值是一个有用的想法,因为 – 在大多数具有VM的机器上 – 堆栈是从4k页面构建的,这些页面在分配给进程之前被擦除。

请注意,即使使用像efence这样的东西,静态变量仍然存在问题 – 整个区域在加载程序时被擦除为零(并且数据再次对齐),因此如果写入静态字符串变量,未终止的字符串可能会被忽略只有一次 – 如果重新使用字符串变量来存储较短的未终止字符串,则只会注意到该问题。

在一个相关的问题上,变量的对齐解释了为什么不为字符串的终止NUL分配足够的空间通常不会被检测到。

字符串中最后一个字节之后的随机垃圾为空。 这是运气。 下次运行程序或连续工作100次时,它可能会失败。 欢迎使用指针错误(它们也很难调试)。

嗯,好吧,把整个MALLOC的东西放在一边,因为PRINTF它只是一个正确的字符串,我知道%d,%x,%s和我们用作格式说明符但是如果仅仅是“C”那就是printf可以输入可变数量的参数的函数。

简单来说,printf是一个特殊的函数,它将字符串视为传递给它的可变数量的CHAR类型参数。

\ n,\ t等或%c,%f等的任何参数都是它的单个字符,并作为特例处理。

 void myprintf(char * frmt,...) { char *p; int i; unsigned u; char *s; va_list argp; va_start(argp, fmt); p=fmt; for(p=fmt; *p!='\0';p++) { if(*p=='%') { putchar(*p);continue; } p++; switch(*p) { case 'c' : i=va_arg(argp,int);putchar(i);break; case 'd' : i=va_arg(argp,int); if(i<0){i=-i;putchar('-');}puts(convert(i,10));break; case 'o': i=va_arg(argp,unsigned int); puts(convert(i,8));break; case 's': s=va_arg(argp,char *); puts(s); break; case 'u': u=va_arg(argp,argp, unsigned int); puts(convert(u,10));break; case 'x': u=va_arg(argp,argp, unsigned int); puts(convert(u,16));break; case '%': putchar('%');break; } } va_end(argp); } char *convert(unsigned int, int) { static char buf[33]; char *ptr; ptr=&buf[sizeof(buff)-1]; *ptr='\0'; do { *--ptr="0123456789abcdef"[num%base]; num/=base; }while(num!=0); return(ptr); } 

希望这会有所帮助,如果它不只是让我知道,我很乐意为你提供帮助:)