问:在代码里遇到这样的结构:
struct name{
intnamelen;
charnamestr[1];
};
在后续的代码里使用内存分配技巧使数组成员用起来像有多个元素,namelen记录了数组元素的个数,感觉有些不合理,但是代码里确实存在,该如何解释?
答:不清楚这种做法的合法性,但是这种技术确实存在,但又不符合C语言的规定,下面看几个演化例子。
例一:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct name{
intnamelen;
charnamestr[1];
};
struct name *makename(char *newname){
char*name = newname;
structname *ret = malloc(sizeof(struct name)-1+strlen(newname)+1);
printf("sizeof(structname=%d,namelen_addr=%d, namestr_addr=%d)\n",(int)sizeof(structname),(int)&ret->namelen,(int)&ret->namestr);
if(ret!=NULL){
ret->namelen=strlen(name);
strcpy(ret->namestr,newname);
}else{
free(ret);
returnNULL;
}
returnret;
}
int main(int argc, char **argv){
char*name = "test make name";
structname *pn = makename(name);
printf("len=%d,name=%s\n", pn->namelen, pn->namestr);
return0;
}
此程序需要关注的地方是:
struct name *ret = malloc(sizeof(structname)-1+strlen(newname)+1);
struct name结构的大小仅为sizeof(structname),这里却申请了sizeof(struct name)-1+strlen(newname)+1,这里大小就是name结构成员namelen的大小和newname所指字符常量的大小的总和。而name结构成员namestr悄悄的成为了刚刚申请的newname所指字符常量大小的首地址。
例二:将结构成员namestr数组扩容到100,代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
struct name{
intnamelen;
charnamestr[MAX];
};
struct name *makename(char *newname){
char*name = newname;
structname *ret = malloc(sizeof(struct name)-MAX+strlen(newname)+1);
printf("sizeof(structname=%d,namelen_addr=%d, namestr_addr=%d)\n",(int)sizeof(structname),(int)&ret->namelen,(int)&ret->namestr);
if(ret!=NULL){
ret->namelen=strlen(newname);
strcpy(ret->namestr,newname);
}else{
free(ret);
returnNULL;
}
returnret;
}
int main(int argc, char **argv){
char*name = "test make name";
structname *pn = makename(name);
printf("len=%d,name=%s\n", pn->namelen, pn->namestr);
return0;
}
其实这个MAX扩容是多余的,和例一是一样的。
例三:
真正安全的做法是使用指针,而不是数组:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct name{
intnamelen;
char*namestr;
};
struct name *makename(char *newname){
structname *ret = malloc(sizeof(struct name));
printf("sizeof(structname=%d,namelen_addr=%d, namestr_addr=%d)\n",(int)sizeof(structname),(int)&ret->namelen,(int)&ret->namestr);
if(ret!=NULL){
ret->namelen=strlen(newname);
ret->namestr=malloc(strlen(newname)+1);
if(ret->namestr==NULL){
free(ret->namestr);
returnNULL;
}
strcpy(ret->namestr,newname);
}else{
free(ret);
returnNULL;
}
returnret;
}
int main(int argc, char **argv){
char*name = "test make name";
structname *pn = makename(name);
printf("len=%d,name=%s\n", pn->namelen, pn->namestr);
return0;
}
这里显然把长度和字符串保存在一块内存中的方便已经不复存在了,而且在释放这个结构的实例的时候需要调用两次free函数。
将两次malloc调用合成一次的例子如例四。
例四:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct name{
intnamelen;
char*namestr;
};
struct name *makename(char *t_name){
char*buf = malloc(sizeof(struct name)+strlen(t_name)+1);
if(buf==NULL){
printf("bufmalloc error\n");
returnNULL;
}
structname *pname=(struct name *)buf;
pname->namelen=strlen(t_name);
strcpy(buf+sizeof(structname), t_name);
pname->namestr=buf+sizeof(structname);
returnpname;
}
int main(int argc, char **argv){
char*test_name="test_name";
structname *pn = makename(test_name);
printf("namelen=%d,namestr=%s\n",pn->namelen, pn->namestr);
return0;
}
结束