6

私は C を学んでいますが、K&R 演習の 1 つであるリストがわかりません。

htoi(s)練習問題 2-3、16進数の文字列 (オプションの0xorを含む0X) を等価の整数値に変換する関数 を書きなさい 。使用できる数字は、 09afAFです。

ここで再帰を行う必要があると思いますが、番号付けの種類やそれらのさまざまな変換方法などについてあまり知らないと思います.

誰かがそれを理解するための最良の方法についていくつかの指針を与えることができますか?とprintf("%x", skill);

4

8 に答える 8

12

再帰は必要ありません。基数位置の乗数である 1 桁の変換時間を合計して、文字列を逆方向にループする (つまり、単位の列から開始する) だけで済みます。これは擬似コードであり、オプションの 0x プレフィックスを処理しません (また、オーバーフローの可能性をチェックしていません):

long total = 0;
long multiplier = 1;
for (int i = string.length - 1; i >= 0 i--)
{
   digit = ConvertSingleHexDigittoInt(string[i]);
   total += digit * multiplier;
   multiplier *= 16;
}

ConvertSingleHexDigittoInt() の簡単な実装はあなたに任せました:)

于 2009-04-25T01:27:28.270 に答える
5

Mitch は基本的な考え方を正しく理解していますが、もう少し詳しく見てみましょう。

16 進数は 16 進数です。つまり、数字 (右から左) は次の値になります。

桁×16 0(つまり1)
桁×16 1(つまり16)
桁×16 2(256)

等々。たとえば、0xE は 14 です。

必要なのは、文字列の右端から始まるループです。文字列が s だとしましょう。length(s) は文字列の長さです。疑似コードでは、あなたが望む

value = 0
r = 1   // ask yourself "what values does r take as this proceeds?"
for i from length(s)-1 to 0   // Ask yourself "why length(s)-1?"
   value = value + (digitval(s[i])*r)
   // get ready for the next digit
   r = r * 16

digitval(char c)"0123456789ABCDEF" の checract を 0 から 15 (両端を含む) の値に変換する関数である必要があります。これは演習として残しておきますが、1 つのヒントは「配列」です。

1 つの余分な問題に注意してください。先頭に「0」または「0x」がある可能性があるため、これらのケースを確実に処理する必要があります。

于 2009-04-25T01:38:14.653 に答える
4

文字列を左から右に処理する方が簡単で、数学に慣れている人にとっては間違いなく読みやすいでしょう。戦略とは、例えば、1234 = (((1 x 10) + 2) x 10 + 3) x 10 + 4

つまり、各桁を左から右に処理するときに、前の合計に基数を掛けて、事実上 1 桁分「左に移動」してから、新しい桁を追加します。

long decFromHexStr(const char *hexStr)
{
    int i;
    long decResult = 0;  // Decimal result

    for (i=0;  i < strlen(hexStr);  ++i)
    {
        decResult = 16 * decResult + decFromHexChar(hexStr[i]);
    }
    return decResult;
}

経験豊富なプログラマーは、文字列を配列として扱うのではなく、ポインターを使用して文字列をステップ実行する可能性があります。

long decFromHexStr(const char *pHex)
{
    long decResult = 0;

    while (*pHex != '\0')
    {
        decResult = 16 * decResult + decFromHexChar(*pHex++);
    }
    return decResult;
}

学んでいるので、コーディング スタイルを研究し、それが役立つかどうかを判断することは価値があります。そうすれば、早い段階で良い習慣を身につけることができます。

楽しむ!

于 2009-04-25T02:14:11.883 に答える
2

16 進数の実際の意味は何ですか? 15FAを取りましょう。その意味は

1 * 16^3 + 5 * 16^2 + 15 * 16^1 + 10 * 16^0

Aは 10 を表し、Bは11 を表し、Fは 15 を表します。また、16^0 は 1 です。

したがって、上記の式の値を計算するだけです。最も簡単な方法は、おそらく次の順序で行うことです。

10 * 1
15 * 16
5  * 256   //256  = 16 * 16
1  * 4096  //4096 = 16 * 16 * 16

数字がさらにある場合、これはさらに続く可能性があります。本当に必要なのは、ループといくつかの変数だけです。

上記の式を次のように因数分解することで説明される別の方法があります。

((1 * 16 + 5) * 16 + 15) * 16 + 10

必要に応じて、これらの方法をそれぞれ試してください。

より高度な情報:

基本的に、コンピューターはすべての数値と計算に基数 2 (バイナリとも呼ばれます) を使用します。文字列「1A6DC0」でさえ、1 と 0 でエンコードされ、最終的には文字と数字として画面に表示されます。

コンピューターがバイナリを使用しているという事実を利用できる場合もありますが、通常は考える必要はありません。

たとえば、あなたがするとき

x = (11 + y) * 6;

ある段階で 11 と 6 が一連の高電圧と低電圧として表されることを心配する必要はありません。期待どおりに機能します。10 進数 (私たちが使用する数値システム) と 2 進数との間の変換は、コンピューターが簡単に実行できる単純なプロセスです。

