16進数の下位2桁を取り出す処理について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
sadora3
記事: 175
登録日時: 7年前

16進数の下位2桁を取り出す処理について

#1

投稿記事 by sadora3 » 2年前

学校の課題です。下記のような問題が出ましたが、サッパリ分かりません。
リトルエンディアンについて調べたのですが、理解出来ませんでした。
バイトオーダーがビックエンディアンとリトルエンディアンでは、実行結果が変わるのでしょうか?

それと、どうすれば16進数の下位2を取り出せますか?
桁数さえわかれば、あとはシフトして上位桁を追い出していくだけだと思うのですが、16進数の値の桁数の求め方とか分かりません。

OS:Windows10
コンパイラ:VisualStudio2010
言語:C

コード:

/*
以下のプログラムは「入力された整数の16進数下2ケタを表示する」プログラムである。
例・0x12345678 → 78  0x3456 → 56
「空欄」と記された部分を埋めてプログラムを完成させよ。
なお、このプログラムが動作するコンピュータのバイトオーダーはリトルエンディアンとする。
*/

#include<stdio.h>

//空欄1
void DispSimo(short* temp);
void DispSimo(long* temp);
//空欄1

void main(){
	short a;
	long b;

	printf("1つ目の整数を16進数で入力:");
	scanf("%hx", &a);
	DispSimo(&a);		//変数aの16進数下2ケタを表示

	printf("2つ目の整数を16進数で入力:");
	scanf("%lx", &b);
	DispSimo(&b);		//変数bの16進数下2ケタを表示

	rewind(stdin);
	getchar();
}

//空欄2
void DispSimo(short* temp){

	printf("%x\n", *temp);
}

void DispSimo(long* temp){

	printf("%x\n", *temp);
}
//空欄2

box
記事: 1735
登録日時: 8年前

Re: 16進数の下位2桁を取り出す処理について

#2

投稿記事 by box » 2年前

sadora3 さんが書きました: 言語:C
C言語において
sadora3 さんが書きました:

コード:

void DispSimo(short* temp);
void DispSimo(long* temp);
このように同じ名前で違う型の引数を持つ関数は複数定義できないはずです。
どこかで何かが間違っているような気がします。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#3

投稿記事 by sadora3 » 2年前

そうだったのですか・・・ではこの記述では間違っていますね。
では、
void DispSimo(void* temp);
こうなるのでしょうか?
しかし、この記述ですと実体にアクセスするにはどうすればいいのでしょうか?
void*の変数は、実体を使うためにはキャストが必要ですよね?

C6b14

Re: 16進数の下位2桁を取り出す処理について

#4

投稿記事 by C6b14 » 2年前

リトルエンディアンのマシンなら これで いいはず だけど (インテルはリトルエンディアンです)試してみてください。

コード:

void DispSimo1(short* temp) {
	*temp = *temp & 0xFF;
	printf("%x\n", *temp);
}
void DispSimo2(long* temp) {
	*temp = *temp & 0xFF;
	printf("%x\n", *temp);
}

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#5

投稿記事 by sadora3 » 2年前

*temp = *temp & 0xFF;
このやり方ですと、上位2ケタを取り出していませんか?
それともリトルエンディアンは、この記述で下位2ケタを取り出したことになるのでしょうか?

C6b14

Re: 16進数の下位2桁を取り出す処理について

#6

投稿記事 by C6b14 » 2年前

私のマシンでは 正解が でます。(インテルCPUです)

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#7

投稿記事 by みけCAT » 2年前

C6b14 さんが書きました:リトルエンディアンのマシンなら これで いいはず だけど
ダメですね。
%xはunsigned int型のデータを期待するので、long型のデータを渡してしまうと未定義動作になり、int型とlong型のデータが異なる環境では誤動作の原因になるかもしれません。
リトルエンディアンならたまたま不都合を踏まないかもしれませんが、未定義動作を起こすのは美しくありません。

(shortは可変長引数に渡すときはintに変換され、符号付きでも符号なしでも表現できる値を符号の有無だけが違う所に渡すのは問題ないので、セーフでしょう)

