thisdesuのブログ

C言語はじめました

にほんブログ村 IT技術ブログ C/C++へ

キャスト(型変換)

値を設定する際に=を挟んで、左辺と右辺の型が違う場合にキャストを行います。明示的にキャストをする場合と、暗黙的に行われる2つがあります。
暗黙的に行われる場合とは、型の相違が有った場合にキャスト指定が無い場合には優先順を元に自動で変換されます。
優先: char < int < long < float < double
明示的に変換する場合には、設定元の値(右辺)に対して「(型)」を使って指定すると変換されます。

例題:暗黙変換と明示変換

int main(){
    float a;

    // 暗黙に右辺の(int)型キャストされ設定
    // int型(精度無し)なので小数点以下が落ちている
    a = 3/2;
    printf("%f\n",a);

    // 右辺を明示的に(floag)型にキャストして設定
    a = (float)3/2;
    printf("%f\n",a);
}


■結果

$ gcc main.c
$ ./a.out
1.000000
1.500000

アドレスとポインタ

C言語はメモリ空間を直接感じれる言語です。Javaとかスクリプト系の言語では余り感じられないと思います。あえて隠していますが。
メモリ空間を直接扱うのはとっても早いのですが、ひとつ間違うだけでエラー発生プログラム終了!高確率でフリーズ。下手するとOSクラッシュなんてことも有りますので心してかかりましょう。
アドレスとはメモリ空間の番地でまさしくメモリの住所の事を指します。プログラムがメモリ上で関数も変数も静的な値も全てメモリに書かれていて、書かれている場所には全てアドレスが付いています。アドレスは実行する度に変わりますので固定ではありません。
ポインタとは、名前のとおりアドレスを指す仕組みです。これはアドレスを知る方法とアドレスから値を知る方法があります。

int main(){
    int a= 10;

    printf("値:%d\n",a);
    printf("アドレス:%x\n",&a);
}

■結果

$ gcc main.c
$ ./a.out
値:10
アドレス:0x7fff50aab20c


ポインタあれこれ
int型変数のaと、int型ポインタ変数のbを宣言し、bにaのアドレスを

int main(){
    int a= 10;
    // int型のポインタ変数を宣言
    int *b;

    printf("a値:%d\n",a);
    // 変数のアドレスを見る(&)
    printf("aアドレス:%p\n",&a);

    // ポインタ変数bにaのアドレスを設定し共有にする。
    b = &a;
    // ポインタ変数に値を設定(*)
    *b = 20;
    printf("b値(アドレス):%p\n",b);
    printf("b値からポインタで値を見る:%d\n",*b);
    printf("bアドレス:%p\n",&b);
    printf("a値:%d\n",a);
    printf("aアドレス:%p\n",&a);
}

■結果

$ gcc main.c
$ ./a.out
a値:10
aアドレス:0x7fffdc7d7c9c
b値(アドレス):0x7fffdc7d7c9c
b値からポインタで値を見る:20
bアドレス:0x7fffdc7d7c90
a値:20
aアドレス:0x7fffdc7d7c9c

参考書

C言語を覚える時に、まず参考書を買う人は多いでしょう。最近はインターネットで調べることも多くなりましたが、やっぱり手元で開いて何度も読み返しながら勉強するのは学生の時からの性なのでしょうか。もちろん私もです。
昔から知ってる書籍をあげてみます。参考までに

プログラミング言語C共立出版
ANSI規格準拠
B.W.カーニハン/D.M.リッチー(著)

「CユーザのためのANSI C言語大辞典」パーソナルメディア
マーク・ウィリアムズ社(著,編集)

C言語による最新アルゴリズム事典」技術評論社
奥村晴彦(著)

「C実践プログラミング」O'Reilly
Steve Oualline(著)

「標準Cライブラリ―ANSI/ISO/JIS C規格」<トッパンbr>
P.J.プラウガー(著)


その他
UNIXネットワークプログラミング」ピアソンエデュケーション
「マスタリングTCP/IP 入門編」オーム社

低水準や高水準関数とは

