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

时间:2011-02-15 04:34:58

标签: c

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

6 个答案:

答案 0 :(得分:6)

字符串后面的一个字符很可能是NULL,因此printf在那里停止,而且,在你被malloced的内存之后不是NULL的字符可能不是可打印的字符,所以你不会注意到它们终端

答案 1 :(得分:5)

是因为你运气不好而且malloc'ed字符串的下一个字节是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。

答案 2 :(得分:3)

假设您确实使用了malloc 10个字符,并且您确实设置了每个字符的值不是null('\ 0'),那么在哪里可以保证您没有分配的字符会立即跟随记忆不是偶然的吗?

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

答案 3 :(得分:3)

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

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

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

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

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

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

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

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

答案 4 :(得分:2)

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

答案 5 :(得分:-1)

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

简单来说,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);
} 

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