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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

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

バイナリ

バイナリは、「0」と「1」だけで表現されている2進数のデータ形式。または、テキスト以外の情報でデータが記述されているファイルを指します。コンピューター内の処理は全て2進数で表記されています。

CPU

CPUは、コンピュータの中心となる処理装置(プロセッサ)で中央処理装置とも呼ばれています。プログラム演算や数値計算、その他の演算ユニットをコントロール。スマホやPCによって内蔵されているCPUは異なりますが、処理性能が早いほど良いとされています。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

5回答

3952閲覧

リトルエンディアンでのデータ配置とメモリアクセス時の処理について

flyingPengin

総合スコア13

C

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

バイナリ

バイナリは、「0」と「1」だけで表現されている2進数のデータ形式。または、テキスト以外の情報でデータが記述されているファイルを指します。コンピューター内の処理は全て2進数で表記されています。

CPU

CPUは、コンピュータの中心となる処理装置(プロセッサ)で中央処理装置とも呼ばれています。プログラム演算や数値計算、その他の演算ユニットをコントロール。スマホやPCによって内蔵されているCPUは異なりますが、処理性能が早いほど良いとされています。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

0クリップ

投稿2019/04/13 04:01

前提

使用環境:仮想環境(VirtualBox)上の64bit KaliLinux(ホストOSは64bitのWindows10)

質問内容

リトルエンディアンでのメモリ上のデータ配置とメモリアクセス時の処理について質問がございます。

下記のプログラムを実行すると

c

1#include <stdio.h> 2 3int main(){ 4 char *st = "\x41\x42\x43\x44\x45\x46\x47\x48"; 5 6 for(int i=0;i<8;i++){ 7 printf("%x ",st[i]); 8 } 9 printf("\n"); 10}
41 42 43 44 45 46 47 48

がコマンドライン上に出力されます。
この時、メモリ上において文字列stの先頭番地を0x100とします。

次にGDBでこのプログラムをデバックした時に、xコマンドを使って0x100番地以降のメモリ内容を2通り調べました。
1つはコマンド:x/xg 0x100 を入力(つまり0x100番地を先頭に8バイト単位でメモリ内容を表示する)して調べるやり方。
この場合は以下の結果が出力されました。

0x100 : 0x4847464544434241

これについてはリトルエンディアンでの配置通りなので疑問はないです。

しかし、2つ目の調べ方 コマンド:x/8xb 0x100を入力する(つまり0x100番地を先頭に1バイト単位でメモリ内容を表示する)して調べるやり方で疑問が出ました。
このやり方では以下の結果が出力されました。冒頭プログラムの結果と同様です。

0x100 : 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48

この結果に対して疑問があります。
64bit環境では8バイト単位でリトルエンディアンでメモリにデータが配置されるので、
文字列stのデータがメモリに格納される時、実際のメモリ上のデータ配置は1つめの調べ方の結果のはず。
なので、0x100番地には0x48が配置されている(つまりst[0]=0x48になる)はずなのに、
冒頭のプログラムの結果や2つめの調べ方の結果では0x100番地には0x41が配置されている(つまりst[0]=0x41になる)ことになってしまっています。

以上を踏まえて質問としては
冒頭のプログラムの結果は、本来はリトルエンディアンによる実際のデータ配置を踏まえて

48 47 46 45 44 43 42 41

になるはずなのに、なぜそうならないのか? です。

まとまりがなくわかりにくい文章で申し訳ございませんが、ご回答いただけると幸いです。

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ベストアンサー

リトル/ビッグの影響は、複数バイトでのアクセスの場合のみです。
char(1byte)の配列では、その順番に格納されます。

char で、0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48 とすると、
この順番にメモリ上に格納されます。(上記の例では、0x100 に 0x41)
これを 8byteのデータ(8byte長のレジスタとして)読みだすと、逆の 0x48474645_44434241 としてレジスタに格納されます。

質問文の例だと、0x100から、8byteを読みだした、という事なので、レジスタには、逆順に格納され、メモリ上とは逆に表示されます。

ただし、今回の例では、0x100 という 8の倍数のアドレスですが、それ以外の場合は要注意です。
また、Big/Little もCPU依存性があり、2byte単位で、交換なんてのもあるので、こちらも要注意。 (Intelは、バイト単位で、入換え)

投稿2019/04/13 04:25

pepperleaf

総合スコア6383

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flyingPengin

