0

初めまして、タイトル失礼します。どのように表現すればよいか、あまりよくわかりませんでした。

C では、次のように宣言および割り当てられた 2D 文字列配列があります。

char ** args = malloc(50*(num_args+1));
for (int i = 0; i < num_args+1; i++){
    args[i] = malloc(50);

私はこれを一種の「初歩的なシェル」タイプのプログラムで使用しており、bash の機能の一部を模倣しているため、num_args 変数を使用しています。

複数のマシンでコンパイルして実行すると、args[4] のアドレスは常に範囲外になります。関連する gdb 出力は次のとおりです。

(gdb) print args[0]
$2 = 0x609140 "gcc"
(gdb) print args[1]
$3 = 0x609180 ""
(gdb) print args[2]
$4 = 0x6091c0 ""
(gdb) print args[3]
$5 = 0x609200 ""
(gdb) print args[4]
$6 = 0x636367 <Address 0x636367 out of bounds>
(gdb) print args[5]
$7 = 0x609280 ""

ご覧のとおり、args[4] の前後のアドレスは有効です。この 1 つのアドレスがどのように範囲外になるのでしょうか?

このコードが使用される関数全体は、次のとおりです

void parse(const char * command){
    // first parse built-ins (ie, not a call to the OS)
    if (strcmp(command, "history") == 0){
        show_history();
        return;
    }
    if (strcmp(command, "exit") == 0){
        exit(0);
    }

    hist_add(command);

    // copy 'command' into arg_string, while ignoring any possible comments
    char * arg_str;
    int num_args = 1;
    arg_str = malloc(strlen(command));
    for (int i = 0; i < strlen(command); i++){
        if (command[i] == '#' || command[i] == '\n') break;
        if (command[i] == ' ') num_args++;
        arg_str[i] = command[i];
    }

    // split arg_str into a string array where each string is an argument
    // to the command
    char ** args = malloc(num_args+1);
    for (int i = 0; i < num_args+1; i++){
        args[i] = malloc(50);
    }
    int tokens = 0;
    const char token = ' ';
    char * next = strtok(arg_str, &token);
    while (next != NULL){
        strcpy(args[tokens++], next);
        next = strtok(NULL, &token);
        if (next == NULL)
            args[tokens] = (char *)NULL;
    }

    exec_command(args);
}
4

3 に答える 3

4

あなたの質問に対する答えは、それが 2D 配列ではないという事実にあります。代わりにargs、ポインターの 1D 配列の最初の要素へのポインターを含み、それらの各要素自体が の 1D 配列の要素を指すことができますchar(これはしばしば「不規則配列」と呼ばれます。これらの 1D 配列は、長さが違う)。

したがって、1 つのアドレスargs[4]が範囲外である可能性がある理由は、args[3]および でargs[5]はない場合でも、3 つのポインターargs[3]およびargs[4]args[5]完全に独立した値であるためです。

実際には割り当てられた領域の外にあるため、誤った値で上書きされている可能性が非常に高く、args[4]が指す配列に十分なスペースを割り当てていませんargs。あなたの呼び出しはbytesmalloc()を要求しますが、それぞれが 1 バイト以上を占めるpointersのための十分なスペースが必要です。呼び出しを次のように変更することをお勧めします。num_args + 1 num_args + 1 malloc()

char ** args = calloc(num_args + 1, sizeof args[0]);

(使用するのではなくcalloc()、もちろん自分で乗算num_args + 1sizeof args[0]て を呼び出すmalloc()ことができますが、これを行う場合は、乗算がオーバーフローしないことを確認する必要がありますSIZE_MAX. calloc()がそれを処理する必要があります)。

于 2012-06-07T03:49:48.427 に答える
1

の引数malloc()は、割り当てるバイト数です。num_args型のすべてのポインタを保持するには不十分であり、文字列の長さを考えると十分char *ではないと思います。50私はあなたの完全なコードを詳細に調べていませんが、おそらくmalloc(sizeof(char *) * num_args)すべてのポインタを引数文字列に割り当てるために行う必要があります。次に、ループして、各文字列(コピーされている場合)に十分なスペースを割り当てます。malloc(sizeof(char) * len)ここで、lenは保存する必要のある文字列の最大長です。

于 2012-06-07T03:35:13.333 に答える
0

次の行にメモリ割り当てエラーとパフォーマンス バグの可能性があります。

arg_str = malloc(strlen(command));
for (int i = 0; i < strlen(command); i++){

多くの場合strdup()、文字列を複製するために使用できる関数があります。利用できない場合は、次を使用します。

char *arg_str = malloc(strlen(command) + 1);

端末のヌルにも十分なスペースを確保します'\0'

パフォーマンスのバグはstrlen()、文字列が長い場合、ループの各反復での評価にコストがかかることです。文字列の長さが反復ごとに変化しない限り、長さを一度計算して再利用します。

文字列を null で終了していません。そうすることが重要です。

int len = strlen(command);
int i;  // Must be in scope after the loop ends
for (i = 0; i < len; i++){
    if (command[i] == '#' || command[i] == '\n') break;
    if (command[i] == ' ') num_args++;
    arg_str[i] = command[i];
}

// i is out of scope if you use for (int i = 0;...
arg_str[i] = '\0';

null 終端がないことは、おそらく他の問題の原因です。疑わしい場合は、その都度出力してください。ただし、文字列がヌルで終了していない間は注意してください。

于 2012-06-07T04:41:02.440 に答える