在不同的类似UNIX的操作系统中,如何克服snprintf的不一致行为?
时间:2020-03-06 14:25:23 来源:igfitidea点击:
对于每个手册页,snprintf返回从glibc 2.2版开始写入的字节数。但是在较低版本的libc2.2和HP-UX上,它返回一个正整数,这可能导致缓冲区溢出。
如何克服这一难题并编写可移植的代码?
编辑:为了更清晰
这段代码在lib 2.3中运行良好
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计算机上打印的字符数。希望这个解释很好。
解决方案
我发现了一种可移植的方法来预测和/或者限制sprintf和相关函数返回的字符数,但是这种方法效率低下,并且许多人认为它不雅致。
我们要做的是使用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; } }
我们是否考虑过printf的可移植实现?我前一阵子找了一个,然后决定住三人间。
http://daniel.haxx.se/projects/trio/
问题仍然不清楚。链接到的手册页这样说:
The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing 'int ret = snprintf(cmd, cmdLen + 1, ". %s%s", myVar1, myVar2 ) == -1) if(ret == -1 || ret > cmdLen) { //output was truncated } else { //everything is groovy }'). If the output was truncated due to this limit then the return value is the number of characters (not including the trailing '##代码##') which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated.
因此,如果我们想知道输出是否被截断,请执行以下操作:
##代码##请改用高级得多的asprintf()。
这是一个GNU扩展,但是如果它本身不可用,则值得复制到目标平台。
- printf可移植性存在很多问题,实际上,我们可能希望遵循以下三种方法之一:
- 需要一个符合c99的* printf,因为对于任何人来说9年就足够了,并且只说该平台已损坏即可。
- 对于要支持所有特定平台的特定平台,有一个带有#ifdef的my_snprintf(),并在其下调用所有vsnprintf()(了解最低的公分母是我们拥有的)。
- 只需在代码中随身携带vsnprintf()的副本,就简单的用例而言,它实际上非常简单;对于其他用例,我们可能想看一下vstr,我们将免费获得客户格式化程序。
...就像其他人建议的那样,仅针对-1情况,我们可以合并#1和#2,但是这样做有风险,因为c99 * printf在某些情况下可以/确实返回-1.
就个人而言,我建议仅使用类似于ustr的字符串库,该库可以为我们提供简单的解决方法,并为我们免费提供托管字符串。如果我们真的很在意,可以将其与vstr结合使用。