C语言结构的灵活(变长)数组成员为何不能用指针代替

LoveCreatesBeauty 2004-12-22 12:08:36
/* 使用b1代替a1,b2代替a2,则出错。 */

#include <stdio.h>
#include <string.h>
#include <malloc.h>

typedef struct S
{
int len;
char result[1];
//char result[]; //如果是C99编译器,result可以这样声明,代替上一句。
} S;

typedef struct S2
{
int len;
char * result;
} S2;

int main()
{
char * pszHello = "hello, world";
S * pS; //a1
//S2 * pS; //b1

pS = (S *)(malloc(sizeof(S) + strlen(pszHello) + 1)); //a2
//pS = (S2 *)(malloc(sizeof(S2) + strlen(pszHello) + 1)); //b2

pS->len = 122;
strcpy(pS->result, pszHello);

printf("%s\n", pS->result);
free(pS);
}
...全文
205 点赞 收藏 13
写回复
13 条回复
aleon 2004年12月24日
数组和指针在操作内存上是一样的方便,但数组名代表的是一块内存,本身是一个指针,
一般的指针没有数组名那个语义,仅只占一个指针的空间,所以在用指针操作内存前,
先要让指针指向一块具体的内存,
楼主所提的问题所在就是在b情况时,没有将result指向一块具体的内存,只是给他分配了一个指针空间,liem(阿明)的做法是正确的,

-------------
pS->result = pS + 1;

这种做法也不错
回复 点赞
LoveCreatesBeauty 2004年12月23日
谢谢啊
回复 点赞
winstonch 2004年12月23日
TO LoveCreatesBeauty(爱产生美) 其实结构体里面用数组成员和灵活数组是一样的,都需要动态的为结构体分配空间,而用指针就不同了,如果没有C99时用指针,而有C99时,想改成灵活数组可就不那么容易了,是吧?
因为用数组成员和灵活数组都是连续的内存空间,而用指针成员就不一样.
回复 点赞
LoveCreatesBeauty 2004年12月22日
mefit(何足道)说的很对,我用错了。谢谢。
回复 点赞
LoveCreatesBeauty 2004年12月22日
Michael_555(Stack)的这句
pS->result = pS + 1; //让pS->result指向pS加上一个单位的sizeof(S2)的位置。


pS->result = (char *)pS + sizeof(S2);
或者
pS->result = (S2 *)pS + 1;

是一样的吧。



我对使用成员数组(非C99)的用法感觉很迷糊:如果用指针,只是需要多分配一个指针,但是这个开销并不大啊。并且不需要为整个结构在动态分配内存。并且这种数组成员在C99之前是不可移植的。还请多多指教。
回复 点赞
liem 2004年12月22日
你可以用指针,但必须为指针分配空间
typedef struct S2
{
int len;
char * result;
} S2;

int main()
{
char * pszHello = "hello, world";
S2 * pS;
pS = (S *)(malloc(sizeof(S));
pS->result = (char *)(malloc(strlen(pszHello) + 1)); //为result分配空间
pS->len = 122;
strcpy(pS->result, pszHello);

printf("%s\n", pS->result);
free(pS);
}
回复 点赞
Michael_555 2004年12月22日
楼主,用b1代替a1,b2代替a2后,你虽然为指针result将要指向的内容分配了空间,但你没有让result指向这块地址啊。这样修改一下就好了。

int main()
{
char * pszHello = "hello, world";
//S * pS; //a1
S2 * pS; //b1

//pS = (S *)(malloc(sizeof(S) + strlen(pszHello) + 1)); //a2
pS = (S2 *)(malloc(sizeof(S2) + strlen(pszHello) + 1)); //b2
pS->result = pS + 1; //让pS->result指向pS加上一个单位的sizeof(S2)的位置。

pS->len = 122;
strcpy(pS->result, pszHello);

printf("%s\n", pS->result);
free(pS);
}
回复 点赞
Dong 2004年12月22日
回复人: mefit(何足道) ( ) 信誉:100 2004-12-22 08:32:00 得分: 0


我认为,指针变量char * result只是为存储地址申请了内存空间而已,char result[]是申请了一段内存存放字符,两者的意义不一样,前面的一个只要把pszHello赋给result就可以了,后面的一个必须pszHello指向的内容复制给result[],所以需要用strcpy(pS->result, pszHello)。
-----------------------------------------------------------------------------
这段描述是有道理的,因为char[1]与char[n]在传输上结构被编译器看成一样的,有个实际的使用方式就是在Linux/Unix里的消息队列,消息队列的定义是
struct sss{
int len;
char ch[1]
};
但我们在传输消息(megsnd)的过程中的消息信息长度肯定会大于char[1]的范围,而这并不是在定义时的错误,而是我们可以自己根据需要定义一个的结构,只要结构的变量类型一样就可以了,比如:
struct ss{
int len;
char ch[100]
};
使用这样的结构一样被正常传输

其实这个结构体:
typedef struct S
{
int len;
char result[1];
} S;
的可扩充性很高的,所以可以扩充到pS = (S1 *)(malloc(sizeof(S1) + strlen(pszHello) + 1));
在对pS->result拷贝数据时他会按扩充的方式处理

但pS = (S2 *)(malloc(sizeof(S2) + strlen(pszHello) + 1)); 因为 pS->result 需要分配空间,而你没有分配空间,所以是错误的。

不过总的来是还是错误的,因为就算是typedef struct S,他的有效范围是char[1],在小的测试里没事,但在大型软件里,内存有被覆盖的危险!
回复 点赞
LoveCreatesBeauty 2004年12月22日
HI, 建华!

新年快乐! [圣诞节不关我什么事的,不信基督教 :-)]

