前人挖坑,后人种树。
不能对同一段内存(同一个buf) 或是两个重叠的内存使用snprintf,换句等方面说,在使用snprintf 时,就确保内存不重叠。
先来看看一段问题代码的执行结果。 代码是前人所作,从工程Copy出来后稍做修改,用途是对输入字符串进行排序,输入为0~7的任意组合。
#include <stdio.h>
#include <string.h>
void main()
{
char week[32] = "0,1,2,6,5";
char new_week[32] = {0};
int i = 0;
char *weekday[] ={"0","1","2","3","4","5","6","7"};
int n = 0;
for(i = 0; i < 8; i++)
{
if(strstr(week, weekday[i]))
{
snprintf(new_week,sizeof(new_week),"%s%s,", new_week, weekday[i]);
n += 2;
}
}
if (new_week[n - 1] == ',')
new_week[n - 1] = 0;
printf("week: %s\t; new_week: %s\n", week, new_week);
}
编译如下:
1) 在MIPS(BRCM6838 CPU)平台下运行如下:
结果如预期。
2) 在笔者的主机上运行如下:
并不是所期望的结果, new_weeek 只有最后一位(排序后的)。
3) 在ARM平台下运行如下:
不能对同一段内存(同一个buf) 或是两个重叠的内存使用snprintf,换句等方面说,在使用snprintf 时,就确保内存不重叠。
总结:
我们发现在MIPS平台上,程序运行结果是我们所期望的结果。但是是Intel 和ARM平台上, 程序并非按期望的结果运行。
通过研究发现,不同构架所链接的库文件不同,而不同的库文件对sprintf的实现不同。 在MIPS平台上所UClibc, 在ARM平台上使用的库为 glibc。两者的实现不同。
在glibc中,对snprintf为实现,函数调用关系如下:
即
snprintf (char *s, size_t maxlen, const char *fmt, ...)
{
...
done = __nldbl_vsnprintf (s, maxlen, fmt, arg);
...
}
__nldbl_vsnprintf (char *string, size_t maxlen, const char *fmt, va_list ap)
{
...
res = _IO_vsnprintf (string, maxlen, fmt, ap);
...
}
_IO_vsnprintf (string, maxlen, format, args)
{
...
string[0] = '\0';
...
}
在 _IO_vsnprintf 函数的实现中 中有一行代码为:
string[0] = '\0';
for(i = 0; i < 8; i++) {
...
n += snprintf(new_week + n, sizeof(new_week), "%s,", weekday[i]);
// snprintf(new_week,sizeof(new_week),"%s%s,", new_week, weekday[i]); // TODO: 而不是这样
...
}
用来首先清空目标buf的内容,所以在就出现了如前所示的在主机的ARM平台的运行结果,结果只取得了最后一位。
本文的中的问题代码对的snprintf函数的用法是有误的,即下面的用法是不正确的。
snprintf(new_week,sizeof(new_week),"%s%s,", new_week, weekday[i]);
结论:
不能对同一段内存(同一个buf) 或是两个重叠的内存使用snprintf,换句等方面说,在使用snprintf 时,就确保内存不重叠,否则结果不可预期。
补充:
基于上面的结论有朋友提出这样的问题:那么如何在调用前确定src与dst不重叠。
其实,准确地说,对于 snprintf, 1) 就不能有出现重叠的情况,2) src与dst更不能使用同一段内存。
对于1),snprintf 原型如下,其中第二个参数就是用来确保内存不重叠的,dst所申请内存的大小必须大于或等于(一般取等于)第二个参数maxlen的值。
snprintf (char *s, size_t maxlen, const char *fmt, ...)
对于2) 在本例中,src与dst使用同一段内存,其初衷是为了在原有内存(字符串)追加新的内容(通过把int转化为char),那么对于这种情况的正确用法如下。
for(i = 0; i < 8; i++) {
...
n += snprintf(new_week + n, sizeof(new_week), "%s,", weekday[i]);
// snprintf(new_week,sizeof(new_week),"%s%s,", new_week, weekday[i]); // TODO: 而不是这样
...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)