2016-06-11 14 views
-1

私はK & Rの本の演習に取り掛かりました。04-06を拡張しようとしている間に奇妙なバグに遭遇しました文字列名を持つ変数を許可します。実際には、私は実際にバグを修正することができましたが(後ほど説明します)、最初にエラーが発生した理由を知りたいと思います。snprintfに空でない文字列を渡すと、関連のないchar *配列がアドレスを変更する

問題に慣れていない人には、基本的には、文字名の変数を格納して呼び出すことができるコマンドライン計算機(ポーランド表記を使用)を作成するように求められます。

ここで問題が発生し、関連するコードです:

#define MAXOPLEN 1000 

int varCount = 1; 
char **keys; 
char **values; 
// changing the declaration to: 
// char strOps[][STROPCOUNT] = { ... }; 
// fixed the issue 
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print", 
          "clr", "sin", "cos", "tan", 
          "exp", "pow", "ln", "log", 
          "mem", "re"}; 

main() { 
    keys = malloc(varCount * sizeof(char[MAXOPLEN])); 
    keys[0] = "ans"; 
    values = malloc(varCount * sizeof(char[MAXOPLEN])); 
    values[0] = "0.0"; 

    ... // Other stuff related to the program 
} 

// flag is unrelated to the problem I'm asking about. It just checks to see 
// if the variable name used to store value n is 'ans', which is where 
// the last returned value is stored automatically 
void memorize(char s[], double n, bool flag) { 
    ... // small conditional block for flag 

    for (i = 0; i < varCount; i++) { 
     if (equals(keys[i], s)) { 
      found = True; 
      // Next line is where the program actually breaks 
      snprintf(values[i], MAXOPLEN, "%f", n); 
      break; 
     } 
    } 

    if (!found) { 
     i = varCount; 
     varCount++; 

     keys = realloc(keys, varCount * sizeof(char*)); 
     keys[i] = malloc(sizeof(char[MAXOPLEN])); 
     keys[i] = s; 

     values = realloc(values, varCount * sizeof(char*)); 
     values[i] = malloc(sizeof(char[MAXOPLEN])); 
     snprintf(values[i], MAXOPLEN, "%f", n); 
    } 
} 

をコンパイルして実行した後、あなたが計算する式に入力した最初の時間は、すべてがスムーズに実行するようです。しかし、デバッグ中にstrOpsの最初の3つのchar *が違ったアドレスを指すように変わっていることが分かりました。方程式の戻り値を "ans"に保存しようとすると、既に文字列sがキー名として使用されていたかどうかを確認しようとするmemorize()のfor-loopに入ります。これはsの値( "ans")と一致する文字列を指し示すキー[0]を正しく見つけ、double nを文字列に変換して値[0]に保存しようとします。

のsnprintf()関数内で、strOpsの最初の3つのchar *がcorecrt_stdio_config.hでこのメソッド内の他の場所指すようになされているが:

_Check_return_ _Ret_notnull_ 
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void) 
{ 
    // Error occurs after this next line: 
    static unsigned __int64 _OptionsStorage; 
    return &_OptionsStorage; 
} 

上記のコードにコメントしたように、strOps 2Dを製造します(charポインタの配列ではなく)文字配列が問題を解決しました。文字の配列には個々の文字の値を変更することはできないが、私が理解していないのは、なぜcorecrt_stdio_config.hのそのメソッドが最初の3つのポインタの値を変更したのかということです。

ありがとうございます!

+0

正しいプロトタイプ関数宣言子の使用を開始してください。 'main()'は推奨されません。さらに最近の本を使用すると、K&Rは古くから古くなっています。それは現代Cを教えていない!そして[ask]を見て、[mcve]を提供してください。あなたのコードが警告なしでコンパイルされても、それは意味をなさない。 – Olaf

+0

さらに、実行に関連する関数の結果を確認してください! 'realloc'が失敗するかもしれません! – Olaf

答えて

2

あなたの初期化は間違っているとの変化を引き起こしている:

keys[0] = "ans"; 
values[0] = "0.0"; 

"ans""0.0"の両方が文字列リテラルであり、配列を初期化するために使用することはできません、あなたが割り当てた後strcpyを使用する必要があります。

strcpy (keys, "ans"); 
strcpy (values, "0.0"); 

あなたの他のオプションは、一度に1つの文字を割り当てることです:

size_t i; 
char *p = "ans"; 
for (i = 0; i < strlen (p); i++) 
    keys[i] = p[i];     /* copy to keys */ 
p[i] = 0;       /* nul-terminate */ 

注:これらはあなたのエラーの例ですが、あなたのコード全体で同じことを行います。

+0

あなたがしようとすると何が起こるか少し説明できますか? keys [0] = "ans"; ? – velocirabbit

+0

'strOps'は、文字列リテラルのリストに初期化されたポインタの配列です。 'keys'はchar *へのポインタへのポインタです。'keys'を割り当てると、' varCount * sizeof(char [MAXOPLEN]) '('〜125、初期化されていないポインタ 'のために十分な' 1000'バイト)のブロックを作成します。 'keys [0] = 'ans';'を代入すると、 '' ans ''(読み出し専用メモリ)のアドレスを' keys'の最初のポインタに代入します。 char *へのポインタとして 'keys'にアクセスしようとすると' keys * 'のアドレスが変更されているように見えます(*配列の*アドレスは最初の要素の*アドレスです)。 –

+0

意味があります。キー[0]の値を後で変更すると、なぜstrOpsの最初の3つのポインタが変更されたのですか?私が知る限り、彼らは決して関連していません。 – velocirabbit

関連する問題