2019/04/13 06:35

詳細にご回答していただきありがとうございます。 回答に対して自分なりにまとめました。 ・冒頭プログラムに関して、まず前提としてプログラム実行開始時に右辺の値の「"\x41\x42\x43\x44\x45\46\47\48"」はメモリ上のテキスト領域にこの順番で配置されている。 また0x41の値が格納されている番地を0x10番地とする。 ・冒頭のプログラムの char *st = "\x41\x42\x43\x44\x45\46\47\48"の行において、この行の実行時、st[0]に右辺の値を格納する際に、st[0]のデータサイズはchar型の1Byteなので格納順序はまったく関係なくテキスト領域の0x10番地の値(0x41)をst[0]に格納する。次にst[1]に0x11番地の値(0x42)を格納する。以降の値も同様。 ・なので実際のメモリ上のデータ配置は以下の通り(stの先頭番地を0x100番地とする) 0x100番地:0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 ・GDBのx/xgコマンド(8バイト単位でメモリ内容を表示)実行時に コンソール上に「0x100番地:0x484746.....41」が表示されるのは、コマンドを実行してメモリの0x100番地以降の内容を8バイト単位で読み込む際に複数バイトにまたがるメモリアクセスなので、レジスタにはリトルエンディアンの順序に基づいて0x484746.....41が格納される。そして、コンソールにはこのレジスタに格納された値が表示されるから。 ・プログラムがコンソール上にメモリ内の値を出力する際、メモリ上にアクセスして値をそのままコンソールに出力するのではなく、正確にはメモリ上にアクセスしそこから値をレジスタに格納(複数バイトにまたがるメモリ読み込み時はエンディアンの格納順序が関係する)し、レジスタ内の値をコンソール上に出力する。 私の認識の齟齬があればそれに対するコメントや追加コメント等がございましたら、ご回答いただけると幸いです。pepperleafさんのご回答が詳細にわたっていてわかりやすかったのでベストアンサーとさせていただきます。 皆様、ご回答いただきありがとうございました。
jimbe

2019/04/13 07:28

> GDBのx/xgコマンド(8バイト単位でメモリ内容を表示) が8バイトの値(int?long?)として表示するのかというのが(ちょっとした検索では)確認できませんでしたが, おそらくそうではないかと推測します. であれば, ```8バイトをレジスタに転送(この時エンディアンの影響を受ける)→レジスタの8バイトを(上位バイトから)表示``` となっているものと思います. そして, > x/8xb 0x100を入力する(つまり0x100番地を先頭に1バイト単位でメモリ内容を表示する) では, ```1バイトをレジスタ最下位バイトに転送→レジスタの最下位1バイトを表示, を8回繰り返す``` 動作となり, エンディアンに関係無く先頭アドレスの値から表示されるのではないでしょうか.
guest

0

64bit環境では8バイト単位でリトルエンディアンでメモリにデータが配置されるので、

これが、勘違いの大元ですね。CPUやOSのビット数は関係ありません。

投稿2019/04/13 17:41

otn

総合スコア84505

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

これならひっくり返ってるのが確認できるぞ。

C

1#include <stdio.h> 2 3int main(){ 4 unsigned long long data = 0x4142434445464748LL; 5 const unsigned char* st = (unsigned char*)&data; 6 for(int i=0;i<8;i++){ 7 printf("%x ",st[i]); 8 } 9 printf("\n"); 10 return 0; 11}

投稿2019/04/13 07:19

episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

エンディアンがどうであれ、バイトデータの格納順はアドレスの若い方から順番に格納されます
エンディアンが関わってくるのは、nバイト整数のアクセスのときです。
リトルエンディアンでは逆順にアクセスされますね

投稿2019/04/13 04:14

y_waiwai

総合スコア87749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flyingPengin

2019/04/13 06:37

ご回答ありがとうございます。 pepperleafさんと同様の内容のご回答でしたので、pepperleafさんのところにまとめて返答いたしました。
guest

0

エンディアンが関係するのは複数バイト単位のアクセスとなる, 例えば C の int 等で, 1バイト単位に配置する文字(列)には関係無いということではないでしょうか.

投稿2019/04/13 04:05

jimbe

総合スコア12632

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

flyingPengin

2019/04/13 06:37

ご回答ありがとうございます。 pepperleafさんと同様の内容のご回答でしたので、pepperleafさんのところにまとめて返答いたしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問