在编写云平台管理的实际项目中,遇到了需要将 int 数据类型转换字符串 str —— 虚拟机名称的
功能函数编写。简单说就是要编写一个返回字符串指针的函数,这里我采用了两种方法来编写如下:
写法(一)
char *change_vsm_name_str(unsigned char vsm_n)
{
char *vsm_name = NULL;
char num[4] = {0};
vsm_name = (char *)malloc(10 * sizeof(char)); //malloc must be free
if (NULL == vsm_name)
{
return NULL;
}
memset(vsm_name, 0, sizeof(10 * sizeof(char)));
sprintf(num, "%d", vsm_n);
strcpy(vsm_name, "vsm");
strcat(vsm_name, num);
return vsm_name;
}
由于栈中的内存在函数执行完毕后自动被释放,所以不能返回函数中栈上的内存指针。这里,采用 malloc 来申请堆内存,最终返回的 vsm_name 属于堆内存的范畴,调用者的写法如下:
int edit_user_token_cmd(unsigned char vsm_n, char *user_token)
{
assert(NULL != user_token);
char *id_str = NULL;
char *name_str = NULL;
id_str = get_request_id_str();
strcpy(user_cmd.request_id, id_str);
strcpy(user_cmd.opr_type, "user_token");
if(NULL == (name_str = change_vsm_name_str(vsm_n)))
{
return -1;
}
strcpy(user_cmd.vsm_name, name_str);
strcpy(user_cmd.user_token, user_token);
free(name_str); //must be free
name_str = NULL;
return 0;
}
我们知道堆内存是需要开发者自己来管理的,这块动态申请出来的堆内存,在使用完毕之后必须通过 free 函数来释放。
写法 (二)
char *change_vsm_name_string(char *name_str, unsigned char vsm_n)
{
assert(NULL != name_str);
char *str = NULL;
char num[4] = {0};
str = name_str;
sprintf(num, "%d", vsm_n);
strcpy(name_str, "vsm");
strcat(name_str, num);
return str;
}
相较于写法一而言,该函数中多了一个参数 char *name_str,最终返回的指针也是它。实际上是要求调用者提供一个内存地址,而这个内存一般由调用者在栈上事先定义好。上级函数(调用者)写法如下:
int edit_user_token_cmd(unsigned char vsm_n, char *user_token)
{
assert(NULL != user_token);
char *id_str = NULL;
char name_str[10] = {0}; // 栈区
id_str = get_request_id_str();
strcpy(user_cmd.request_id, id_str);
strcpy(user_cmd.opr_type, "user_token");
if(NULL == change_vsm_name_string(name_str, vsm_n)) //提供name_str 参数
{
return -1;
}
strcpy(user_cmd.vsm_name, name_str);
strcpy(user_cmd.user_token, user_token);
return 0;
}
由于 name_str 存储于栈区,这里我们不需要进行 free 操作了。
总结:一般而言,良好的内存管理,要求malloc 与 free 成对出现,并且要遵循“谁申请,谁释放”的原则。写法一要求开发者在上一级函数中free堆内存,很容易被开发者遗忘,从而造成内存泄漏;第二种写法,调用者希望返回字符串指针,则事先要给函数传递一个字符串指针,更符合“谁做事,谁负责”的惯例,因此很个人比较推荐的是第二种写法。