int型や long型,float型などの複数バイトのデータをメモリに格納する場合,上位バイトから格納するか (Big endian),下位バイトから格納するか (Little endian)は,プロセッサによって異なる.このようなバイト並びを仮定したプログラムは移植性がない.
次のプログラムはエンディアンをチェックするプログラムです.
このプログラムを実行するプロセッサが Big endian
か Little endian
かを表示します.
#include <stdio.h> int main(void) { unsigned int i; int ival; long long lval; char *cp; int endian; union { long l; char c[sizeof (long)]; } u; ival = 0x01020304; lval = 0x0102030405060708LL; cp = (char *)&ival; printf("int val = %08x\n", ival); for (i = 0; i < sizeof(int); i++) { printf("%02x ", cp[i]); } puts(""); cp = (char *)&lval; printf("long long val = %08llx\n", lval); for (i = 0; i < sizeof(long long); i++) { printf("%02x ", cp[i]); } puts("\n"); /* Are we little or big endian? */ u.l = 1; endian = (u.c[sizeof (long) - 1] == 1); printf("This computer is "); if (endian == 0) { printf("'Little endian'"); } else { printf("'Big endian'"); } printf(" machine.\n"); return 0; }
ネットワークを使った数値の通信においては,バイトオーダ(上位バイトから送信/受信するか下位バイトから送信/受信するか)が送受信双方で異なっていると正しく通信できないため,あらかじめバイトオーダを決めておく必要がある(ネットワークバイトオーダという). TCP/IP プロトコルでは,ネットワークバイトオーダは MSB から先に送る Big endian となっている.したがって,Little endian のマシンで通信する場合,変換が必要になる.
ネットワークバイトオーダの変換関数として htons() と htonl() が,その逆は ntohs() と ntohl() が用意されている.htons は host to network short の略というような命名規則である.
次のプログラムは,変換関数によるバイトオーダ変換の確認プログラムです.
#include <stdio.h> #include <arpa/inet.h> int main(void) { unsigned int val = 0xAABBCCDD; unsigned char *pc = (unsigned char *)&val; printf("%X : %X : %X : %X\n" , pc[0] , pc[1] , pc[2] , pc[3]); val = htonl(val); printf("%X : %X : %X : %X\n" , pc[0] , pc[1] , pc[2] , pc[3]); val = ntohl(val); printf("%X : %X : %X : %X\n" , pc[0] , pc[1] , pc[2] , pc[3]); return 0; }
これを実行すると次のような結果が得られます(リトルエンディアン環境).
DD : CC : BB : AA AA : BB : CC : DD DD : CC : BB : AA
Cプログラムは一般的には以下のように5つの領域から構成されている.
・TEXTセグメント | : | コード領域 |
・DATAセグメント | : | 初期化データ領域 |
・BSSセグメント | : | 非初期化データ領域 |
・ヒープ領域 | : | 動的確保領域 |
・スタック領域 | : | スタック領域 |
各セグメントの並びはアドレスの低番地から上記の順になっている.但し,処理系によってはスタック領域とヒープ領域が逆になる場合がある.
次のプログラムは上記のアドレスを確認するプログラムです.
#include <stdio.h> #include <stdlib.h> int ex1; int exi = 1; int ex2; static int es; static int esi = 1; int main(void) { int a; static int s; char *p, *q; a = s = ex1 = ex2 = 0; p = "test data"; q = (char *)malloc(100); printf("p: char const = %p\n", p); printf("exi: extern(init) = %p\n", &exi); printf("esi: static(init) = %p\n", &esi); printf("s: static = %p\n", &s); printf("es: static = %p\n", &es); printf("ex2: extern = %p\n", &ex2); printf("ex1: extern = %p\n", &ex1); printf("q: malloc = %px\n", q); printf("a: auto = %px\n", &a); free(q); return 0; }
これを実行すると次のような結果が得られます.
p: char const = 0x400824 exi: extern(init) = 0x601054 esi: static(init) = 0x601050 s: static = 0x601070 es: static = 0x601074 ex2: extern = 0x60107c ex1: extern = 0x601078 q: malloc = 0x6f0010x a: auto = 0x7fffd13df6e0x
C言語では,変数を使用するのに変数の型を指定するが,ANSI C 標準では正確なサイズは定義されていない.すなわち,実行する環境により,変数が実際に使用する領域サイズは異なる.
プログラムの移植性を高めるためには,変数のサイズを仮定したコードを書かないようにする必要がある.C言語には,変数や型のサイズを調べられる sizeof 演算子が用意されている. 指定された型や変数のデータサイズがバイト単位で返される.
次のプログラムは,sizeof 演算子を使って変数型やポインタのサイズを確認するプログラムです.
#include <stdio.h> int main(void) { printf("char = %ld\n", sizeof(char)); printf("short = %ld\n", sizeof(short)); printf("int = %ld\n", sizeof(int)); printf("long = %ld\n", sizeof(long)); printf("long long = %ld\n", sizeof(long long)); printf("float = %ld\n", sizeof(float)); printf("double = %ld\n", sizeof(double)); printf("char* = %ld\n", sizeof(char *)); return 0; }
これを実行すると次のような結果が得られます(64ビット環境).
char = 1 short = 2 int = 4 long = 8 long long = 8 float = 4 double = 8 char* = 8