2

私はそれのコツをつかむためにメモリの割り当てと割り当て解除をいじっています。以下では、コマンドライン(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メソッドに対してエラーを発生させず、出力に問題がない場合、なぜこのようなメモリの混乱が発生するのですか?

編集:私はOSX10.8を使用していることにも言及する必要があります-どうやらValgrindにはいくつかの問題があります...

4

2 に答える 2

3

コードを解放する

あなたの解放コードは興味深いものです—正確には間違っていませんが、実際には正しくありません。

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解放する直前にテストしますが、ずっと使用しているので、テストは冗長です。その上、あなたは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の場合、nullポインタへのポインタを返します。そして、1つ以上の議論については、やるべきことがあります。

次の行を検討してください。

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

あなたが考えるかもしれない2つのケースがあります:sizeof(char) == 1そしてsizeof(char) > 1(はい、待ってください—待ってください!)。の場合sizeof(char) == 1、それを掛ける必要はありません。の場合sizeof(char) > 1、十分なスペースを割り当てていません。式はである必要があります(stringLength + 1) * sizeof(char)。したがって、次の2行のいずれかを記述します。

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

C標準では、sizeof(char) == 1定義上、実際の結果は同じであると規定されていることに注意してください。ただし、配列などを扱っている場合は、適切な場所でint乗算をsizeof(type)行うことが重要になる可能性があります。

ISO / IEC 9899:2011§6.5.3.4sizeofおよび_Alignof演算子

¶4タイプ、、、または、 (またはその修飾バージョン)sizeofを持つオペランドに適用されると、結果は1になります。charunsigned charsigned char

Mac OSX10.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ブロックは、このマシンでは「通常」です。それらはMacOSXCランタイムライブラリによって割り当てられます。valgrind比較するMacOSX10.8システムを使用していません。で非常に単純なプログラムを実行してみて、valgrindそれが何を示しているかを確認することをお勧めします。

int main(void) { return 0; }

これがシステムのベースラインになります。リークがある場合、それはコードが原因ではありません。プログラムがO / Sの違反に対してペナルティを科されないように、抑制を記録する方法を学ぶ必要があります。

上記の単純なプログラムを実行してもリークが発生しない場合、またはリークが根本的に単純で小さい場合は、少し複雑なプログラムを試してください。

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

これにより、malloc()がドラッグされて使用されることが保証されますが、明らかにリークすることはありません(また、malloc()障害も正しく処理されます!)。

valgrindMac OSX10.8のサポート

ValgrindのWebサイトには次のように書かれています。

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 OSX10.8の限定的なサポートの側面かもしれません。

自分で再構築する必要があるようvalgrindです(3.7.0は最新ではありません)。

于 2013-02-24T18:23:04.897 に答える
2

何も設定していないと思うduplicate[0]ので、i = 0から始めてで使用するcopy[i]freeDuplicateArgs、どうなるかわかりません。

于 2013-02-24T18:11:00.490 に答える