2017-02-01 7 views
1

私はパス(環境変数)のリストを取り、パスを分割して印刷するプログラムを書いています。それをコンパイルすると、私はsegfaultを取得します。以下は、GDBの私の出力です:valgrindのオンSegfault in Cプログラム、mallocコール

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000400eb0 in dest (name=0x7fffffffbce0 "PATH") at executables.c:100 
100  dest[i] = malloc(srclen+1); 

char** dest(char *name){ 
    int i=0; 
    char *vp; 
    const char s[2]=":"; 
    char *token; 
    char **dest; 
    name[strlen(name)-1]='\0'; 
    vp=getenv(name); 
    if(vp == NULL){ 
     exit(1); 
    } 
    token =strtok(vp,s); 
    while(token != NULL){ 
     size_t srclen = strlen(token); 
     dest[i] = malloc(srclen+1); 
     strcpy(dest[i], token); 
     token = strtok(NULL, s); 
     i++; 
    } 
    dest[i]=NULL; 
    return dest; 
} 

そして、これは私のメインです:

==21574== 1 errors in context 2 of 3: 
==21574== Use of uninitialised value of size 8 
==21574== at 0x400EB0: dest (executables.c:100) 
==21574== by 0x400B5B: main (main.c:9) 

は、これは私の関数である

#include "executables.h" 
int main(int argc, char **argv){ 
    char *path; 
    char name[BUFSIZ]; 
    printf("enter name of environment variable:\n"); 
    fgets(name,BUFSIZ,stdin); 
    char **p=dest(name); 
    int j=0; 
    while(p[j]!=NULL){ 
     printf("%s\n",p[j]); 
     j++; 
    } 
    return(0); 
} 
+3

0x400EB0での初期化されていない値[...]の使用:dest' – tkausl

+0

私は自分の答えを修正しなければならなかったので、関数はスタックに割り当てられた静的にスタックに割り当てられませんでした変数は未定義/ガーベジであり、コンパイラが望むものはすぐに再利用されます。そこでポインタの配列malloc()に変更し、メインにもそのレベルの空きを追加しました。 – clearlight

+0

もう1つのこと - malloc、zalloc、calloc、strdupなどでメモリを割り当てる関数では、呼び出し元までコメントブロックを置いて、割り当てられたメモリを解放して、誤用やメモリリークのリスク。一般的な問題は、プログラムや別の解決策の別の機能ですぐに使用できるように変更を加えて、その事実を逃してリークを導入するために人々が後で戻ってくることです。それは良い予防措置です。 – clearlight

答えて

2

strdup()を使用してください。ステップを保存します( のアカウントも '\ 0')。あなたが使用しているアプローチのために手近にいくつかのメモリを割り当てる必要があります。そうしないと、配列パターンを使用する代わりに、リンクされたリストを作成してパケットを割り当てることができます。 dest[i] = <ptr value>と言うと、割り当てられていないメモリのオフセットにインデックスを付けてそこに何かを格納しているので、segvioです。

#include <string.h> 
#define MAXTOKENS 10000 

char **get_dest(char *name) { 
    // Since dest has to be exposed/persist beyond this function all 
    // need dynamically allocate (malloc()) rather than stack allocate 
    // of the form of: char *dest[MAXTOKENS]. 
    char *dest = malloc(MAXTOKENS * sizeof (char *)); // <--- need to allocate storage for the pointers 
    char *vp; 
    if ((vp = getenv(name)) == NULL) 
     exit(-1); // -1 is err exit on UNIX, 0 is success 

    int i = 0; 
    char *token = strtok(vp, ":"); 
    while (token != NULL) { 
     dest[i] = strdup(token); // <=== strdup() 
     token = strtok(NULL, ":"); 
     i++; 
    } 

// dest[i] = NULL; // Why are you setting this to NULL after adding token? 
    return dest; 

} 

それはmain()の場合はましだ凝り性のfgets()が処理される場合、メインであるためget_dest()関数に適切なヌル終端文字列を渡すの面倒を見ます。一般的には、最も理にかなって最も関連性の高いものをローカルで実行したいと考えています。 get_dest()関数を使ってfgets()によって文字列が読み込まれなかった場所で使用した場合、ターミネータを上書きするだけの無駄なステップになります。だから、fgets()の前にchar配列をゼロに初期化することによって、末尾のバイトを '\ 0'に設定することについて心配する必要はありません。

最後に、あなたの関数名destにdestを返す変数と同じ名前を付けることは、おそらく良くないでしょう。同じ名前のプログラム内に複数のシンボルがある状況では、問題が発生する可能性があります。 getenvのmanページから

#include "executables.h" 
int main(int argc, char **argv) { 
    char *path; 
    char name[BUFSIZ] = { 0 }; // You could initialize it to zero this way 
    printf("enter name of environment variable:\n"); 
// bzero(name, BUFSIZ); //... or you could initialize it to zero this way then 
    fgets(name, BUFSIZ, stdin); 
    char **p = get_dest(name); 
    int j = 0; 
    while(p[j] != NULL) { 
     printf("%s\n", p[j]); 
     j++; 
     free(p[j]); // like malloc(), strdup'd() strings must be free'd when done 
    } 
    free(p); 
    return 0; 
} 
2
dest[i] = malloc(srclen + 1); 

destに格納されている各charポインタだけでなく、charポインタ(dest)へのポインタにもメモリを割り当てる必要があります。あなたが提供したコードでは、どちらのステップもとられません。

+0

それは良いキャッチだ。 – clearlight

+0

私はすべての信用を取ることはできません。上記のコメントでは、@ tkauslの答えは短かったが、説明する必要があると感じた。 – synchronizer

+0

私はあなたの答えをとにかくuv'dしました。少なくともあなたはここにバットを揺らしています:-) – clearlight

1

は... として一般的に実施し、getenvは()は環境リスト内の文字列 へのポインタを返すノート

呼び出し側は、 この文字列を変更しないように注意する必要があります。これは、プロセスの環境を変更するためです。

あなたのコードでは、そのルールに違反:

vp=getenv(name); 
... 
token =strtok(vp,s); 

これは、不正なメモリの書き込み動作です。