2024年03月17日
3月12日 C言語
Q:C言語ではエスケープ文字としてバックスラッシュを使いますが、
日本語では円記号を使うのは何故ですか。
A:これは、バックスラッシュのASCIIコードが 16進数の5C(0x5c、5Ch)
であることと、日本語のJISコードにおいては円記号がASCIIコードの 5Ch
に割り当てられていることによります。
つまり、コードが一致しているからです。
Q:C言語では文字も数字も一緒くたに扱われているように思えます。
初心者には紛らわしいのですが。
A:
私たちが画面で見る文字も数字も、最小情報量であるビット(0 か 1)
の組み合わせで表現されます。
例えば、ASCIIコードでは、英字の A は41h(0x41)という値で表現されます。
最も機械に近いレベルで表現すると 01000001 です。
つまり、'A'も0x41も機械内部では 01000001 であり、画面上では 英字のAです。
したがって、C言語では、変数をXとして、X='A'とするのと、X=0x41
とするのは同じことになります。
紛らわしく感じるのは、画面上の数字や文字をそのまま受け取るからです。
次のように考えると良いでしょう。私たちは文字「A」を文字そのものとして
理解していますが、コンピュータが扱う最小情報量はビットで 0 か 1ですから、
「A」そのものは認識できません。そこで、「A」を 01000001 に当てはめ、機
械で「A」という文字を処理できるようにしているのです。ただし、0 と1の並
びは人間にわかりにくいので、通常は16進数で表現されます。Aは 01000001
ですから、16進数で41となります。
コンピュータでの表現 画面での英数字
------------------------------------------------------------
16進数の 48 65 6C 6C 6F 英字で H e l l o
16進数の 31 32 33 34 35 数字の 1 2 3 4 5
とにかく、私たちが使う文字や数字が機械の内部ではどのように扱われているか、
ということに興味を持つことです。
Q:よく「文字化けする」ということを耳にしますが、文字化けする原理を教えて下さい。
A:まず、C言語との関連で説明しましょう。例えば、エスケープ文字は円記号(¥)で、
そのASCIIコードは 5Chです。ここで、漢字の「表」を例に取ると、シフトJISでは16進数
の 955C です。もうお分かりですね。もしコンパイラなどが 955C を漢字と認識せず、95と5C
と別々に読むと、5Ch は '\' ですから文字として認識されません。したがって、955Ch は 95
だけとして読まれてしまい、「表」はどこかへ消えます。古いコンパイラ(Lattice C ver.2
など)ではよくあった話です。現在のCコンパイラは漢字を扱えますが、注意はしておく必要
があります。
次に、漢字コードの関係で文字化けが生じる原因を説明しましょう。漢字変換には、パソコン
ではJISコードやシフトJISコード、ワークステーションではEUCコードが使われます。
例えば、「大学」を例にとると、
大 学
シフトJISでは、91E5 8A77
JISでは、4267 3358
と16進数の値が異なります。このように、同一漢字でも16進数が異なることから、漢字を認識
する方法も異なるということになります。ですから、異なる機種の間で通信する場合はコード
を自動的に判別するソフトでないと文字化けすることがあります。自分のパソコンがどの漢字
コードを用いているか、通信の相手がどのコードを用いているかを調べておくことも重要です。
試しに、図書館を呼び出して、図書検索をEUCコードと異なるコートでやってみて下さい。
Q:通信ではファイルをバイナリで送ったり(アップロード)、受け取ったり(ダウンロード)
するのが安全といわれますが何故ですか。
A: Internet の Q&A に書いて有ります。そこをご覧下さい。
Q:マスクとはどのようなことですか。
A:例で示しましょう。
X & 0X00FF とは、数字X と 0X00FFとのビットごとのANDをとることです。
0X00FFをビットで示す(上段はビット、下段はビットフィールド)と、
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
----------------------------------------------------------
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
となります。上位8ビットはゼロですから、Xと0X00FFとのANDをとった数も、
上位8ビットはゼロになります。つまり、上位8ビットはフィルターをかけ
て隠すことを意味しますので、マスクするというわけです。
その他の例
X & 0x7fff 15ビット目をマスク
X & 0x003f 6ビット目以上をマスク
X & 0x000f 4ビット目以上をマスク
(補足)
インターネットでは「サブネットマスク」が使われます。
サブネットマスクは十進数のオクテットで、
255.255.0.0
255.255.255.0
などと表されます。
10進数の255は、16進数の FF、2進数の 11111111 です。
10進数の 0 は、16進数の 00、2進数の 00000000 です。
これから、なぜ「マスク」というのか予想できますね。
「サブネットマスク」については、Internetのページに説明があ
ります。
Q:ビットシフトとはどのようなことですか。
A:例で示しましょう。
0XFF00 >> 8 の場合、右へ8ビットシフトすると、右側の8ビットは除か
れ、代わりに左側に8ビット分 0が詰められます。
図示しますと、
1111 1111 0000 0000 ・・・・ 0XFF00
-----------------------------
1111 1111 ・・・・ 右の8ビット( 0000 0000)が除かれる
0000 0000 ・・・・ 左に8ビット0が詰められる
-----------------------------
0000 0000 1111 1111 ・・・・ 結局、0X00FF となる。
Q:マスクやビットシフトはどのようなことに応用されますか。
A:例で示しましょう。
例題:構造体の中に「年月日」を示す16ビットのビットフィールド
が入っているものとします。
Borland C++ Ver3.0 の ffblk.ff_fdate を例にとっています。
ffblk.ff_fdate のビットフィールド・・・ 日:ビット 0-4
月:ビット 5-8
年:ビット 9-15
図示すると、
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|------- 年 --------------|---- 月 -----|---- 日 -------|
となります。
ここで、年(1980年以降の年数)をy、月(1-12)をm、日(1-31)をd、としましょう。
年を得るのは簡単ですね。ffblk.ff_fdate を9ビット右へシフトすればよい。
(「月日」の桁を削除し、残った「年」を右へ桁下げすると考えると分かり易い)
結局
y = ffblk.ff_fdate >> 9;
日を得るのはどうですか。ffblk.ff_fdate の 左側11ビットをマスクすればよい。
0000 0000 0001 1111 は 0X001F です。
結局、
d = ffblk.ff_fdate & 0X001F;
月を得るのは少々手強いです。月を得るのは、マスクとビットシフトの応用問題です。
まず、5ビット右へシフトさせて、日の5ビットを捨てましょう。
すると 00000年月 が イメージとしてのビットフィールドになります。
次に、月(下位4ビット)だけ残すために、上位12ビットをマスクして隠します。
マスクするのは上位12ビットですから、
0000 0000 0000 1111 との AND をとればよい。( 0X000F との AND をとる。)
結局、
m = ( ffblk.ff_fdate >> 5 ) & 0X000F
分かりましたか。初級者には、ビット操作は無理かもしれません。しかし、C言語の魅
力の一つはこういったビット単位の操作が可能なことにあるのです。
Q:最も簡単なプログラムを一つあげてCのプログラムを説明して下さい。
A:どの入門書でも、"Hello World" と表示するだけのものから始まります。
①最も簡単なプログラムは、以下のような5行のプログラムです。
=====================================================
#include "stdio.h"
void main(void)
{
printf("\n Hello World \n");
}
=====================================================
・Cプログラムは関数の集まりのような形をしています。
・ #include は、プリプロセッサにヘッダファイル(stdio.h)を取り込ませる命令です。
stdio.h は基本的な inputとoutputのきまりを指定したファイルです。
stdio.hの両端を右のようにくくります。<stdio.h> もしくは "stdio.h"
--------------------------------------------------------------------
注意: "<" と ">" は半角です!! ここでは全角になっていますが、こ
うしないと netscape navigator はタグ(tag)と受け取り、画面に表
示されなくなるからです。
--------------------------------------------------------------------
どのプログラムにもstdio.hは必要です。
プリプロセッサ制御命令にはセミコロン(;)はつけません。
main()が必要です。中身は{ と }の間に書きます。
printf()は画面に表示する命令です。表示の中身は "Hello World" です。
"\n" は改行を意味します。
ですから、"\n Hello World \n" は、まず改行し、"Hello World" を表示し、改行する
ことを意味します。文の最後にセミコロン(;)をつけます。
②次は、画面消去後 "Hello World" と表示させます。
=====================================================
#include "stdio.h"
void main(void)
{
printf("\x01b[2J");
printf("Hello World \n");
}
=====================================================
printf("\x01b[2J") は画面消去命令です。\x01b はエスケープコードです。
また、例えば画面を青くしたければ、
printf("\x01b[34m");
とします。これはDOSに対する命令の仕組みですので、こういう場合はこう命令
するのだと覚えるのがC言語上達の近道です。
"Hello World \n" と、"Hello World" の前に \n がないのは、画面を消去したの
で、改行する必要がないからです。
Q:ポインタが分からなくてC言語から落ちこぼれる人が多いと聞きますが。
A:そうらしいですね。
ポインタは変数や関数のアドレスをもつ変数です。
ポインタと配列との関係
pがポインタ変数で、その内容が "HELLO"であるとすると、以下のようになります。
p == "HELLO"
*p == p[0] == "H"
*(p+1) == p[1] == "E"
*(p+2) == p[2] == "L"
*(p+3) == p[3] == "L"
*(p+4) == p[4] == "O"
文字列へのポインタの例を示します。
void string(*st)
{
int i = 0;
int count = 0;
char *str;
str = st; /*文字列の代入(アドレスを一致させる)*/
while( str[i] != '\0' ) {
printf( "str[%d] = %c\n", i, str[i] );
/*1文字ずつプリントする*/
i++;
count++;
}
printf( "Number of Characters = %d \n", count );
/*文字数をプリントする*/
}
日本語では円記号を使うのは何故ですか。
A:これは、バックスラッシュのASCIIコードが 16進数の5C(0x5c、5Ch)
であることと、日本語のJISコードにおいては円記号がASCIIコードの 5Ch
に割り当てられていることによります。
つまり、コードが一致しているからです。
Q:C言語では文字も数字も一緒くたに扱われているように思えます。
初心者には紛らわしいのですが。
A:
私たちが画面で見る文字も数字も、最小情報量であるビット(0 か 1)
の組み合わせで表現されます。
例えば、ASCIIコードでは、英字の A は41h(0x41)という値で表現されます。
最も機械に近いレベルで表現すると 01000001 です。
つまり、'A'も0x41も機械内部では 01000001 であり、画面上では 英字のAです。
したがって、C言語では、変数をXとして、X='A'とするのと、X=0x41
とするのは同じことになります。
紛らわしく感じるのは、画面上の数字や文字をそのまま受け取るからです。
次のように考えると良いでしょう。私たちは文字「A」を文字そのものとして
理解していますが、コンピュータが扱う最小情報量はビットで 0 か 1ですから、
「A」そのものは認識できません。そこで、「A」を 01000001 に当てはめ、機
械で「A」という文字を処理できるようにしているのです。ただし、0 と1の並
びは人間にわかりにくいので、通常は16進数で表現されます。Aは 01000001
ですから、16進数で41となります。
コンピュータでの表現 画面での英数字
------------------------------------------------------------
16進数の 48 65 6C 6C 6F 英字で H e l l o
16進数の 31 32 33 34 35 数字の 1 2 3 4 5
とにかく、私たちが使う文字や数字が機械の内部ではどのように扱われているか、
ということに興味を持つことです。
Q:よく「文字化けする」ということを耳にしますが、文字化けする原理を教えて下さい。
A:まず、C言語との関連で説明しましょう。例えば、エスケープ文字は円記号(¥)で、
そのASCIIコードは 5Chです。ここで、漢字の「表」を例に取ると、シフトJISでは16進数
の 955C です。もうお分かりですね。もしコンパイラなどが 955C を漢字と認識せず、95と5C
と別々に読むと、5Ch は '\' ですから文字として認識されません。したがって、955Ch は 95
だけとして読まれてしまい、「表」はどこかへ消えます。古いコンパイラ(Lattice C ver.2
など)ではよくあった話です。現在のCコンパイラは漢字を扱えますが、注意はしておく必要
があります。
次に、漢字コードの関係で文字化けが生じる原因を説明しましょう。漢字変換には、パソコン
ではJISコードやシフトJISコード、ワークステーションではEUCコードが使われます。
例えば、「大学」を例にとると、
大 学
シフトJISでは、91E5 8A77
JISでは、4267 3358
と16進数の値が異なります。このように、同一漢字でも16進数が異なることから、漢字を認識
する方法も異なるということになります。ですから、異なる機種の間で通信する場合はコード
を自動的に判別するソフトでないと文字化けすることがあります。自分のパソコンがどの漢字
コードを用いているか、通信の相手がどのコードを用いているかを調べておくことも重要です。
試しに、図書館を呼び出して、図書検索をEUCコードと異なるコートでやってみて下さい。
Q:通信ではファイルをバイナリで送ったり(アップロード)、受け取ったり(ダウンロード)
するのが安全といわれますが何故ですか。
A: Internet の Q&A に書いて有ります。そこをご覧下さい。
Q:マスクとはどのようなことですか。
A:例で示しましょう。
X & 0X00FF とは、数字X と 0X00FFとのビットごとのANDをとることです。
0X00FFをビットで示す(上段はビット、下段はビットフィールド)と、
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
----------------------------------------------------------
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
となります。上位8ビットはゼロですから、Xと0X00FFとのANDをとった数も、
上位8ビットはゼロになります。つまり、上位8ビットはフィルターをかけ
て隠すことを意味しますので、マスクするというわけです。
その他の例
X & 0x7fff 15ビット目をマスク
X & 0x003f 6ビット目以上をマスク
X & 0x000f 4ビット目以上をマスク
(補足)
インターネットでは「サブネットマスク」が使われます。
サブネットマスクは十進数のオクテットで、
255.255.0.0
255.255.255.0
などと表されます。
10進数の255は、16進数の FF、2進数の 11111111 です。
10進数の 0 は、16進数の 00、2進数の 00000000 です。
これから、なぜ「マスク」というのか予想できますね。
「サブネットマスク」については、Internetのページに説明があ
ります。
Q:ビットシフトとはどのようなことですか。
A:例で示しましょう。
0XFF00 >> 8 の場合、右へ8ビットシフトすると、右側の8ビットは除か
れ、代わりに左側に8ビット分 0が詰められます。
図示しますと、
1111 1111 0000 0000 ・・・・ 0XFF00
-----------------------------
1111 1111 ・・・・ 右の8ビット( 0000 0000)が除かれる
0000 0000 ・・・・ 左に8ビット0が詰められる
-----------------------------
0000 0000 1111 1111 ・・・・ 結局、0X00FF となる。
Q:マスクやビットシフトはどのようなことに応用されますか。
A:例で示しましょう。
例題:構造体の中に「年月日」を示す16ビットのビットフィールド
が入っているものとします。
Borland C++ Ver3.0 の ffblk.ff_fdate を例にとっています。
ffblk.ff_fdate のビットフィールド・・・ 日:ビット 0-4
月:ビット 5-8
年:ビット 9-15
図示すると、
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|------- 年 --------------|---- 月 -----|---- 日 -------|
となります。
ここで、年(1980年以降の年数)をy、月(1-12)をm、日(1-31)をd、としましょう。
年を得るのは簡単ですね。ffblk.ff_fdate を9ビット右へシフトすればよい。
(「月日」の桁を削除し、残った「年」を右へ桁下げすると考えると分かり易い)
結局
y = ffblk.ff_fdate >> 9;
日を得るのはどうですか。ffblk.ff_fdate の 左側11ビットをマスクすればよい。
0000 0000 0001 1111 は 0X001F です。
結局、
d = ffblk.ff_fdate & 0X001F;
月を得るのは少々手強いです。月を得るのは、マスクとビットシフトの応用問題です。
まず、5ビット右へシフトさせて、日の5ビットを捨てましょう。
すると 00000年月 が イメージとしてのビットフィールドになります。
次に、月(下位4ビット)だけ残すために、上位12ビットをマスクして隠します。
マスクするのは上位12ビットですから、
0000 0000 0000 1111 との AND をとればよい。( 0X000F との AND をとる。)
結局、
m = ( ffblk.ff_fdate >> 5 ) & 0X000F
分かりましたか。初級者には、ビット操作は無理かもしれません。しかし、C言語の魅
力の一つはこういったビット単位の操作が可能なことにあるのです。
Q:最も簡単なプログラムを一つあげてCのプログラムを説明して下さい。
A:どの入門書でも、"Hello World" と表示するだけのものから始まります。
①最も簡単なプログラムは、以下のような5行のプログラムです。
=====================================================
#include "stdio.h"
void main(void)
{
printf("\n Hello World \n");
}
=====================================================
・Cプログラムは関数の集まりのような形をしています。
・ #include は、プリプロセッサにヘッダファイル(stdio.h)を取り込ませる命令です。
stdio.h は基本的な inputとoutputのきまりを指定したファイルです。
stdio.hの両端を右のようにくくります。<stdio.h> もしくは "stdio.h"
--------------------------------------------------------------------
注意: "<" と ">" は半角です!! ここでは全角になっていますが、こ
うしないと netscape navigator はタグ(tag)と受け取り、画面に表
示されなくなるからです。
--------------------------------------------------------------------
どのプログラムにもstdio.hは必要です。
プリプロセッサ制御命令にはセミコロン(;)はつけません。
main()が必要です。中身は{ と }の間に書きます。
printf()は画面に表示する命令です。表示の中身は "Hello World" です。
"\n" は改行を意味します。
ですから、"\n Hello World \n" は、まず改行し、"Hello World" を表示し、改行する
ことを意味します。文の最後にセミコロン(;)をつけます。
②次は、画面消去後 "Hello World" と表示させます。
=====================================================
#include "stdio.h"
void main(void)
{
printf("\x01b[2J");
printf("Hello World \n");
}
=====================================================
printf("\x01b[2J") は画面消去命令です。\x01b はエスケープコードです。
また、例えば画面を青くしたければ、
printf("\x01b[34m");
とします。これはDOSに対する命令の仕組みですので、こういう場合はこう命令
するのだと覚えるのがC言語上達の近道です。
"Hello World \n" と、"Hello World" の前に \n がないのは、画面を消去したの
で、改行する必要がないからです。
Q:ポインタが分からなくてC言語から落ちこぼれる人が多いと聞きますが。
A:そうらしいですね。
ポインタは変数や関数のアドレスをもつ変数です。
ポインタと配列との関係
pがポインタ変数で、その内容が "HELLO"であるとすると、以下のようになります。
p == "HELLO"
*p == p[0] == "H"
*(p+1) == p[1] == "E"
*(p+2) == p[2] == "L"
*(p+3) == p[3] == "L"
*(p+4) == p[4] == "O"
文字列へのポインタの例を示します。
void string(*st)
{
int i = 0;
int count = 0;
char *str;
str = st; /*文字列の代入(アドレスを一致させる)*/
while( str[i] != '\0' ) {
printf( "str[%d] = %c\n", i, str[i] );
/*1文字ずつプリントする*/
i++;
count++;
}
printf( "Number of Characters = %d \n", count );
/*文字数をプリントする*/
}