読者です 読者をやめる 読者になる 読者になる

kentsu.log

何かその時の興味でいろいろする人。最近はScala使ってる。アルゴリズムと自然言語処理、深層学習が大好き。

C言語の基礎を一日で復習してみた[メモ]

最近、パケットキャプチャをC言語作っていて

なんだかちょいちょい怪しい部分があるので、基礎を一日で復習してみようと思う

 

変数、ループ、場合分けなんかは大丈夫だと思っていたけど

Cでメモリを意識する際に重要なことを見つけた

 

まず、変数とは

値を格納するためのメモリ領域を確保し、名前をつけたもの

ということ(らしい)

 

次に強制的型変換について

強制的な型変換、つまり キャスト」 について

 

キャストすることで、変数や数値といった値を強制的に別のデータ型に変換できる

例えば、整数型の値を計算して浮動小数型に代入すると値に応じて小数部が切り捨てられたりするが

計算部を浮動小数型でキャストすることで欠損なく本来の値を代入できる

例をあげるなら

 

int a = 3;

int b = 2;

があったとき

float f;にf = a/b;と言った代入をしたならfは1.0になる

しかしf = (float)a/b;とすることで強制的にfloatに変換され

f = 1.5となる

 

 

次にキーボード入力

scanf("%dなど",変数のアドレス);で行う

また複数入力が可能であったり、区切り文字なんかも入れることができる

ただ入力形式が間違っていたりするとバグの原因になる

それを回避するためには入力を文字列として受け取り、文字列解析する方法が一般的

 

 

ここから先はしばらく出来る(ループ、if,switch)

ただ気をつけるならswitch~case文でcaseには数値しか入らず、条件式などが入らないこと

 

関数についても普段から使っているから大丈夫なはず

ただmain関数より下に記述するときはプロトタイプ宣言が必要

また関数は複数の戻り値を返すことができない(当然)

 

ここからだんだん内容が重くなる…

 

複数のデータをまとめて扱いたいときはもちろん「配列」を使う

配列は

データ型 配列名[要素数];で宣言でき

データ型 配列名 = {};と{}の中にデータをおいておくと初期化出来る

 

データを取り出すなら、配列名[インデックス];で出来る

 

配列のコピーはfor文でひたすらコピーすることもできるけど

string.hをインクルードしてmemcpy関数を使うのといい

memcpy(コピー先,コピー元,サイズ);

サイズはsizeof関数で出せるからこっちの方が簡単

 

配列を使うことで文字列を扱うことが出来る

文字型のデータ型があるので、これを配列にすることで実質的に文字列として表現できる

文字を配列で扱うと最後に"\0"が自動で追加されている(配列のサイズに余裕があるときだけ)

またこれは明示的に宣言可

この終端文字が無いと延々と文字列を表示してしまうためエラー、バグの原因となる

 

文字列はatoi関数で数値に変換できる

atoi関数stdlib.hにあるからそれをインクルードすればいい

 

文字列をコピーしたいときは

string.hをインクルードしstrcpy(コピー先、コピー元);

 

文字列を結合したいときは

string.hをインクルードして、strcat(コピー先,コピー元);

 

文字列合成したいときは

sprintf(生成先,初期式,変数…);で行える

 

文字列の長さはstring.hをインクルードしてstrlen関数を使う

 

文字列の比較は==で行えないのでstring.hをインクルードして

strcmp関数を使う、中身が同じである場合0が返ってくる

 

 

鬼のポインタ

 

多分ココが一番長くなる

ポインタとはなにか…

 

その前にまず、最初の方で書いたけど変数はメモリ上にあるって言うことが結構重要

そして変数のメモリ上での場所(入門書じゃ住所って言われたりする)をアドレスっていって

表示するには

 

&変数名をprintfとかで"%p"で呼び出せば出来る

 

ざっくり言えば&演算子はアドレスを求めることができる奴ということ

そしてなぜアドレスを知る必要があるかというと

 

関数は通常、引数がポインタなどメモリに関連したもので無い限り値渡しであり

実際に渡される変数の値はコピーされた関数内でどう変更されようと影響がない

 

しかし、アドレスをしりそこに代入することで呼び出し元の変数を書き換えることができる

これを使ったのがscanf関数

 

そして、このアドレスを格納する変数をポインタ型変数といい、ポインタ型変数に格納されている値をポインタ値という

 

ポインタは

 

データ型* 変数名;

データ型 *変数名;として宣言し、アドレスを代入しなければならない

(宣言しっぱなしはダメ)

ちなみにアドレスを代入するときは

 

変数名1 = &変数名2;でいい

ポインタに値を代入するときは

*変数名 = 値;でいい

 

また値の表示は

整数なら%dで*変数名でいい

 

さらにポインタを引数とする関数に大してアドレスをかっこ内に直接記述し渡すことができる

 

しかし、引数を配列にすると奇妙な動作する

例えば、配列の要素数が無視されたりコピー側を変更すると呼び出し元まで変更されるとか

 

実態は、配列を引数で渡すと実はアドレスが渡されているから

 

そのため配列を引数にしなくてもポインタが引数であれば配列は渡せる

 

しかもポインタ型を宣言しておきながらそれをいきなり配列チックに扱える

 

それはCの配列がアドレスを足し算することで表現していて、たまたまポインタと設計が似ていたから

だけど、配列は先頭の固定されたアドレスで、ポインタは可変なアドレスだから実は違う

 

さっきの使い方すると、ポインタに配列を渡した際にアクセスするとき

 

*(ポインタ変数+要素番号)みたいにアクセスできるこれがポインタ演算

 

ポインタについてはこれぐらいで

 

 

あと、構造体、共用体、マクロ、動的配列なんかあるけどそれは問題ないからメモらない

 

さくっとまとめたから色々間違ってるかもしれない