用strcpy( )有问题,用strncpy( )还是有问题。
#define PATH_MAX 128
比如:strncpy(file_path, src, PATH_MAX);
strncpy( )容易出现2种问题:1)源串太长,造成没有结束符,引用时有越界乱码现象。 2)字符串长度容易与标称不一致。比如file_path说是最大支持128字符,然而如果把结束符算上,经常只有127。
strncpy源码:当src达到count时,就没有结束符了:
char* strncpy(char* dest, const char* source, size_t count)
{
char* start=dest;
while (count && (*dest++=*source++))
count--;
if(count)
while (--count)
*dest++='\0';
return(start);
}
一个好的解决方案是:
1.保证字符串长度是标称的长度,不虚标
2.其次不能越界,必须有正确的'\0'结束符
3.能够检查出dst长度必须至少比src长度一个字符
可能的实现如下:使用safe_strncpy(),并且获取dst长度进行安全检查
#define PATH_MAX 128
char file_path[PATH_MAX+1];
比如:safe_strncpy(file_path, src,PATH_MAX+1, PATH_MAX);
int safe_strncpy(char *dst, const char *src, size_t dst_size,size_t str_size)
{
if(dst_size <= str_size)
return -1;
dst[dst_size-1] = '\0';
return strncpy(dst,src,str_size);
}
这其中的关键这是要保证dst字符串长度要足够。
在运行时检查其实并不是太好,原因的必须测试到这才能发现问题。
更好的方案应该是在编译期间来对dst长度进行安全检查。
GCC编译期检查方案
GCC好像在4.3开始支持编译期断言
_Static_assert( expr,"msg")
如果代码写错了,dst空间与size相等时,存在无结束符bug应该报错。
如下字体串file_path支持128个字符,但是file_path定义空间也是128,应该定义为129
#define PATH_MAX 128
char file_path[PATH_MAX];
比如:safe_strncpy(file_path, src, PATH_MAX);
使用效果如下:
../include/comm.h:202:5: error: static assertion failed: "strncp small buf size error"
_Static_assert( sizeof(dst) > size,"strncp small buf size error");\
^
cmd_mark.c:137:13: note: in expansion of macro ‘safe_strncpy’
safe_strncpy(file_path, optarg, OS_PATH_MAX);
代码如下:
#define safe_strncpy(dst,src,size) \
do { \
_Static_assert( sizeof(dst) > size,"strncp small buf size error");\
_safe_strncpy(dst,src,sizeof(dst),size); \
}while(0)
这样,只要经过编译,就能保证所有调用safe_strncpy()的地方,有足够多的空间。
编译期断言的威力在于能更早发现问题,而不是等到运行时发现。如果有人误用了safe_strncpy(char * ptr, src,SIZE)也会提前发现代码的问题,而不是在运行时,甚至在发布后才发现问题。