目录[-]
今天在项目中使用snprintf时遇到一个比较迷惑的问题,追根溯源了一下,在此对sprintf和snprintf进行一下对比分析。
因为sprintf可能导致缓冲区溢出问题而不被推荐使用,所以在项目中我一直优先选择使用snprintf函数,虽然会稍微麻烦那么一点点。这里就是sprintf和snprintf最主要的区别:snprintf通过提供缓冲区的可用大小传入参数来保证缓冲区的不溢出,如果超出缓冲区大小则进行截断。但是对于snprintf函数,还有一些细微的差别需要注意。
snprintf函数的返回值
sprintf函数返回的是实际输出到字符串缓冲中的字符个数,包括null结束符。而snprintf函数返回的是应该输出到字符串缓冲的字符个数,所以snprintf的返回值可能大于给定的可用缓冲大小以及最终得到的字符串长度。看代码最清楚不过了:
1
2
3
4
5
|
char
tlist_3[10] = {0};
int
len_3 = 0;
len_3 = snprintf(tlist_3,10,
"this is a overflow test!\n"
);
printf
(
"len_3 = %d,tlist_3 = %s\n"
,len_3,tlist_3);
|
1
|
len_3 = 25,tlist_3 =
this
is a
|
snprintf函数的字符串缓冲
1
2
|
int
sprintf
(
char
*str,
const
char
*format, ...);
int
snprintf(
char
*str,
size_t
size,
const
char
*format, ...);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
char
tlist_1[1024] = {0},tlist_2[1024]={0};
char
fname[7][8] = {
"a1"
,
"b1"
,
"c1"
,
"d1"
,
"e1"
,
"f1"
,
"g1"
};
int
i = 0, len_1,len_2 = 0;
len_1 = snprintf(tlist_1,1024,
"%s;"
,fname[0]);
len_2 = snprintf(tlist_2,1024,
"%s;"
,fname[0]);
for
(i=1;i<7;i++)
{
len_1 = snprintf(tlist_1,1024,
"%s%s;"
,tlist_1,fname[i]);
len_2 =
sprintf
(tlist_2,
"%s%s;"
,tlist_2,fname[i]);
}
printf
(
"tlist_1: %s\n"
,tlist_1);
printf
(
"tlist_2: %s\n"
,tlist_2);
|
1
2
|
tlist_1: g1;
tlist_2: a1;b1;c1;d1;e1;f1;g1;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
glibc-2.18/stdio-common/snprintf.c:
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <libioP.h>
21 #define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a)
22
23
/* Write formatted output into S, according to the format
24 string FORMAT, writing no more than MAXLEN characters. */
25
/* VARARGS3 */
26
int
27 __snprintf (
char
*s,
size_t
maxlen,
const
char
*format, ...)
28 {
29
va_list
arg;
30
int
done;
31
32
va_start
(arg, format);
33 done = __vsnprintf (s, maxlen, format, arg);
34
va_end
(arg);
35
36
return
done;
37 }
38 ldbl_weak_alias (__snprintf, snprintf)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
glibc-2.18/libio/vsnprintf.c:
94
int
95 _IO_vsnprintf (string, maxlen, format, args)
96
char
*string;
97 _IO_size_t maxlen;
98
const
char
*format;
99 _IO_va_list args;
100 {
101 _IO_strnfile sf;
102
int
ret;
103 #ifdef _IO_MTSAFE_IO
104 sf.f._sbf._f._lock = NULL;
105 #endif
106
107
/* We need to handle the special case where MAXLEN is 0. Use the
108 overflow buffer right from the start. */
109
if
(maxlen == 0)
110 {
111 string = sf.overflow_buf;
112 maxlen =
sizeof
(sf.overflow_buf);
113 }
114
115 _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
116 _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
117 string[0] =
'\0'
;
118 _IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
119 ret = _IO_vfprintf (&sf.f._sbf._f, format, args);
120
121
if
(sf.f._sbf._f._IO_buf_base != sf.overflow_buf)
122 *sf.f._sbf._f._IO_write_ptr =
'\0'
;
123
return
ret;
124 }
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
glibc-2.18/stdio-common/snprintf.c:
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <libioP.h>
21 #define
vsprintf
(s, f, a) _IO_vsprintf (s, f, a)
22
23
/* Write formatted output into S, according to the format string FORMAT. */
24
/* VARARGS2 */
25
int
26 __sprintf (
char
*s,
const
char
*format, ...)
27 {
28
va_list
arg;
29
int
done;
30
31
va_start
(arg, format);
32 done =
vsprintf
(s, format, arg);
33
va_end
(arg);
34
35
return
done;
36 }
37 ldbl_hidden_def (__sprintf,
sprintf
)
38 ldbl_strong_alias (__sprintf,
sprintf
)
39 ldbl_strong_alias (__sprintf, _IO_sprintf)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
glibc-2.18/libio/iovsprintf.c:
27 #include
"libioP.h"
28 #include
"strfile.h"
29
30
int
31 __IO_vsprintf (
char
*string,
const
char
*format, _IO_va_list args)
32 {
33 _IO_strfile sf;
34
int
ret;
35
36 #ifdef _IO_MTSAFE_IO
37 sf._sbf._f._lock = NULL;
38 #endif
39 _IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
40 _IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
41 _IO_str_init_static_internal (&sf, string, -1, string);
42 ret = _IO_vfprintf (&sf._sbf._f, format, args);
43 _IO_putc_unlocked (
'\0'
, &sf._sbf._f);
44
return
ret;
45 }
46 ldbl_hidden_def (__IO_vsprintf, _IO_vsprintf)
47
48 ldbl_strong_alias (__IO_vsprintf, _IO_vsprintf)
49 ldbl_weak_alias (__IO_vsprintf,
vsprintf
)
|
一开始是打算使用gdb调试跟踪进入snprintf函数探个究竟的,可是调试时发现用step和stepi都进不到snprintf函数里面去,看了一下链接的动态库,原来libc库已经stripped掉了:
1
2
3
4
5
6
7
8
|
hong@ubuntu:~
/test/test-example
$ ldd snprintf_test
linux-gate.so.1 => (0xb76f7000)
libc.so.6 =>
/lib/i386-linux-gnu/libc
.so.6 (0xb7542000)
/lib/ld-linux
.so.2 (0xb76f8000)
hong@ubuntu:~
/test/test-example
$
file
/lib/i386-linux-gnu/libc
.so.6
/lib/i386-linux-gnu/libc
.so.6: symbolic link to `libc-2.15.so'
lzhong@ubuntu:~
/test/test-example
$
file
/lib/i386-linux-gnu/libc-2
.15.so
/lib/i386-linux-gnu/libc-2
.15.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), BuildID[sha1]=0x7a6dfa392663d14bfb03df1f104a0db8604eec6e,
for
GNU
/Linux
2.6.24, stripped
|
在找glibc源码时,我想知道系统当前使用的glibc版本,一时不知道怎么查,Google一下大多数都是Redhat上的rpm查法,不适用于Ubuntn,而用dpkg和aptitude show都查不到glibc package,后来才找到ldd用法。
1
2
3
4
5
6
|
hong@ubuntu:~
/test/test-example
$ ldd --version
ldd (Ubuntu EGLIBC 2.15-0ubuntu20) 2.15
Copyright (C) 2012 Free Software Foundation, Inc.
This is
free
software; see the
source
for
copying conditions. There is NO
warranty; not even
for
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
|