簡體   English   中英

Valgrind-復制/釋放陣列時內存泄漏

[英]Valgrind - Memory Leaks When Duplicating/Freeing Arrays

我在弄亂分配和釋放內存以獲取更多信息。 下面,我從命令行( char **argv )接收字符串參數,並嘗試將它們作為大寫字符串復制到另一個malloc數組中:

static char **duplicateArgs(int argc, char **argv) {
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }
    char **duplicate = (char**)malloc((argc + 1) * sizeof(char**));

    int i = 1;
    int j = 0;
    int stringLength;

    while (argv[i]) {
        stringLength = strlen(argv[i]);
        duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
        for (j = 0; j < stringLength; j++) {
            duplicate[i][j] = toupper(argv[i][j]);
        }
        duplicate[i][j]= '\0';
        i++;
    }
    duplicate[i] = NULL;
    return duplicate;
}

我也有釋放它們的功能:

static void freeDuplicateArgs(char **copy) {
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
        }
}

main函數中,我調用以下方法,將它們打印出來,然后釋放它們:

    char **copy = duplicateArgs(argc, argv);
    char **p = copy;

    argv++;
    p++;
    while(*argv) {
        printf("%s %s\n", *argv++, *p++);
    }

    freeDuplicateArgs(copy);
    return 0;

輸出按預期工作,定期打印出字符串,然后大寫輸出。 但是,當我對Valgrind進行檢查時,會遇到以下麻煩:

==20867== Conditional jump or move depends on uninitialised value(s)
==20867==    at 0x100000D1A: freeDuplicateArgs (part2.c:32)
==20867==    by 0x100000E19: main (part2.c:57)
==20867== 
==20867== 
==20867== HEAP SUMMARY:
==20867==     in use at exit: 62,847 bytes in 368 blocks
==20867==   total heap usage: 520 allocs, 152 frees, 66,761 bytes allocated
==20867== 
==20867== LEAK SUMMARY:
==20867==    definitely lost: 8,639 bytes in 18 blocks
==20867==    indirectly lost: 1,168 bytes in 5 blocks
==20867==      possibly lost: 4,925 bytes in 68 blocks
==20867==    still reachable: 48,115 bytes in 277 blocks
==20867==         suppressed: 0 bytes in 0 blocks

如果編譯器沒有為我的freeDuplicates方法引發任何錯誤並且我的輸出正常,為什么會出現這樣的內存混亂情況?

編輯:我還應該提到我在OSX 10.8上-顯然Valgrind與此有關。

釋放代碼

您的釋放代碼很有趣-並非完全錯誤,但也並非完全正確:

static void freeDuplicateArgs(char **copy) {
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
    }
}

您可以在釋放copy != NULL之前測試copy != NULL ,但是一直以來都在使用它,因此該測試在那里很多余。 此外,您可以執行free(NULL)而不會遇到問題。

然后我們看一下循環:

 while (copy[i] != NULL)
 {
     if (copy[i] != NULL)

循環條件和if條件相同-同樣,您可以free(NULL)

您的while循環具有寫出的for循環結構。 您最好在以下方面做得更好:

static void freeDuplicateArgs(char **copy)
{
    if (copy != NULL)
    {
        for (int i = 0; copy[i] != NULL; i++)
            free(copy[i]);
        free(copy);
    }
}

copy != NULL的測試可防止崩潰,並且僅在已知為非null時釋放它,這是(非常非常)小的優化。

我懷疑這是問題的直接起因(稍后我會在其余的代碼中看起來有點困難),但這是要考慮的問題。

分配代碼

你有:

static char **duplicateArgs(int argc, char **argv) {
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }

首先,可以在沒有用戶參數的情況下調用程序,但是它仍然具有argv[0] ,程序名稱和NULL指針。 您的代碼可能應該通過返回(char **)NULL簡單地處理負值。 對於0參數,您將返回一個指向空指針的指針。 對於一個或多個參數,需要做的工作。

考慮這一行:

duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);

您可能會考慮兩種情況: sizeof(char) == 1sizeof(char) > 1 (是的,請稍等-等待!)。 如果sizeof(char) == 1 ,那么您不需要乘以它。 如果sizeof(char) > 1 ,則說明您沒有分配足夠的空間。 表達式應為(stringLength + 1) * sizeof(char) 因此,編寫以下兩行之一:

duplicate[i] = (char*)malloc((stringLength + 1) * sizeof(char));
duplicate[i] = (char*)malloc(stringLength + 1);

我注意到C標准說定義為sizeof(char) == 1 ,所以實際結果是相同的,但是如果您正在處理int或其他sizeof(type)數組,請在右側獲取與sizeof(type)相乘的值這個地方可能很關鍵。