(unsigned) shortを出力するにはhを、(unsigned) longを出力するにはlを%とxの間に入れますが、
今回の場合は出力する値が十分小さい(intは少なくとも-32767~32767の値を、unsigned intは少なくとも0~65535の値を格納できる)ので、
キャストするのが簡単でしょう。
ついでに、必ず2桁出力するように桁数の指定を入れてみました。

コード:

void DispSimo1(short* temp) {
	*temp = *temp & 0xFF;
	printf("%02x\n", (unsigned int)*temp);
}
void DispSimo2(long* temp) {
	*temp = *temp & 0xFF;
	printf("%02x\n", (unsigned int)*temp);
}
C6b14 さんが書きました:試してみてください。
ちゃんと警告が出ますね。
コード

コード:

#include <stdio.h>

void DispSimo1(short* temp) {
	*temp = *temp & 0xFF;
	printf("%x\n", *temp);
}
void DispSimo2(long* temp) {
	*temp = *temp & 0xFF;
	printf("%x\n", *temp);
}

int main(void){
    return 0;
}
コンパイラの出力

コード:

prog.c: In function 'DispSimo2':
prog.c:9:2: warning: format '%x' expects argument of type 'unsigned int', but argument 2 has type 'long int' [-Wformat=]
  printf("%x\n", *temp);
  ^
http://melpon.org/wandbox/permlink/5IjH7uSuHeiNRii8
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#8

投稿記事 by みけCAT » 2年前

sadora3 さんが書きました:*temp = *temp & 0xFF;
このやり方ですと、上位2ケタを取り出していませんか?
それともリトルエンディアンは、この記述で下位2ケタを取り出したことになるのでしょうか?
(まともな処理系なら)エンディアンに関係なく、下位2ケタを残し、他のケタを0にする(=下位2ケタを取り出す?)でしょう。
なぜなら、C言語の(抽象機械上の計算として)定義された演算のみを使用し、エンディアンなどの具体的な計算機の性質に依存しないコードだからです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#9

投稿記事 by sadora3 » 2年前

あ!実行せずに変なこといって申し訳ありませんでした。
実行してみたらちゃんと下位2桁を取り出せていました。
以下のような計算になると勘違いしていました・・・。本当に申し訳ありません。

例えば4A5Bと入力したとする。
4A5B
FF
これの論理積になるので、出力は4Aとなる。

では、この問題はどうやって関数のプロトタイプ宣言を一つで済ませるか。という問題なのですね。
void DispSimo(void* temp);
これであっているのでしょうか?
また、こうした場合、実体を使うにはどうすればいいのでしょうか?

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#10

投稿記事 by みけCAT » 2年前

sadora3 さんが書きました:では、
void DispSimo(void* temp);
こうなるのでしょうか?
しかし、この記述ですと実体にアクセスするにはどうすればいいのでしょうか?
void*の変数は、実体を使うためにはキャストが必要ですよね?
キャストしてデリファレンスすればいいのでしょう。
unsigned char型を含むcharacter typeのポインタを通じてなら、任意のオブジェクト(の中のバイト)へのアクセスが許されます。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C6b14

Re: 16進数の下位2桁を取り出す処理について

#11

投稿記事 by C6b14 » 2年前

もちろん プロトタイプ宣言 も 直す 必要が あります。C 言語は よく分からないけど 機械語 では これで 下位 がでるはずです。( コードは もとのまま 追加するだけです )

コード:

#include<stdio.h>

//空欄1
void DispSimo1(short* temp);
void DispSimo2(long* temp);
//空欄1

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#12

投稿記事 by sadora3 » 2年前

>>みけCATさん
申し訳ありませんが、仰っていることが分かりません・・・。
キャストはtempをキャストするのでしょうか?また、何型にキャストするのでしょうか?
「unsigned char型を含むcharacter typeのポインタ」というものが理解できません。character typeのポインタとはなんでしょうか?

