2

Zed Shaw のLearn C The Hard Wayコースを受講しています。演習 11 の追加クレジットの質問 #2 で、彼は次のように尋ねます。

  1. i-- を使用して argc から開始して 0 までカウントダウンすることにより、これらのループを逆方向にカウントします。配列インデックスを正しく機能させるには、いくつかの計算を行う必要がある場合があります。

  2. while ループを使用して、argv から状態に値をコピーします。

私は試します:

#include <stdio.h>

int main(int argc, const char *argv[]){

    int i = argc - 1;
    while(i >= 0){
        printf("arg %d: %s\n", i, argv[i]);
        i--;
    }

    char *states[] = {
        "California", "Oregon",
        "Washington", "Texas"
    };

    int num_states = 4;
    i = num_states - 1;
    while( i >= 0){
        printf( "state %d, %s\n", i, states[i]);
        i--;
    }

    i = 0;
    while(i < argc && i < num_states){
        int j = 0;
        while( (states[i][j++] = argv[i][j++]) != '\0' ){
            i++;
        }
        states[i][j] = '\0';
    }

    i = num_states - 1;
    while( i >= 0){
        printf( "state %d, %s\n", i, states[i]);
        i--;
    }

    return 0;
}

を取得しSegmentation Faultます。Cで配列をコピーできないこと、それらがconstポインターまたは類似のものであることを理解しています(または私が読んだことです)。そのため、文字ごとにコピーしようとしています。

while(i < argc && i < num_states){
    int j = 0;
    while( (states[i][j++] = argv[i][j++]) != '\0' ){
        i++;
    }
    states[i][j] = '\0';
}

それでもうまくいきません。どうすればいいですか?コンパイラは、コンパイル時に次の警告を表示します。

$ make ex11
cc -Wall -g    ex11.c   -o ex11
ex11.c: In function ‘main’:
ex11.c:26:28: warning: operation on ‘j’ may be undefined [-Wsequence-point]

jそれが未定義 であると言う理由がわかりません。valgrindこれは言います:

$ valgrind ./ex11 this is a test
==4539== Memcheck, a memory error detector
==4539== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==4539== Using Valgrind-3.8.0 and LibVEX; rerun with -h for copyright info
==4539== Command: ./ex12 this is a test
==4539== 
arg 4: test
arg 3: a
arg 2: is
arg 1: this
arg 0: ./ex11
state 3, Texas
state 2, Washington
state 1, Oregon
state 0, California
==4539== 
==4539== Process terminating with default action of signal 11 (SIGSEGV)
==4539==  Bad permissions for mapped region at address 0x400720
==4539==    at 0x4005F1: main (ex11.c:26)
==4539== 
==4539== HEAP SUMMARY:
==4539==     in use at exit: 0 bytes in 0 blocks
==4539==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==4539== 
==4539== All heap blocks were freed -- no leaks are possible
==4539== 
==4539== For counts of detected and suppressed errors, rerun with: -v
==4539== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Segmentation fault

[EDIT 1] Aはこれのために私のコードを切り替えました

int i = 0;
while( i < argc && i < num_states ){
    states[i] = argv[i];
    i++;
}

動作しますが、コンパイラから警告が表示されます。また、この質問を投稿した後、以前のことを認識しました:

while( (states[i][j++] = argv[i][j++]) != '\0' ){
    i++;
}

単純に間違っています。j++はループごとに 2 回実行され、それi++は外側のループのそのループの外側にある必要があるためです。また、以下のコメントで述べたように、実際にはポインターの配列があるため、配列の配列を使用して実行しようとするバイト単位のコピーは機能しません。

そうは言っても、コンパイラの警告なしでこれを行う方法はありますか?

4

2 に答える 2

2

1 つの問題 (SEGV の原因となる問題) は whilestatesが の配列でありchar *、一連の文字列リテラル ( であるconst char *) で初期化していることです。したがって、少なくともその行で警告が表示されるはずです。これは、文字列定数 (読み取り専用メモリにある) に書き込もうとすると問題になります。

argvから文字列を一度に1 バイトずつコピーしたい場合states(ポインタを単にコピーするのではなく、たとえば)、それが書き込み可能states[i] = argv[i]であることを確認する必要があります。文字へのポインターの配列ではなく、文字の配列の配列をstates[i][j]作成することで、これを行うことができます。states

char  states[][16] = {
    "California", "Oregon",
    "Washington", "Texas"
};

もちろん、これらは固定サイズの配列なので、オーバーフローについて心配する必要がありますが、すでに状態配列自体がオーバーフローするという問題がありました。

于 2012-08-20T03:13:57.440 に答える