質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.61%

  • C

    3563questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3332questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C言語 16進数→10進数への変換方法

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 6,360

ichitarou

score 1

プログラミング未経験、今月就職し1週間ほどC言語を勉強しています。

研修を受けている会社からの練習問題で6桁までの16進数を入力し、10進数に変えて出力する…という問題が出たのですが、
pow関数というのを見つけそれを使用したところ、「シフト演算(?)を使った方が早い」と言われてしまいました。

C言語で16進数をシフト演算を使用し、10進数に変換したいです。

ヒントを教えていただけないでしょうか。

また、powを使って書いたものは以下になります。
これとはまったく別物になるのでしょうか?
あと、書き方等のアドバイスが有ればお願いします。
研修中に出来るだけ知識をつけたいです。
よろしくおねがいします。

#include<stdio.h>
#include<math.h>
#include <string.h>

long HexToBin(char *str)
{
    long tmp;
    int i;
    long sum=0;
    
    for(i=0;i<strlen(str);i++)
    {
        if(*(str+i)>='0' && *(str+i)<='9')
        {
            tmp=*(str+i)-'0';
            sum=(tmp*pow(16,strlen(str)-i-1))+sum;
        }
        else if(*(str+i)>='A' && *(str+i)<='F')
        {
            tmp=*(str+i)-'A'+10;
            sum=(tmp*pow(16,strlen(str)-i-1))+sum;
        }
        else if(*(str+i)>='a' && *(str+i)<='f')
        {
            tmp=*(str+i)-'a'+10;
            sum=(tmp*pow(16,strlen(str)-i-1))+sum;
        }
        else
        {
            sum=-1;
            break;
        }
    }
    return (sum);
}

int main()
{
    char hex[50];
    long dec;
    
    printf("16進数を入力:");
    scanf("%s",&hex[0]);

    dec=HexToBin(&hex[0]);
    
    if(dec==-1)
        printf("入力された値が不正です。\n\n");
    else
    printf("結果:%ld\n\n",dec);

    return 0;
}

*returnの中を書き換え忘れていたので修正しました。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