>>C6b14さん
もしプロトタイプ宣言部をそのように直してしまうと、main関数内の関数使用部を変更しなければならなくなってしまいます。
問題文に、「「空欄」と記された部分を埋めてプログラムを完成させよ。」とあるように、空欄以外の部分は触ってはいけない決りとなっているためその方法は取れません。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#13

投稿記事 by みけCAT » 2年前

空欄以外を変えずに、関数を分けるプログラムを考えてみました。

コード:

/*
以下のプログラムは「入力された整数の16進数下2ケタを表示する」プログラムである。
例・0x12345678 → 78  0x3456 → 56
「空欄」と記された部分を埋めてプログラムを完成させよ。
なお、このプログラムが動作するコンピュータのバイトオーダーはリトルエンディアンとする。
*/

#include<stdio.h>

//空欄1
int afunc(short* x) {
	return printf("%02x\n", (unsigned int)(*x & 0xFF));
}

int bfunc(long* x) {
	return printf("%02x\n", (unsigned int)(*x & 0xFF));
}

#define DispSimo(x) 1 x ## func(x)
//空欄1

void main(){
	short a;
	long b;

	printf("1つ目の整数を16進数で入力:");
	scanf("%hx", &a);
	DispSimo(&a);		//変数aの16進数下2ケタを表示

	printf("2つ目の整数を16進数で入力:");
	scanf("%lx", &b);
	DispSimo(&b);		//変数bの16進数下2ケタを表示

	rewind(stdin);
	getchar();
}

//空欄2

//空欄2
&の存在をダミーの計算をさせることで邪魔にならないようにしつつ、トークンを結合する(プリプロセッサの)演算子を用いて別の関数を使えるようにしました。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C6b14

Re: 16進数の下位2桁を取り出す処理について

#14

投稿記事 by C6b14 » 2年前

ただ 関数 を 2個に しただけ ですよ。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#15

投稿記事 by みけCAT » 2年前

もしくは、void main()やrewind(stdin)を含む気持ち悪いプログラムは潰してしまえばいいでしょう。

コード:

/*
以下のプログラムは「入力された整数の16進数下2ケタを表示する」プログラムである。
例・0x12345678 → 78  0x3456 → 56
「空欄」と記された部分を埋めてプログラムを完成させよ。
なお、このプログラムが動作するコンピュータのバイトオーダーはリトルエンディアンとする。
*/

#include<stdio.h>

//空欄1
int main(void) {
	/* ここに好きなように実装を書く! */
	/* もちろん、このmain関数の前に関数を定義して使ってもOK! */
	return 0;
}

/* #if 0 ~ #endif で囲まれた部分はプリプロセッサに消し去ってもらえる! */
#if 0
//空欄1

void main(){
	short a;
	long b;

	printf("1つ目の整数を16進数で入力:");
	scanf("%hx", &a);
	DispSimo(&a);		//変数aの16進数下2ケタを表示

	printf("2つ目の整数を16進数で入力:");
	scanf("%lx", &b);
	DispSimo(&b);		//変数bの16進数下2ケタを表示

	rewind(stdin);
	getchar();
}

//空欄2
#endif
//空欄2
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#16

投稿記事 by sadora3 » 2年前

なんてコードだとびっくりしました!
defineを使う発想は思いつきもしませんでした。
しかも、2つめの回答もいい意味でいかれています!
こんな天才的な発想がよく出来ますね!
みけCATさんにはいつもお世話になっています。本当にありがとうございました。

この質問に回答してくれた他のお二方もありがとうございました!
物凄く勉強になりました!

C6b14

Re: 16進数の下位2桁を取り出す処理について

#17

投稿記事 by C6b14 » 2年前

>もしプロトタイプ宣言部をそのように直してしまうと、main関数内の関数使用部を変更しなければならなくなってしまいます。
>問題文に、「「空欄」と記された部分を埋めてプログラムを完成させよ。」とあるように、空欄以外の部分は触ってはいけない決りとなってい>るためその方法は取れません。
あ、ひっかかって しまった。 なぜ rewind(stdin); が使って あるかですね。


C6b14

Re: 16進数の下位2桁を取り出す処理について

#19

投稿記事 by C6b14 » 2年前

