1.概述
在C编程当中常常会用到这三个函数。一般初级程序员会区分不出其本质性区别。在编程当中不正确使用,或者随意使用常常会带来不可控的风险。
2.函数原型
char* strcpy(char* des,const char* source)
void *memcpy(void *dest, const void *src, size_t n);
extern char *strdup(char *s);
3.函数实现
int strcpy(char *tar,char *src)
{
while ( (*tar++ = *src++) != '\0') {
;
}
return 0;
}
void *memcpy(void *s, const void *ct, int n)
{
register int i;
char *dst = (char *)s;
char *src = (char *)ct;
for (i = 0; i < n; i++)
{
*dst++ = *src++;
}
return s;
}
char *strdup (const char *str)
{
char *new_str;
size length;
if (str)
{
length = strlen (str) + 1;
new_str = malloc(char, length);
memcpy (new_str, str, length);
}
else
new_str = NULL;
return new_str;
}
4.分析
⑴ strcpy
该函数用于字符串拷贝,大伙都觉得它太简单了,但稍微不留神,你就已经编出非常危险的代码了。而且这种危险是不易被察觉的。下面笔者就来说几种导致程序异常的情况。
① 非法访问
char *dest = "hello";
strcpy(dest,"world!");
NOTE: dest 指向的是只读数据区,该数据段是不允许写的。执行这一操作,将导致该程序(进程)崩溃。
② 栈破坏
void test(const char *str);
{
char dst[4] = {0,};
strcpy(dst,str);
}
test("hello world!");
NOTE: 安全编程建议使用strncpy函数等防止栈溢出。
⑵ memcpy
memcpy 跟 strcpy的差别在于memcpy可以拷贝任意类型的数据,而strcpy则只能是字符串。当然内部实现还是大同小异。
① 非法访问
memcpy(“hello”,”world”,5);
⑶ strdup
strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。
这里我需要特别讲一下。这里在面向对象编程当中使用的最多。需要特别跟strcpy()函数区别开来。
如以下对象(代码摘自json-c):
struct json_object
{
enum json_type o_type;
json_object_private_delete_fn *_delete;
json_object_to_json_string_fn *_to_json_string;
int _ref_count;
struct printbuf *_pb;
union data
{
json_bool c_boolean;
double c_double;
int64_t c_int64;
struct lh_table *c_object;
struct array_list *c_array;
struct
{
union
{
char *ptr;
char data[LEN_DIRECT_STRING_DATA];
} str;
int len;
} c_string;
} o;
json_object_delete_fn *_user_delete;
void *_userdata;
}
创建一个字符串对象
struct json_object* json_object_new_string(const char *s)
{
struct json_object *jso = json_object_new(json_type_string);
if (!jso)
return NULL;
jso->_delete = &json_object_string_delete;
jso->_to_json_string = &json_object_string_to_json_string;
jso->o.c_string.len = strlen(s);
if(jso->o.c_string.len < LEN_DIRECT_STRING_DATA)
{
memcpy(jso->o.c_string.str.data, s, jso->o.c_string.len);
}
else
{
jso->o.c_string.str.ptr = strdup(s);
if (!jso->o.c_string.str.ptr)
{
json_object_generic_delete(jso);
errno = ENOMEM;
return NULL;
}
}
return jso;
}
在释放函数时候,需要释放该指针所分配的内存,所以该指针所指向的内存区必须在堆区,否则就会导致系统崩溃。
再举个例子:
char g_str[5] = {0,};
int main()
{
char *p = "1234";
char *dst = NULL;
det = strdup("hello");
//释放内存
free(dst); //正常释放内存
free(p); //导致程序崩溃(段错误)
free(g_str); //导致程序崩溃(段错误)
}
NOTE: 所以我们在编程的时候需要注意这些区别。