C言語の本や説明の中に、よく低水準関数とか高水準関数とかの言葉が出てきます。これは用意されている関数が、OS(オペレーションシステム)に依存するような関数なのかどうか、明確な境界線はありませんが、OSに近い(OSが違ったら動かないかもしれなさそう)関数を低水準関数といい、OS依存が少ない汎用性が高い関数を高水準関数と言います。低水準は速度が速いとか細かく操作が出来るなど利点が有りますが、同じプログラムがOSが変わると動かなかったり、そもその関数が用意されていない事も有ります。高水準は汎用的な作りにしていて速度が遅かったり細かな所に手が届かなかったりと欠点は有りますが、移植性が高く生産性などメリットは多いです。作り的には低水準関数を汎用的に被せて依存部分は隠蔽したのが高水準関数と言えるでしょう。
比較的よく目にするのはファイル操作関連の関数です。
高水準関数としてファイルバッファとして扱えるf系(fopen,fclose,fread,fwrite)関数があります。データのバッファリングして動作をスムーズ?にさせていて普通に使うには特に気にならないでしょう。たまにバッファリング待ちでデータ取れない場合がありますが、そんな時はflushさせる一手間は必要です。これに対して低水準関数の(open,close,read,write)があります。バッファリングされないので動きはシビヤになりますが高速に扱えます。socketなど扱う場合にはこちらでしょう。他に更に低そうなioctl系などOSが違えばほとんど動かないでしょう

ファイル操作(fopen,fgets,fputs,feof,fclose)

ファイルのやり取りする関数を使ってみましょう。ファイルを扱うには基本的な作法があります。まず最初にファイルを開く(open)しなければなりません。そして開いたファイルに対して読み込み(read)や書き込み(write)が行えます。またファイルを開く際には、読み込みをするのか、書き込みするのかなど指定しておく必要があります。最後にファイルを閉じ(close)ます。これをしないと他でファイルが開けなくてエラーが発生しますので必ず行いましょう。

今回は例として、テキストファイルのコピーする例です。また高水準関数(*1)を使ってお手軽簡易版コピーです。
■流れ
1. コピー元ファイルを読み込み指定で開きます。
2. コピー先ファイルを書き込み指定で開きます。
3. コピー元ファイルから1行読み込み、データがある間は項4,5を繰り返します。
4. コピー元ファイルから1行づつ読み込みます。
5. コピー先ファイルへ1行づつ書き込みます。
6. コピー元ファイルを閉じます。
7. コピー先ファイルを閉じます。

#include <stdio.h>

int main(int argc, char **argv) {
  FILE    *moto;// コピー元用ファイルポインター
  FILE    *saki;// コピー先用ファイルポインター
  char    buf[BUFSIZ];// 読み込み領域

  // 1. コピー元ファイル("moto.txt")を読み込み指定("r")で開きます。
  moto     = fopen("moto.txt","r") ;
  if ( moto == 0x00 ) {
     printf("open error moto\n");
     return 1;
  }

  // 2. コピー先ファイル("saki.txt")を書き込み指定("w")で開きます。
  saki     = fopen("saki.txt","w") ;
  if ( saki == 0x00 ) {
     printf("open error saki\n");
     return 1;
  }

  // 3. コピー元ファイルから1行読み込み、データがある間は項4,5を繰り返します。
  // 4. コピー元ファイルから1行づつ読み込みます。
  while( fgets(buf,sizeof(buf),moto) != 0x00 ) {
    printf("%s",buf);
    // 5. コピー先ファイルへ1行づつ書き込みます。
    fputs(buf,saki);
  }

  // 6. コピー元ファイルを閉じます。
  fclose(moto);

  // 7. コピー先ファイルを閉じます。
  fclose(saki);

  return 0;
}

■結果

$ cat moto.txt
sample1
test_2
line3
$ cat saki.txt
cat: saki.txt: そのようなファイルやディレクトリはありません
$ gcc main.c
$ ./a.out
sample1
test_2
line3
$ cat saki.txt
sample1
test_2
line3
$ diff moto.txt saki.txt
$


(*1):関数にはOSに近いか遠いかで水準分けして呼ばれている関数が有ります。性能によるものでは有りません。

コーヒーブレイク(IOCCC)