[追記]ビッグエンディアンとリトルエンディアンの 問題は バイトオーダーの 問題 なので C 言語 上は 同じ です。 バイト の 中身 が 逆 ( 答え 5B なら B5 )です。卵 の  さき どちらか ( 大きなほうか 小さなほうか ) に由来してるで どっちにしろ卵に かわりは 無い という たとえばなし です。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#20

投稿記事 by みけCAT » 2年前

C6b14 さんが書きました:バイト の 中身 が 逆 ( 答え 5B なら B5 )です。
いいえ。バイトの中身ではなく、バイトを配置する順番が違います。
例えば、0xDEADBEEFという値は、(バイト単位でアドレスがついている)メモリ上のデータは
リトルエンディアンだとEF BE AD DE
ビッグエンディアンだとDE AD BE EF
となります。

エンディアン - Wikipedia
バイトオーダ - ビッグエンディアン/リトルエディアン
最後に編集したユーザー みけCAT on 2016年12月01日(木) 09:54 [ 編集 1 回目 ]
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C6b14

Re: 16進数の下位2桁を取り出す処理について

#21

投稿記事 by C6b14 » 2年前

ごめんなさい。”卵 の  さきは どちらか” です。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#22

投稿記事 by みけCAT » 2年前

もしもバイトの中身が逆だとしたら、どうして5B (0101 1011)はDA (1101 1010)ではなくB5になるのですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C6b14

Re: 16進数の下位2桁を取り出す処理について

#23

投稿記事 by C6b14 » 2年前

えーと ゆっくり 検証 してみますね。ややこしい 話なので。

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#24

投稿記事 by sadora3 » 2年前

C言語上ではバイトオーダーは気にしなくてもいいのですね。
では、なぜリトルとビッグがあるのでしょうか?
実行速度が若干変わったりするのでしょうか?

Egg

Re: 16進数の下位2桁を取り出す処理について

#25

投稿記事 by Egg » 2年前

オフトピック
ハッカーに反論するなら半端な知識じゃだめだし、言葉足らずな省略は通用しない
仕様書に記載するがごとく会話しなければ、突っ込まれるだけ

Egg

Re: 16進数の下位2桁を取り出す処理について

#26

投稿記事 by Egg » 2年前

>では、なぜリトルとビッグがあるのでしょうか?
https://msdn.microsoft.com/ja-jp/library/cc354745.aspx
こういうのに対応するためです。
順序を逆にする必要があるのは、Microsoft Windows オペレーティング システムがリトルエンディアン アーキテクチャを使っているからである。'Y' = 0x59、'U' = 0x55、および '2' = 0x32 なので、'2YUY' は 0x32595559 になる。
逆に言えばビッグエンディアン アーキテクチャの処理系なら
'YUY2' 0x59 0x55 0x59 0x32の順に1バイトづつ代入しないと'2YUY'とコンピュータが理解してしまって
「何やこのファイル、2YUYなんてコーデック、ワイ持ってないから再生できないわ」って言われます。

sadora3
記事: 175
登録日時: 7年前

Re: 16進数の下位2桁を取り出す処理について

#27

投稿記事 by sadora3 » 2年前

回答ありがとうございます。
今の私には難しい問題だと思いました・・・。
とても理解出来ませんでした。

アバター
みけCAT
記事: 6150
登録日時: 8年前
住所: 千葉県
連絡を取る:

Re: 16進数の下位2桁を取り出す処理について

#28

投稿記事 by みけCAT » 2年前

Egg さんが書きました:>では、なぜリトルとビッグがあるのでしょうか?
https://msdn.microsoft.com/ja-jp/library/cc354745.aspx
こういうのに対応するためです。
順序を逆にする必要があるのは、Microsoft Windows オペレーティング システムがリトルエンディアン アーキテクチャを使っているからである。'Y' = 0x59、'U' = 0x55、および '2' = 0x32 なので、'2YUY' は 0x32595559 になる。
逆に言えばビッグエンディアン アーキテクチャの処理系なら
'YUY2' 0x59 0x55 0x59 0x32の順に1バイトづつ代入しないと'2YUY'とコンピュータが理解してしまって
「何やこのファイル、2YUYなんてコーデック、ワイ持ってないから再生できないわ」って言われます。
これは違うエンディアンがあることの短所であって、違うエンディアンがある理由(長所)ではないでしょう。

