我正在努力使用一个C结构,它必须包含一个较小结构的动态数组:
typedef struct issueStruct {
int data;
} issue;
typedef struct volumeStruct {
issue* collection;
size_t elements;
} volume;
我可以在卷结构的数组中动态创建尽可能多的问题结构。我也可以遍历那个数组:
int main(){
volume* TimeMagazine = (volume*)malloc(sizeof(volume));
TimeMagazine->collection = (issue*)malloc(4 * sizeof(issue));
TimeMagazine->elements = 4;
issue* ptr = TimeMagazine->collection;
int i;
// Populate & iterate through array:
i = 0;
while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+i; // Advance ptr
}
return 0;
}
OUTPUT:
[Linux]$ gcc -Wall magazines.c
[Linux]$ ./a.out
0) 0
1) 100
2) 200
3) 300
[Linux]$
到目前为止,这么好。当我在GDB中逐步完成上述操作时,一切看起来都没问题,尽管我注意到问题结构似乎没有连续的内存地址。这是我看到的内存地址:
issue 0) 0x602030
issue 1) 0x602034
issue 2) 0x60203c
issue 3) 0x602048
这给了我一些停顿;我会假设所有问题都是4个字节,如sizeof(issue) = 4
。更严重的是,当我修改我的“迭代通过”代码以释放数组的元素时,我的代码会出错。具体来说,它在尝试释放第二个问题时出错。这是代码:
i = 0;
ptr = TimeMagazine->collection;
issue* ptr2 = ptr;
while(i< TimeMagazine->elements){
printf("freeing %d...\n", i);
i++;
free(ptr2); // free ptr2
ptr2 = ptr = ptr+i; // advance ptr & ptr2
}
这是错误(Linux上的GCC):
*** Error in `./a.out': free(): invalid pointer: 0x000000000137c034 ***
所以我确定我在这里遗漏了一些东西,但不确定是什么。有人可以推荐一种有效的方法来释放()数组元素吗?
非常感谢!
-Pete
PS - 有许多“阵列中的释放结构”帖子,但似乎没有一个与我正在做的完全匹配。所以我发布这个是希望我的这个问题的版本是独一无二的。
答案 0 :(得分:7)
$sEnv = "<?xml version=""1.0"" encoding=""utf-8""?>"
$sEnv += "<soap:Envelope xmlns:soap=""http://www.w3.org/2003/05/soap-envelope"" xmlns:max=""http://www.asdf.com/asdf"">"
$sEnv += " <soap:Body>"
$sEnv += " <max:QueryASSET>"
$sEnv += " <max:ASSETQuery>"
$sEnv += " <max:ASSET>"
$sEnv += " <max:ASSETTAG operator=""="">asdf45412</max:ASSETTAG>"
$sEnv += " </max:ASSET>"
$sEnv += " </max:ASSETQuery>"
$sEnv += " </max:QueryASSET>"
$sEnv += " </soap:Body>"
$sEnv += "</soap:Envelope>"
$soapEnvelopeXml = New-Object 'System.Xml.XmlDocument'
$soapEnvelopeXml.LoadXml($sEnv)
$tamhost = 'cadd.ad2.gt.au:9045'
$sURL = "http://$tamhost/mweb/services/AEXTSYS4_QueryAssets"
$request = [System.Net.HttpWebRequest]([System.Net.WebRequest]::Create($sURL))
$request.Host = $tamhost
$request.ContentType = 'application/soap+xml;charset=UTF-8;action=""urn:processDocument""'
$request.Method = 'POST'
$stream = $request.GetRequestStream()
$soapEnvelopeXml.Save($stream)
$stream.Close()
$response = $request.GetResponse() #"The remote server returned an error: (401) Unauthorized."
您在while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+i; // Advance ptr
}
中使用了错误的指针算法,应该是ptr = ptr+i
,或者您可以在边界外访问。 ptr = ptr+1
部分也是如此。
正如@kaylum在评论中指出的那样:你在循环中调用free
,这也是错误的,你可以free
一次free(TimeMagazine->collection);
,因为你为4
预留了空间同一块中的元素。
答案 1 :(得分:2)
这是关于连续内存和包含动态数组的结构的附注。有关实际答案,请参阅the answer given by @KeineLust。
如前所述,一个malloc
==一个free
。
然而,未提及的是,由于缓存的考虑,连续内存通常表现得更好。
这意味着如果使用相同的struct volumeStruct
调用分配内存和动态数组,malloc
的效果会更好。
有两种常见的方法可以实现这一目标。
一,使用你目前拥有的相同结构(我修改了你的循环以获得ptr = ptr + 1
,所以我们不会超出界限):
int main(){
volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue)) );
TimeMagazine->collection = TimeMagazine + 1; // pointer arithmetics
TimeMagazine->elements = 4;
issue* ptr = TimeMagazine->collection;
int i;
// Populate & iterate through array:
i = 0;
while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+1; // Advance ptr
}
free(TimeMagazine);
return 0;
}
另一个选项(我认为这是在C99中引入的)是在结构的末尾添加一个可变长度数组。这样可以节省collection
指针所需的8(或4)个字节。
即:
typedef struct issueStruct {
int data;
} issue;
typedef struct volumeStruct {
size_t elements;
issue collection[];
} volume;
int main(){
volume* TimeMagazine = (volume*)malloc(sizeof(volume) + (4 * sizeof(issue)) );
TimeMagazine->elements = 4;
// no need to assign a value for TimeMagazine->collection
issue* ptr = TimeMagazine->collection;
int i;
// Populate & iterate through array:
i = 0;
while(i < TimeMagazine->elements){
ptr->data = 100*i;
printf("%d) %d\n", i, ptr->data);
i++;
ptr = ptr+1; // Advance ptr
}
free(TimeMagazine);
return 0;
}
最大的好处是CPU内存缓存和更简单的代码。在大多数情况下,我们为每个对象保存两个系统调用(一个malloc
和一个free
)这一事实无关紧要。