面向对象编程之C库函数 -- strcpy , memcpy , strdup 之区别

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: 所以我们在编程的时候需要注意这些区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值