ただし、16 進数と 2 進数の間で変換する場合は、ショートカットがあります。2 進数の 4 桁は 1 桁の 16 進数と同じであるため、各 16 進数を個別に 2 進数に変換し、それらをつなぎ合わせることができます。

たとえば、15FAは次のように展開されます。

1 -> 0001
5 -> 0101
F -> 1111
A -> 1010
15FA -> 0001 0101 1111 1010

これは通常、直接行うことはできず、通常は論理和とビット シフト (|および<<) を伴うことに注意してください。楽しいもの。

于 2009-04-25T02:32:59.140 に答える
1

私の失礼な英語で説明してみてください:(

私のコード(すべての入力が正しいと仮定します。防御的なプログラミングは避けてください)

#include <stdio.h>


enum { SZ = 11 };

unsigned int htoi(const char *s);


int main()
{

  char buff[SZ];  //Max 11 char: 0x XX XX XX XX '\0' (2 + 8 + 1)

  while(fscanf(stdin, "%s", buff) != EOF)
    printf("%X\n", htoi(buff) ); 

  return 0;
}


unsigned int htoi(const char *s)
{
  unsigned int i, r = 0;

  for(i = (s[1] == 'x') ? 2 : 0; s[i] != '\0'; i++)
    r = ( r << 4 ) +  ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF );

  return r;
}

まず、r = 0を割り当てます。次に、for-bucleを開始するときに、インデックス変数iにinit値を指定します。文字列が0x形式であるかどうかを確認する必要があります。位置1をチェックするだけで、入力文字列を0x形式で処理しているかどうかを知ることができます。

これで、最初の正しい文字を指すインデックスができました。反復ごとに、4ビットを左に移動します。4つのゼロを取得します。新しい16進数を追加するための完璧なギャップ!例:

Input: 0xBE1234

Is s[1] == 'x' ? true then i = 2;
r = 0;

iter 1: r = 0x0; r = 0x0; r = 0xB;
iter 2: r = 0xB; r = 0xB0; r = 0xBE;
iter 3: r = 0xBE; r = 0xBE0; r = 0xBE1;
iter 4: r = 0xBE1; r = 0xBE10; r = 0xBE12;
iter 5: r = 0xBE12; r = 0xBE120; r = 0xBE123;
iter 6: r = 0xBE123; r = 0xBE1230; r = 0xBE1234

これは少し複雑かもしれません:

 r = ( r << 4 ) + ( (s[i] > '9') ? 0x9 : 0x0 ) + ( s[i] & 0xF );

まず、16ごとの乗算と同じですが、より効率的な4ビットを置き換えます。次に、「9」より大きいASCII文字があるかどうかを調べます。それが本当なら、私たちはA、B、C、D、E、Fまたはa、b、c、d、e、fで作業しています。正しい入力があると想定していることを忘れないでください。では、ASCIIテーブルを見てみましょう。

A = 0100 0001  -  a = 0110 0001
...
F = 0100 0110  -  f = 0110 0110

しかし、私たちはこのようなものが欲しいです:

A = 0000 1010  -  a = 0000 1010
...
F = 0000 1111  -  f = 0000 1111

どうすればいいですか?置換後、マスクs[i]と0xFを使用して4つの最上位ビットをクリアします。

s[2] == 'B' == 0100 0010
s[2] & 0xF == 0000 0010

整数値に適応するために9を追加します({'A' ...'F'、'a' ...'f'}のs[i]の場合のみ)

s[2] & 0xF + 0x9 = 0000 0010 + 0000 1001 = 0000 1011 (0xB)

最後に、置き換えられたr値に追加し、rに割り当てます。2回目の反復の実行シーケンス(s [3]):

r == 0xB, s[3] == 'E' == 0100 0101 (start iter 2)
(r << 4) == 0xB0, s[3] == 'E' == 0100 0101 (displacement r << 4 )
(r << 4) == 0xB0, (s[3] & 0xF + 0x9) == 0000 1110 == 0xE (clear most significant bits of s[3] and add 0x9)
r = (r << 4) + ( s[3] & 0xF + 0x9 ) == 0xBE == 1011 1110 (add all and assign to r)

s [4]のような数字があるとどうなりますか?

s[4] == '1' == 0011 0001
s[4] & 0xF == 0000 0001

変位rの4つの位置、0(なし)を追加し、論理演算s [i]&0xFの結果を追加し、最後にrに割り当てます。

r == 0xBE, s[4] == '1' == 0011 0001 (start iter 3)
(r << 4) == 0xBE0, s[4] == '1' == 0011 0001 (displacement r << 4 )
(r << 4) == 0xBE0, (s[4] & 0xF + 0x0) == 0000 0001 (clear most significant bits of s[4] and add 0)
r = (r << 4) + s[4] & 0xF == 0xBE1 == 1011 1110 0001 (add all and assign)

4つのゼロのギャップで重要度の低いビットを追加しているため、桁ビットをメッシュしないように4をシフトすることを忘れないでください。

PD:申し訳ありませんが、より良い説明のために英語を上達させることを約束します。

于 2009-04-25T06:55:06.343 に答える
1