这涉及到数组与指针的区别。

比如 S 的1个对象在内存中的布局大体是这样的:

地址 内容 说明
10000 32 对应 int len, 假定int长度为4字节
10004 ‘H' result[0]
l0005 'e' result[1]
10006 'l' result[2]
10007 'l' result[3]
10008 'o' result[4]
10009 ',' result[5]
10010 'w' result[6]
10011 'o' result[7]
10012 'r' result[8]
l0013 'l' result[9]
10015 'd' result[10]
10016 '\0' result[11], 字符串结束符

而相似的S2的内存布局大致如下

地址 内容 说明
10000 32 对应 int len, 假定int长度为4字节
10004 32500 result, 是一个指针,也就是说他存放的是一个内存地址,在本情况下
| 是字符串"Hello, world"的首址
|
+--------+
|
|
V
32500 ‘H'
32501 'e'
32502 'l'
32503 'l'
32504 'o'
32505 ','
32506 'w'
32507 'o'
32508 'r'
32509 'l'
32510 'd'
32511 '\0'


可以看到,对于S对象,字符串的首地址可以由其首址加一个固定的偏移得到,而对于S2的对象,很大程度是运行起决定的,必须检测S2.result指针的内容才能知道。 对于S2正确的存放方法是:

S2 * ps;
char * pszHello = "hello, world";

ps = (struct S2*)malloc(sizeof(struct S2));

ps->len = 122;

ps->result = (char *)malloc(strlen(pszHello)+1);
/* 如果不经过上一步, ps->result中的值不可预测,但是有很大的机会该地址不能够合法的使用, 比如垃圾值0或者76538等等, 这样执行下面一行时会往你不知道而且也不属于你的内存区
写入内容,一般就会产生保护错误[protection error]
*/
strcpy(pS->result, pszHello);

printf("%s\n", pS->result);
free(ps->result)
free(pS);


如果想把S2和字符串顺序相连地放在一个地方,类似S的情况,可以这样做。

S2 * ps;
char * pszHello = "hello, world";

ps = (struct S2*)malloc(sizeof(struct S2) + strlen(pszHello)+1);

ps->len = 122;

ps->result = (char *)ps+8;
/* 为什么要加8 ?谈谈你的理解! */

strcpy(pS->result, pszHello);

printf("%s\n", pS->result);

free(pS);

这样只需要分配和释放各一次!

祝你进步!


ALNG
回复 点赞
LoveCreatesBeauty 2004年12月22日
谢谢winstonch。你的回复也有道理,我以前没有想到过这一点。我对使用成员数组(非C99)的用法感觉很迷糊:如果用指针,只是需要多分配一个指针,但是这个开销并不大啊。并且不需要为整个结构在动态分配内存。并且这种数组成员在C99之前是不可移植的。还请多多指教。

用liem(阿明)的方法可以,我在例子里弄错了。因为没有想明白为什么会需要用数组成员这种奇怪的用法。

Michael_555(Stack)的方法:pS->result = pS + 1;,我没有看明白。

回复 点赞
winstonch 2004年12月22日
用指针来代替灵活数组是可以的,只不过多占了一个指针的空间,所以为什么用指针代替呢?
在没有灵活数组时可以象楼主例子里的那样用一个成员的数组来做,还是少占一个指针的空间,所以为什么要用指针来代替呢?
回复 点赞
mefit 2004年12月22日
我认为,指针变量char * result只是为存储地址申请了内存空间而已,char result[]是申请了一段内存存放字符,两者的意义不一样,前面的一个只要把pszHello赋给result就可以了,后面的一个必须pszHello指向的内容复制给result[],所以需要用strcpy(pS->result, pszHello)。
回复 点赞
SCUM 2004年12月22日
。。
TC 试过 可以


回复 点赞
发动态
发帖子
C语言
创建于2007-09-28

3.2w+

社区成员

24.0w+

社区内容

C语言相关问题讨论
社区公告
暂无公告