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

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

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

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

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

Linux

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

Q&A

解決済

3回答

7835閲覧

htonl / ntohl を使ってホストバイトオーダーを求める

self-taught-cs

総合スコア11

C

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

ソケット

TCP/IPにおいて、IPアドレスとサブアドレスであるポート番号を組み合わせたネットワークアドレスのことを呼びます。また、ソフトウェアアプリケーションにおいて、TCP/IP通信を行う為の仮想的なインターフェースという意味もある。

Linux

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

0グッド

2クリップ

投稿2021/01/21 03:08

編集2021/01/21 05:01

前提・実現したいこと

Ubuntu20.10(Dockerコンテナ)上でホストバイトオーダーを求めようと考えました。一般的にはシェルで簡単に求められると思うのですが、今回はhtonl / ntohl を使って判別するケースを考えたいです。また、ホストはx86_64であるためリトルエンディアンであると前提します。

該当のソースコード

c

1// main.c 2#include <stdio.h> 3#include <netinet/in.h> 4 5 6int main() { 7 unsigned int x = 0xAABBCCDD; 8 // htonlはネットワークバイトオーダー(ビッグエンディアン)に 9 // ntohlはネットワークバイトオーダーの値をホストのエンディアンに変換する 10 // すなわち htonl(x) == ntohl(x) であればホストバイトオーダーはビッグエンディアンである 11 printf("ホストバイトオーダーは"); 12 if (htonl(x) == ntohl(x)) { 13 printf("big endian\n"); 14 } else { 15 printf("little endian\n"); 16 } 17}
実行結果
root@a6bb7bfe96dd:~# gcc main.c root@a6bb7bfe96dd:~# ./a.out ホストバイトオーダーはbig endian

上記プログラムからはビッグエンディアンであると言われます。

何が問題なのか?

同ホスト上でodコマンドを使ってエンディアンを求めると、リトルエンディアンであることが確認されます。私が該当のソースコードに書いたコードはホストがビッグエンディアンという結果を出していることが今回の問題です。

root@a6bb7bfe96dd:~# echo -n "1234ABCD" | od -t x 0000000 34333231 44434241 0000010

同様に下記のエンディアンを求めるプログラムからもリトルエンディアンであると言われます。

c

1 2#include <stdio.h> 3 4int main() { 5 int x = 1; // 0x00000001 6 printf("ホストバイトオーダーは"); 7 if (*(char *)&x ){ 8 printf("little endian\n"); 9 } else { 10 printf("big endian\n"); 11 } 12}

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

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

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

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

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

guest

回答3

0

そもそも、x86(やx64)はリトルエンディアンです


ネットワークオーダはビッグエンディアンです。

ホストがビッグエンディアンの場合
htonl  なにもしない
ntohl なにもしない
なので、htonl(x) ==ntohl(x) は成立する

ホストがリトルエンディアンの場合
htonl リトル>ビッグの変換
ntohl ビッグ>リトルの変換、がかかる
はやいはなし、どっちの関数もエンディアン変換のために同じ操作をしてしまっている、
ため、htonl(x) ==ntohl(x) は成立してしまいますね

投稿2021/01/21 04:01

編集2021/01/21 04:42
y_waiwai

総合スコア87774

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

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

self-taught-cs

2021/01/21 04:32 編集

はい、それは存じ上げております。私の質問が言葉足らずで申し訳ないのですが今回着目したいのは「エンディアンがどちらか」ではなく「"該当のソースコード"は何が良くないのか、或いは私の仮説はどう違うのか」が知りたいのです。
y_waiwai

2021/01/21 04:32

他の回答で、あなたのコードの間違ったところは指摘されとります。 あとなにを聞きたいんでしょうか。
y_waiwai

2021/01/21 04:43

追記しました
guest

0

if (htonl(x) == ntohl(x)) {

if (htonl(x) == x) {

ではないですか?

投稿2021/01/21 03:23

68user

総合スコア2005

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

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

68user

2021/01/21 03:37

hton と ntoh は、どんな環境でも片方がスワップする・もう片方は何もしない という関係性であるわけで、それぞれに同じ引数を与えても結果が一致することはいのでは、 と考えました。
self-taught-cs

2021/01/21 04:38 編集

ビッグエンディアンのホストである場合、 hton / ntoh はともに何もしないと思っているのですが間違っていますか?(恐らく間違っているとは思うのですが...
hidezzz

2021/01/21 05:24 編集

ソースコードに以下の行を入れて表示された値を確認してみてください。 printf( "x=%x htonl(x)=%x ntohl(x)=%x\n", x, htonl(x), ntohl(x) ); リトルエンディアンでもビッグエンディアンでも、htonl(x) == ntohl(x)になります。 x=aabbccdd htonl(x)=ddccbbaa ntohl(x)=ddccbbaa
68user

2021/01/21 05:33

ああすみません間違えておりました。適当な回答して失礼しました。 補足は大間違いでしたが、コードの指摘はたまたまあっておりました、という状況ですね。 誤:if (htonl(x) == ntohl(x)) { 正:if (htonl(x) == x) { 正:if (ntohl(x) == x) {
guest

0

ベストアンサー

2バイト以上のデータがメモリ上に展開されているときの並びと、それが8*Nビットの変数として扱うときの並びの意識に混乱があると推察しての回答です。本筋の回答は既にいただいていますので、補足程度になりますが。

まず、質問者さんのコードをもとに必要な情報を追加出力するようにしたものです。

C

1// main.c 2#include <stdio.h> 3#include <netinet/in.h> 4 5int main() { 6 unsigned int x = 0xAABBCCDD; 7 8 // htonsはネットワークバイトオーダー(ビッグエンディアン)に 9 // ntohsはネットワークバイトオーダーの値をホストのエンディアンに変換する 10 // すなわち htons(x) == ntohs(x) であればホストバイトオーダーはビッグエンディアンである 11 printf("ホストバイトオーダーは"); 12 if (htonl(x) == ntohl(x)) { 13 printf("big endian\n"); 14 } else { 15 printf("little endian\n"); 16 } 17 18 const unsigned char *pc = (const unsigned char *)&x; 19 for (int i = 0; i < sizeof(x); i++) { 20 printf("%02X ", *pc++); 21 } 22 printf("\n"); 23 unsigned int v1 = htonl(x); 24 unsigned int v2 = ntohl(x); 25 printf("sizeof(unsigned int)=%ld, htonl=0x%08X, ntohl=0x%08X\n", 26 sizeof(unsigned int), v1, v2); 27}

これを実行すると以下のようになります。(「ホストバイトオーダーはbig endian」と誤判断する問題はそのままです)

terminal

1$ gcc -Wall t1.c -o t1 2 3$ ./t1 4ホストバイトオーダーはbig endian 5DD CC BB AA 6sizeof(unsigned int)=4, htonl=0xDDCCBBAA, ntohl=0xDDCCBBAA 7 8$

unsigned int x = 0xAABBCCDDの値は、リトルエンディアンのホスト環境ではメモリ上にunsigned char[] a1 = {0xDD, 0xCC, 0xBB, 0xAA}のように収まっています。htonlではホストのバイトオーダーをネットワークバイトオーダーに直すわけですから、32ビットの変数で見るとき、言い換えると32ビットレジスタにロードされたときの値として0xDDCCBBAAになります。これは、リトルエンディアンのホストでメモリ上に収めるとひっくり返ってunsigned char a2[] = {0xAA, 0xBB, 0xCC, 0xDD}になります。

対して、リトルエンディアンのホスト環境では0xAABBCCDDの32ビット値をntohlを通すと0xDDCCBBAAになります。これは、0xDDCCBBAAはビッグエンディアンのホスト環境ではメモリ上に収められた値として、見た目のイメージそのまま、unsigned char a3[] = {0xDD, 0xCC, 0xBB, 0xAA}になるからです。

ネットワークバイトオーダーの必要性としては、ネットワーク上に流れてくる構造体データを直列のデータストリームとみて、ホストのメモリ上に収め、それぞれのメンバーのバイト長を考慮し、参照する際の必要に応じて変換させると考えると良いです。

投稿2021/01/21 05:45

dodox86

総合スコア9183

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

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

dodox86

2021/01/21 09:52

リトルエンディアンのホスト環境において: 1. htonlの関数の立場からすると、入力の0xAABBCCDDをネットワークバイトオーダーにするために出力が0xDDCCBBAAになる。 2. ntohlの関数の立場からすると、入力の0xAABBCCDDをネットワークバイトオーダーとして扱うから、出力が0xDDCCBBAAになる。 。。。とのようなまとめで、htonlとntohlの動きが理解できるでしょうか。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問