私はおそらく大きな貢献をしていません。上に良い答えがあります。でもやってみます。

他の人が私より前に行ったように、いくつかの機能を実装するために残しておきます。

int htoi(const char* x)
{

        unsigned int current_position;/*current position is to be defined*/
        int prefixed=0;                                                         
        int dec=0;
        char* y = x;

        if (x && x+1 && (*(x+1)=='x' || *(x+1)=='X')){  /*Is 0x or 0X prefix present?*/
                prefixed= PREFIXED;             
        }

        if (prefixed) y+=2; /*Jumps over 0x or 0X*/     


        while (*y){
                /*getPos(const char*) and singleHexToDec(const char*,unsigned int) functions to be implemented*/
                current_position=getPos(y);
                dec+=singleHexToDec(y,current_position); 
        }
        return dec;
}
于 2009-04-25T03:53:41.567 に答える
1

従来のアプローチでは、左から右に変換されます。アキュムレータは最初にゼロに設定され、新しい各桁の等価値をループに追加する前に 16 で乗算されます。

オプションhtoi()の先頭0xに . s[0]andの値を直接チェックすることは、s[1]おそらく最も明確なアプローチです。

数字が ASCII であることがわかっている場合は、s[i] - '0'およびのような式を使用s[i] - 'A' + 10して、i 番目の数字を整数値に変換できます。

おそらく、健全性のためにすべてを 1 つのケースにまとめたいと思うでしょう。

編集:この演習の観点から、ポインターは未来からのものであるという観察との一貫性のために変更*sされました。s[i]

個々の数字を値に変換するには、他にもいくつかの方法があることに注意してください。たとえば、すべての数字のベクトル ( のようなものstrchr("0123456789ABCDEF",s[i])) でそれらを検索し、各位置の各数字の値を持つ文字コードによってインデックス付けされた単一のルックアップ テーブルを構築し (適切に初期化されdigitvalue[s[i]]た後) 、別の回答で提案されているように、可能な桁ごとにラベルを付けるか、上記で提案したように範囲チェックと算術演算を使用します。考えるべきことは、どれを選択するか、そしてその理由です。当然の選択ではない可能性があることに注意してください。また、選択した文字セットが ASCII でない場合、最良の答えは異なる可能性があります。int digitvalue[256]switch (s[i])case

于 2009-04-25T01:55:24.690 に答える
-2

昨日、私はこのような関数を書きました。以下の私のコードを見ることができます。

/* Converting a hex string to integer, assuming the heading 
   0x or 0X has already been removed and pch is not NULL */
int hex_str_to_int(const char* pch) {

    int value = 0;
    int digit = 0;

    for (; *pch; ++pch) {

        if (*pch >= '0' && *pch <= '9') {
            digit = (*pch - '0');
        } else if (*pch >= 'A' && *pch <= 'F') {
            digit = (*pch - 'A' + 10);
        } else if (*pch >= 'a' && *pch <= 'f') {
            digit = (*pch - 'a' + 10);
        } else {
            break;
        }

        // Check for integer overflow
        if ((value *= 16) < 0 || (value += digit) < 0) {
            return INT_MAX;
        }
    }

    return value;
}

テストコードは次のとおりです。

int main(void) {

    printf("%d %d\n", hex_str_to_int("0"), 0x0);
    printf("%d %d\n", hex_str_to_int("A"), 0xA);
    printf("%d %d\n", hex_str_to_int("10"), 0x10);
    printf("%d %d\n", hex_str_to_int("A1"), 0xA1);
    printf("%d %d\n", hex_str_to_int("AB"), 0xAB);
    printf("%d %d\n", hex_str_to_int("100"), 0x100);
    printf("%d %d\n", hex_str_to_int("1A2"), 0x1A2);
    printf("%d %d\n", hex_str_to_int("10A"), 0x10A);
    printf("%d %d\n", hex_str_to_int("7FFFFFF"), 0x7FFFFFF);
    printf("%d %d\n", hex_str_to_int("7FFFFFF1"), 0x7FFFFFF1);
    printf("%d %d\n", hex_str_to_int("7FFFFFF2"), 0x7FFFFFF2);
    printf("%d %d\n", hex_str_to_int("7FFFFFFE"), 0x7FFFFFFE);
    printf("%d %d\n", hex_str_to_int("7FFFFFFF"), 0x7FFFFFFF);
    printf("%d %d\n", hex_str_to_int("80000000"), 0x7FFFFFFF + 1);
    printf("%d %d\n", hex_str_to_int("80000001"), 0x7FFFFFFF + 2);

    printf("%d %d\n", hex_str_to_int("10AX"), 0x10A);   
    printf("%d %d\n", hex_str_to_int("203!"), 0x203);

    return 0;
}

次の値を出力します。

0 0
10 10
16 16
161 161
171 171
256 256
418 418
266 266
134217727 134217727
2147483633 2147483633
2147483634 2147483634
2147483646 2147483646
2147483647 2147483647
2147483647 -2147483648
2147483647 -2147483647
266 266
515 515
于 2009-04-25T01:53:28.517 に答える