16進数がお分かりだということは、2進数もきっとバッチリですね。
ヒント: 16と2の関係はなんでしょう?がんばってください。
シフト演算について解説されているページを共有します。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/08/14 02:46

    リンクありがとうございます!16進数だと2進数を4ビットずつずらしていけば良いのでしょうか…多分これはchar型の16進数の状態で使って良いんですよね。

    16進数→10進数の流ればかり考えてましたが、
    16進数→2進数→10進数の流れなのでしょうか…難しい!がんばってみます!

    キャンセル

  • 2015/08/14 03:27

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

    long HexToBin(char *str)
    {
    char tmp;
    int i;
    long sum=0;

    for(i=0;i<strlen(str);i++)
    {
    if(*(str+i)>='0' && *(str+i)<='9')
    {
    tmp=*(str+i)-'0';
    sum=(tmp<<4*(strlen(str)-i-1))+sum;
    }
    else if(*(str+i)>='A' && *(str+i)<='F')
    {
    tmp=*(str+i)-'A'+10;
    sum=(tmp<<4*(strlen(str)-i-1))+sum;
    }
    else if(*(str+i)>='a' && *(str+i)<='f')
    {
    tmp=*(str+i)-'a'+10;
    sum=(tmp<<4*(strlen(str)-i-1))+sum;
    }
    else
    {
    sum=-1;
    break;
    }
    }
    return (sum);
    }

    int main()
    {
    char hex[50];
    long dec;

    printf("16進数を入力:");
    scanf("%s",&hex[0]);

    dec=HexToBin(&hex[0]);

    if(dec==-1)
    printf("入力された値が不正です。\n\n");
    else
    printf("結果:%ld\n\n",dec);

    return 0;
    }


    powの部分を書き換えてできました!
    ありがとうございました!

    キャンセル

  • 2015/08/19 16:02 編集

    既に解決済みですが、この課題はまだまだやりようがありますので、三点修正案を出させていただきます。

    1. HexToBin()関数を見て、ごちゃごちゃした感じがしませんか。一番の理由は、ほぼ同じコードが三回繰り返されているからです。

    16進数に使われる文字は'0'~'9', 'A'~'F', 'a'~'f'です。これらはそれぞれ
    '0'~'9' → 0 ~ 9
    'A'~'F' → 10 ~ 15
    'a'~'f' → 10 ~ 15
    という値を意味しますから、文字を数値に変換する関数を作りましょう。仮にAscToBin()とすると、
    int AscToBin(char ch)
    {
       if (ch >= '0' && ch <= '9') {
          return ch - '0';
       } else if (ch >= 'A' && ch <= 'F') {
          return ch - 'A' + 10;
       } else if (ch >= 'a' && ch <= 'f') {
          return ch - 'a' + 10;
       } else {
          return -1;
       }
    }
    この関数は、toupper()(又はtolower())、isdigit(), isxdigit()などのライブラリ関数を使えば、さらに簡潔に書けます。考えてみて下さい。

    AscToBin()は各文字を0~15の値で返してくれるので、HexToBin() の本体部分は
       for (i = 0; i < strlen(str); i++) {
          tmp = AscToBin(*(str + i));
          if (tmp == -1) {
             return -1L;
          }
          sum = (tmp << 4 * (strlen(str) - i - 1)) + sum;
       }
       return (sum);
    だけで済んでしまいます!

    2. お作りになったコードは、入力した16進数文字列が例えば "31A" だったら、'3' という文字を 3 << 4 * 2 という値として、sum に足しこんでいます。即ち sum の値は 0(初期値)→0x300→0x310→0x31Aと変化しますね。

    このやり方では16進数の中の「桁位置」を計算しなくてはなりません。しかし、文字列というものは、大抵先頭から順に処理できるものです(そして、そうしたやり方のほうが汎用性がある)から、桁位置がわからなくても構わない事が多いのです。

    入力した16進数文字列が、例えば "31A"なら、スタートは先頭の'3'を数値3に変換する事です。
    次に '1' に差し掛かった時点で、先ほどの 3 は、実は "31" という16進数の'3'だった、のだから、それを16倍して48 (== 0x30)にし、これに 1 を足して 49 (== 0x31)にします。
    さらに'A'に差し掛かった時点で、また一桁大きくなるのだから 0x310 == 784 (== 49 * 16) に 'A'(== 10) を足す…。
    このやり方だと sum の値は 0→0x3→0x31→0x31Aと変化します。

    ここで、16倍する事を「4ビット左にシフト」する事で実現するのはお分かりですね。同様に、16倍する代りに10倍すれば10進数文字列を数値に変換できます。何進数でも基本的な手順は同じです。

    つまり、先ほどの for 文は、
       for (i = 0; i < strlen(str); i++) {
          tmp = AscToBin(*(str + i));
          if (tmp == -1) 省略;
          sum = (sum << 4) + tmp;
       }
    と書き換えられます。いかがですか。

    3. この for ループは、繰り返しの条件判定で strlen() を毎回呼び出す事が気になります。HexToBin()が呼ばれた時点で、入力された文字列が変化する事は無いのだから、呼び出すにしても関数の先頭で一度呼べば十分です。即ち
       len = strlen(str);
       for (i = 0; i < len; i++) {
    とするのも手ですが、、、

    C言語の文字列はヌル文字('\0')で終端する(最後にヌル文字がある)事をご存知ですか。入力した16進数の文字列も例外ではありません。この事を活かせば、上記のループは
       for (i = 0; *(str + i) != '\0'; i++) {
    で回すことができます。strlen()を呼び出す必要がありません。

    さらに、変数 i は配列の添字として働く(「*(str + i)」は「str[i]」と書き換えられる)が、str はポインタ「変数」なのだから、それを最大限活かすと、もはや変数 i も不要となり
       while (*str) { /* ←「while (*str != '\0') {」と同じ */
          tmp = AscToBin(*str++);
          ...
    のような書き方ができます。これはC言語らしい簡潔な書き方ですから、少しずつ慣れる事をお奨めします。

    キャンセル

  • 2015/08/21 20:09 編集

    返信が遅くなってしまい申し訳ありません!

    やりようでこんなに短くなるのですね…
    別の問題でもfor、ifを入れ子だらけ状態になってしまったり、
    本当に「ぐちゃぐちゃ」状態で自分の知識、発想力不足に大変悔しい思いをしております…(;_;)
    早速、ご紹介いただいた方法で試してみたいと思います!

    少しでも短くわかりやすく綺麗に済ませられるよう、訓練、精進したいと思います!
    お時間を割いてこんなにも詳しく説明して下さり、本当にありがとうございます!!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C

    3563questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    3332questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。