ISO / IEC 9899:2011§6.5.3.4中sizeof_Alignof運營商

¶4當將sizeof應用於類型為charunsigned charsigned char (或其限定版本)的操作數時,結果為1。

Mac OS X 10.7.5上的Valgrind

以您的代碼幾乎與問題中的代碼相同:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char **duplicateArgs(int argc, char **argv)
{
    if (argc <= 1) {
        printf("%s\n", "No strings to duplicate.");
        exit(1);
    }
    char **duplicate = (char**)malloc((argc + 1) * sizeof(char**));

    int i = 0;
    int j = 0;
    int stringLength;

    while (argv[i]) {
        stringLength = strlen(argv[i]);
        duplicate[i] = (char*)malloc(stringLength * sizeof(char) + 1);
        for (j = 0; j < stringLength; j++) {
            duplicate[i][j] = toupper(argv[i][j]);
        }
        duplicate[i][j]= '\0';
        i++;
    }
    duplicate[i] = NULL;
    return duplicate;
}

static void freeDuplicateArgs(char **copy)
{
    int i = 0;
    while (copy[i]) {
        if (copy[i] != NULL){
            free(copy[i]);
        }
        i++;
    }
    if (copy != NULL){
        free(copy);
        }
}

int main(int argc, char **argv)
{
    char **copy = duplicateArgs(argc, argv);
    char **p = copy;

    argv++;
    p++;
    while(*argv) {
        printf("%s %s\n", *argv++, *p++);
    }

    freeDuplicateArgs(copy);
    return 0;
}

(區別是在本工作代碼中為int i = 0;而不是問題代碼中為int i = 1;

valgrind下運行時,這將獲得干凈的健康單:

$ gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition mlk.c -o mlk
$ valgrind ./mlk nuts oh hazelnuts
==582== Memcheck, a memory error detector
==582== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==582== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==582== Command: ./mlk nuts oh hazelnuts
==582== 
nuts NUTS
oh OH
hazelnuts HAZELNUTS
==582== 
==582== HEAP SUMMARY:
==582==     in use at exit: 18,500 bytes in 33 blocks
==582==   total heap usage: 38 allocs, 5 frees, 18,564 bytes allocated
==582== 
==582== LEAK SUMMARY:
==582==    definitely lost: 0 bytes in 0 blocks
==582==    indirectly lost: 0 bytes in 0 blocks
==582==      possibly lost: 0 bytes in 0 blocks
==582==    still reachable: 18,500 bytes in 33 blocks
==582==         suppressed: 0 bytes in 0 blocks
==582== Rerun with --leak-check=full to see details of leaked memory
==582== 
==582== For counts of detected and suppressed errors, rerun with: -v
==582== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)
$

如您所見,沒有報告內存錯誤。 對於該機器,仍在使用的33個塊是“正常”的; 它們是由Mac OS XC運行時庫分配的。 我還沒有在Mac OS X 10.8系統上使用valgrind進行比較。 我建議您嘗試在valgrind下運行一個非常簡單的程序,看看它能告訴您什么:

int main(void) { return 0; }

這將是您系統的基准。 如果存在泄漏,則泄漏不是由您的代碼引起的,並且您將需要學習如何記錄抑制,以便您的程序不會因操作系統的違背行為而受到懲罰。

如果您在上面運行了簡單程序,發現它不會產生泄漏(或者泄漏從根本上變得越來越小),那么請嘗試一些更復雜的程序:

#include <stdlib.h>
int main(void)
{
    void *vp = malloc(32);
    free(vp);
    return(0);
}

這樣可以確保將malloc()拖入並使用,但顯然不會泄漏(它也可以正確處理malloc()失敗!)。

valgrind對Mac OS X 10.8的支持

Valgrind網站說:

2012年9月18日:valgrind-3.8.1,用於X86 / Linux,AMD64 / Linux,ARM / Linux,PPC32 / Linux,PPC64 / Linux,S390X / Linux,MIPS / Linux,ARM / Android(2.3.x及更高版本),提供X86 / Android(4.0和更高版本),X86 / Darwin和AMD64 / Darwin(Mac OS X 10.6和10.7,對10.8的支持有限)。

您所看到的可能是對Mac OS X 10.8的有限支持的一個方面。

我看到我需要為自己重建valgrind (3.7.0不是最新的)。

我認為您永遠不會將freeDuplicateArgs duplicate[0]設置為任何值,所以當您在freeDuplicateArgs使用copy[i]時(從i = 0開始),您將不知道會發生什么。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM