2017-02-03 17 views
1

私は今この問題を一日中苦労しています。同様の例を見ても私はあまりにも遠くになっていないので、助けてくれるといいですね!誰かがコンテキストを知りたいと思えば、私はオペレーティングシステムコンセプトのCH3の最後にあるプログラミング課題1に取り組んでいます。難しい文字のポインタ配列

問題は基本的に、コマンドプロンプトを作成して、ユーザーがコマンドを入力してforkして実行し、そのコマンドを履歴に保存できるようにすることです。ユーザーはコマンド 'history'を入力して、最新の10個のコマンドを表示することができます。本書では、現在のコマンドを引数のcharポインタの配列として格納するように指示しました。現在はexecvp(args[0], args)を使用して現在のコマンドを実行します。私の教授はこれに他の要件を加えました。だから、このように個々の引数を個別にアクセスできるようにすることは、それらの部分にも役立ちます。

私は、charポインタの配列を使用して同様の方法でコマンドの履歴を保存することに決めました。たとえば、最初のコマンドがls -laで、2番目のコマンドがcd..の場合、history[0] = "ls -la"history[1] = "cd.."となります。私は本当にこれを動作させるのに苦労しています、そして、私はどこかでポインタをねじっているとかなり確信していますが、私はそれを理解できません。

メインでは、arg_history[0]を使用して最初のコマンド(ls -laの場合はls)に最初の単語を印刷できますが、実際にはすべてを印刷することはできません。しかし、私はそこにデータがあることを知っています。私はそれを(add_historyの機能を介して)追加して確認します。歴史を印刷するために作られたget_history関数に渡すとさらに悪いことに、ぎこちない言葉が印刷されます。なぜこれをやっているのか理解する助けに感謝します!今のところ私はそれが機能間のポインタを間違って渡すことと関係があるが、私が見ていることに基づいて、私は問題を見つけることができません!

/** 
* Simple shell interface program. 
* 
* Operating System Concepts - Ninth Edition 
* Copyright John Wiley & Sons - 2013 
*/ 

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



#define MAX_LINE  80 /* 80 chars per line, per command */ 
#define HIST_LENGTH  10 

void get_input(char *args[], int *num_args, char *history[], int *hist_index); 
void get_history(char *history[], int hist_index); 
void add_history(char *history[], char *added_command, int *hist_index); 

int main(void) 
{ 
    char *args[MAX_LINE/2 + 1]; /* command line (of 80) has max of 40 arguments */ 
    char *arg_history[HIST_LENGTH]; 

    int num_args; 
    int hist_index; 

    int should_run = 1; 
    int i; 

    while (should_run){ 
     printf("osh>"); 
     fflush(stdout); 

     get_input(args, &num_args, arg_history, &hist_index); 

     //printf("%s\n", arg_history[0]); //incorrectly prints up to the first space 
     //printf("%s\n", args[0]) //prints the correct arg from the last command (eg. for 'ls -la' it prints ls for args[0] and -la for args[1]) 

     if (strcmp(args[0], "history") == 0) { 
      get_history(arg_history, hist_index); 
     }   
    } 

    return 0; 
} 

void get_input(char *args[], int *num_args, char *history[], int *hist_index) { 
    char input[MAX_LINE]; 
    char *arg; 

    fgets(input, MAX_LINE, stdin); 
    input[strlen(input) - 1] = NULL; // To remove new line character - the compiler doesn't like how I'm doing this 

    add_history(history, input, hist_index); 

    arg = strtok(input, " "); 

    *num_args = 0; 
    while(arg != NULL) { 
     args[*num_args] = arg; 
     *num_args = *num_args + 1; 
     arg = strtok(NULL, " "); 
    } 
} 

void get_history(char *history[], int hist_index) { 
    int i; 
    for (i = 0; i < HIST_LENGTH; i++) { 
     printf("%d %s\n", hist_index, *history); 
     // prints gibberish 
     hist_index = hist_index - 1; 
     if (hist_index < 1) { 
      break; 
     } 
    } 
} 

void add_history(char *history[], char *added_command, int *hist_index) { 
    int i; 
    for (i = HIST_LENGTH-1; i > 0; i--) { 
      history[i] = history[i-1]; 
    } 

    history[0] = added_command; 
    *hist_index = *hist_index + 1; 
    //printf("%s\n", history[0]); prints correctly 
} 

アップデート: 私は、関数の外にinputにポインタを移動する(私がメインでそれを置く)とadd_history機能のためstrcpyの使用を含むソリューションの一部によって提案された変更を行いました。私がこの問題を最初に使っていたのは、配列を使って項目を '上に'回転させていたからです。しかし、履歴がすべての要素でいっぱいになる前に、初期化されていない場所にアクセスしていました。私は今、メインからarg_history[0]を印刷することができましたが、私はまだ何か他のものを印刷することに問題がありました(例えばarg_history [1])。しかしもっと重要なのは、私が実際に解決する必要があった機能であるget_historyから印刷することができなかったことです。より詳細な検査の後、hist_indexは配列にアクセスする前に値が与えられていないことを認識しました。みんな助けてくれてありがとう。もちろん

+0

私はstrtok(input、 "")があなたの入力行を壊したと思います。あなたはstrtok()の前とstrtok()の後にあなたが得たものを見るために入力をプリントアウトしようとするかもしれません。 – Shiping

+0

mainでargs []を出力した場合に 'get_input'を呼び出した後、正しく機能します。問題はarg_historyのデータにアクセスしています –

答えて

3
input[strlen(input) - 1] = NULL; // To remove new line character - the compiler doesn't like how I'm doing this 

それはしていません。これには多くの問題があります。たとえば、strlen(input)が0の場合、strlen(input) - 1は-1で、配列の-1番目の項目にアクセスしています... NULLはポインタで、ではなく、の文字値です。おそらくinput[strlen(input) - 1] = '\0';を意味しますが、より安全な解決策は次のようになります。

input[strcspn(input, "\n")] = '\0'; 

history[0] = added_command; 
*hist_index = *hist_index + 1; 
//printf("%s\n", history[0]); prints correctly 

この正しく印刷あなたはhistory[0]に割り当てるとポインタ値added_commandは、get_commandinputに指しているためまだ生きています。 get_commandが返されると、ポインタが指し示すオブジェクトが存在しなくなるので、history[0]ポインタも存在しません。

書籍を読んでいる場合は、strcpyを使用して文字列を割り当てる必要があります(K & R2Eなど)。これを行う前に、適切なサイズの新しいオブジェクトを作成する必要があります(mallocなど)。

これは、書籍を読んでいない人にとってよくある問題です...あなたはどの書籍を読んでいますか?


printf("%d %s\n", hist_index, *history); 
    // prints gibberish 

はまあ、はい、それはちんぷんかんぷんを出力しますので*historyはかつてget_commandが返される前にget_commandが戻ってきたとき、破壊された、指されているオブジェクト。本はあなたにこれを教えるだろう。

は、同様の説明についてもReturning an array using Cを見るここ

+0

返信いただきありがとうございます!その最初のセクションは非常に便利で、私はコンパイラをよく読むことを学ぶ必要があります。私はそれらの行に沿って何かを知っていたが、私はかなり理解していなかった。 私はあなたの思考の訓練に従っていますが、get_commandが戻ってくるとポインタがなくなってしまいます。なぜなら、printステートメントは返された場所に続く文字列全体をスペースまで印刷するからです。スペースがなければ、私はそれを指している歴史の中で今までにコマンドの全体を印刷します。スペースがあれば、そのスペースを指し示すでしょう。 –

+0

「どういう意味ですか?」などの警告メッセージについて別の質問をすることができます。あなたが理解していない部分を空欄に入れてください... – Sebivor

+0

あなたの懐疑に関して、ポインタがスタック空間を指していると考えてください。スタック領域を再利用する)、そのスタック領域は上書きされません。スタック領域が上書きされると、これはゴミがどこから来るかです。これは技術的に*未定義の動作*なので、避けてください。 – Sebivor

0

)は(はstrtokいくつかの説明です。あなたはコピーを置くのではなく、ヒストリリストに入力のポインタを置くだけなので、最初の単語だけを出力します。

char *strtok(char *str, const char *delim) 
Parameters 
str -- The contents of this string are modified and broken into smaller strings (tokens). 

delim -- This is the C string containing the delimiters. These may vary from one call to another. 
+0

助けてくれてありがとう!私はコピーから新しいポインタを直接入力からコピーする必要があることに同意しますが、 'strtok'がこれにどのように影響するのか混乱していますか? '' strtok''は '' arg [] 'に既にヒストリリストに入れられた後にのみ使用します。 –

+0

@PhilBaldoniポインタ自体だけでなく、入力のテキスト全体をコピーする必要があります。あなたが歴史に入れたのは、入力テキストの始点のアドレスだけです。しかし、同じアドレスをstrtokに渡して、そのアドレスのテキストを壊した(つまり、単語の間に '\ 0'を入れる)ので、保存されたアドレスのテキストを印刷しようとしたときに、最初の単語が得られました。問題を解決する1つの方法は、char copy [MAX_LINE]を追加することです。次にstrcpy(コピー、入力)を行います。入力が終わったら、arg = strtok(copy、 "");を実行します。 arg = strtok(input、 "")の代わりに。 – Shiping

関連する問題