C 如何克服snprintf在不同类UNIX操作系统中的不一致行为?
根据手册页,snprintf返回从glibc版本2.2开始写入的字节数。但在libc2.2和HP-UX的较低版本上,它返回一个正整数,这可能导致缓冲区溢出 如何克服这一点并编写可移植代码 编辑:为了更加清晰 这段代码在lib2.3中运行良好C 如何克服snprintf在不同类UNIX操作系统中的不一致行为?,c,cross-platform,buffer-overflow,buffer-overrun,C,Cross Platform,Buffer Overflow,Buffer Overrun,根据手册页,snprintf返回从glibc版本2.2开始写入的字节数。但在libc2.2和HP-UX的较低版本上,它返回一个正整数,这可能导致缓冲区溢出 如何克服这一点并编写可移植代码 编辑:为了更加清晰 这段代码在lib2.3中运行良好 if ( snprintf( cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) != cmdLen ) { fprintf( stderr, "\nError: Unable to c
if ( snprintf( cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) != cmdLen )
{
fprintf( stderr, "\nError: Unable to copy bmake command!!!");
returnCode = ERR_COPY_FILENAME_FAILED;
}
在Linux上,它返回字符串(10)的长度。但同一代码返回的正数大于HP-UX机器上打印的字符数。希望这个解释很好。 < P>我已经找到了一种预测和/或限制SaveTf和相关函数返回的字符数的便携式方法,但是效率很低,很多人认为它不优美。 您要做的是创建一个临时文件,将tmpfile()、fprintf()设置为该临时文件(可靠地返回写入的字节数),然后将全部或部分文本倒带并读取到缓冲区中 例如:
int my_snprintf(char *buf, size_t n, const char *fmt, ...)
{
va_list va;
int nchars;
FILE *tf = tmpfile();
va_start(va, n);
nchars = vfprintf(tf, fmt, va);
if (nchars >= (int) n)
nchars = (int) n - 1;
va_end(va);
memset(buf, 0, 1 + (size_t) nchars);
if (nchars > 0)
{
rewind(tf);
fread(buf, 1, (size_t) nchars, tf);
}
fclose(tf);
return nchars;
}
您可以创建一个snprintf包装器,当缓冲区中没有足够的空间时,该包装器为每种情况返回-1 有关更多文档,请参阅。它也有一个威胁所有案件的例子
while (1) {
/* Try to print in the allocated space. */
va_start(ap, fmt);
n = vsnprintf (p, size, fmt, ap);
va_end(ap);
/* If that worked, return the string. */
if (n > -1 && n < size)
return p;
/* Else try again with more space. */
if (n > -1) /* glibc 2.1 */
size = n+1; /* precisely what is needed */
else /* glibc 2.0 */
size *= 2; /* twice the old size */
if ((np = realloc (p, size)) == NULL) {
free(p);
return NULL;
} else {
p = np;
}
}
while(1){
/*尝试在分配的空间中打印*/
va_启动(ap、fmt);
n=vsnprintf(p、尺寸、fmt、ap);
va_端(ap);
/*如果有效,则返回字符串*/
如果(n>-1&&n-1)/*glibc 2.1*/
大小=n+1;/*正是所需的*/
else/*glibc 2.0*/
尺寸*=2;/*旧尺寸的两倍*/
if((np=realloc(p,size))==NULL){
自由基(p);
返回NULL;
}否则{
p=np;
}
}
您是否考虑过printf的可移植实现?我前段时间找了一个,最后选择了三人组
你的问题还不清楚。与之相关的人这样说: 函数snprintf()和vsnprintf()的写入长度不超过大小字节(包括 尾随的“\0”)。如果由于此限制而截断了输出,则返回值为字符数(不包括尾随“\0”),如果有足够的可用空间,将写入最终字符串。因此,大小大于等于的返回值意味着输出被截断。 因此,如果您想知道输出是否被截断:
int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1)
if(ret == -1 || ret > cmdLen)
{
//output was truncated
}
else
{
//everything is groovy
}
请改用更高级的asprintf()
这是一个GNU扩展,但如果它本机不可用,则值得复制到目标平台。在*printf可移植性方面存在大量问题,实际上,您可能希望遵循以下三种途径之一:
就我个人而言,我建议只使用,它为您提供简单的解决方法,并免费提供托管字符串。如果你真的在乎,你可以结合。你能澄清一下你的问题吗?目前尚不清楚返回值类型如何导致缓冲区溢出。这不起作用,因为当前有效的snprintf()会在出现错误时返回-1。当它分配两倍于旧大小(/*glibc 2.0*/comment)时,会处理-1情况。printf函数有两种行为:1。它返回-1,告诉您缓冲区不够大2。它返回所需的字节数(您需要进行比较以确定是否正确),asprintf()实际上并不好,因为它根本不是标准的,而且行为也不一样。另外,您得到的不是一个有用的管理字符串,如ustr_dup_fmt()等。