異なるエンディアンがあるのは、それぞれに利点があるからでしょう。
リトルエンディアンは「異なる大きさの整数にするとき、下位バイトに触らず上位バイトのみ伸ばしたり縮めたりすればよい」
ビッグエンディアンは「メモリダンプを見た時、十六進数そのままの順番で表示されるので人間にとってわかりやすい」
などの利点があります。

[search=google]エンディアン なぜ[/search]
[search=google]リトルエンディアン 利点[/search]
[search=google]ビッグエンディアン 利点[/search]

↓数学的な美しさや、ビッグエンディアンで上から受信したほうが位置が決めやすいなどの特長が述べられています。
memo0024 - K-kiwi
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

C6b14

Re: 16進数の下位2桁を取り出す処理について

#29

投稿記事 by C6b14 » 2年前

いま VC2015 に アセンブラ と 逆アセンブラ をさせてみると 明らか WORD 単位 で 逆さまに 入ってます。
すみません。BYTE 単位 と WORD単位 の 勘違い みたいです。

コード:

/*
以下のプログラムは「入力された整数の16進数下2ケタを表示する」プログラムである。
例・0x12345678 → 78  0x3456 → 56
「空欄」と記された部分を埋めてプログラムを完成させよ。
なお、このプログラムが動作するコンピュータのバイトオーダーはリトルエンディアンとする。
*/
int main()
{
	int x = 0x12345678;
	short y = 0x3456;
	short z = 0x4A5B;
	return 0;
}

コード:

]
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.00.24215.1 

	TITLE	D:\csi\_c\c1.c
	.686P
	.XMM
	include listing.inc
	.model	flat

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC	_main
; Function compile flags: /Odtp
_TEXT	SEGMENT
_x$ = -12						; size = 4
_z$ = -8						; size = 2
_y$ = -4						; size = 2
_main	PROC
; File d:\csi\_c\c1.c
; Line 8
	push	ebp
	mov	ebp, esp
	sub	esp, 12					; 0000000cH
; Line 9
	mov	DWORD PTR _x$[ebp], 305419896		; 12345678H
; Line 10
	mov	eax, 13398				; 00003456H
	mov	WORD PTR _y$[ebp], ax
; Line 11
	mov	ecx, 19035				; 00004a5bH
	mov	WORD PTR _z$[ebp], cx
; Line 12
	xor	eax, eax
; Line 13
	mov	esp, ebp
	pop	ebp
	ret	0
_main	ENDP
_TEXT	ENDS
END

コード:

Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file c1.exe

File Type: EXECUTABLE IMAGE

  00401000: 55                 push        ebp
  00401001: 8B EC              mov         ebp,esp
  00401003: 83 EC 0C           sub         esp,0Ch
  00401006: C7 45 F4 78 56 34  mov         dword ptr [ebp-0Ch],12345678h
            12
  0040100D: B8 56 34 00 00     mov         eax,3456h
  00401012: 66 89 45 FC        mov         word ptr [ebp-4],ax
  00401016: B9 5B 4A 00 00     mov         ecx,4A5Bh
  0040101B: 66 89 4D F8        mov         word ptr [ebp-8],cx
  0040101F: 33 C0              xor         eax,eax
  00401021: 8B E5              mov         esp,ebp
  00401023: 5D                 pop         ebp
  00401024: C3                 ret

Egg

Re: 16進数の下位2桁を取り出す処理について

#30

投稿記事 by Egg » 2年前

>みけCATさん
確かにそのとおりですね。

>sadora3さん
みけCATさんが記述されているのが、『エンディアンが二つ存在する原因』で
私の記述は『二つ存在する結果として、上記のような処理を入れる必要がある例』です。

閉鎖

“C言語何でも質問掲示板” へ戻る