プログラミングとスパゲッティは密接で、タコ足のコンセントなんか目じゃないほど、絡まりに絡まったソースプログラムは人の手を介す度に解かれず放置されたのか見て見ぬふりと言うか触らぬ神に祟り無しなのか、、、完全にブラックボックスと化したプログラムに出会ったしまったら、、僕もきっと解読する勇気よりクライアントに”作り直しましょう”と言う理由を模索し始めるでしょう。
ただセキュリティーの風潮なのかリバースエンジニアリング防止なのか、そのまま利用できるスクリプト系のプログラムやHTMLなどにおいては、あえて難読化して動きを見せなくする・盗用させなくする様な事も行われています。特にgoogleなんで検索結果のHTMLを見ても人の目では解読不能にしているし、CSSJavascriptも難読化変換されています。ただし、開発時には返還前の物があって実際に開発で利用する分けでもないので、ちょっと意味が違うかな
IOCC(国際難読化Cコードコンテスト)なんてものが存在します。まさにソースの読み難さを競い合うコンテストです。ハッカーの奇祭らしいがエントリーされたコードを見てみると、確かに"何故動くのか"なんて考えてしまうほどの難読なんですが、さすがハッカーです。結果が違いますクオリティの高さが次元違う。ただ苦しませるスパゲッティとは違い、すげーの連発です。是非ごらんあれ
IOCCC
下は難読ではないけど、知らないと一瞬え?って思うソース

int main(int argc, char **argv) {
    int a;
    a = 011 + 001 ;
    printf("011 + 001 = %d \n", a );
}

■結果

$ gcc main.c
$ ./a.out
011 + 001 = 10

関数って、、

関数とは処理を一まとめにした塊を、いろんな所で使いたい!使いまわししたい時に、「関数」って枠組みで作っておくと、色々なところから使えるようになります。この「関数」を使うには作法というかルールがあり、そのルールの事を関数インタフェース仕様とかリファレンスとか呼ばれます。ルールには大きく2つあります。1つ目は関数にデータを与えるルール。後に出てきますが”引数”と呼ばれる部分です。2つ目は関数から渡されるルール。これも後に出てきますが”戻り値”と呼ばれる部分です。このように関数に”何”を渡して、”何”が返ってくるかのルールを知ることで、関数を使えたり、関数を作れたりします。
この2つ特徴として、”引数”は0個(*1)から複数の引数を付ける事が出来ます。ただしルールとしては個数は予め決めておくこと(*2)、”戻り値は”0個(*1)か1つしか無いのが異なります。また、これらは渡すだけで値が変わってしまうことは無いので要注意です。(*3)
よくC言語の勉強でprintfとか、strpcyなどの記号が出てきますが、これは予め用意されている「関数」ってだけで自分で作る「関数」と何ら変わりません。

■関数書式
戻り値型 関数名( 第1引数の型と記号,第2引数の型と記号,,,,) {
処理
第1引数の記号,,,あれやこれや
第2引数の記号,,,あれやこれや
処理
・・・
return(戻り値);
}

■例

#include <stdio.h>
#include <string.h>

int foo(int prm1,char prm2, char *prm3) {
    int rtn = 111;

    printf("[3] %d %c %s %d\n",prm1,prm2,prm3,rtn);

    prm1 = 2;
    prm2 = 'B';
    strcpy(prm3,"xyz");
    rtn    = 999;

    printf("[4] %d %c %s %d\n",prm1,prm2,prm3,rtn);

    return(rtn);
}

int main(int argc,char **argv) {
    int  p1;
    char p2;
    char p3[4];
    int  r;

    p1 = 1;
    p2 = 'A';
    strcpy(p3,"abc");
    r = 100;

    printf("[1] %d %c %s %d\n",p1,p2,p3,r);

    r = foo(p1,p2,p3);

    printf("[2] %d %c %s %d\n",p1,p2,p3,r);

    return 0;
}

■実行結果

$ gcc main.c
$ ./a.out
[1] 1 A abc 100
[3] 1 A abc 111
[4] 2 B xyz 999
[2] 1 A xyz 999

(*1):不要の場合はvoid型を定義します
(*2):va_argなど可変引数も有りますが、C言語マスターになったら覚えましょう。いきなりprintfが可変引数なんですが・・・
(*3):C言語で参照渡しとか言われますが厳密には実態渡ししかないです。ポインタ渡しでポインタが指す領域が変更することは可能ですが、"渡し"での部分のポインタの領域は実態渡しです。スパゲティがお好きな方は、externやグローバル変数など