C自由动态结构数组 - 为什么它们不是重点

时间:2016-11-15 04:54:52

标签: c arrays dynamic struct free

我正在努力使用一个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 - 有许多“阵列中的释放结构”帖子,但似乎没有一个与我正在做的完全匹配。所以我发布这个是希望我的这个问题的版本是独一无二的。

2 个答案:

答案 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)这一事实无关紧要。