LoginSignup
3
2

More than 5 years have passed since last update.

はじめてのC++【4日目】

Last updated at Posted at 2019-02-28

はじめに

qiita週間コメント数ランキングに乗りました。
アドバイスや訂正、編集リクエストをしてくださる皆様には大変感謝しています。

本題に入ります。
昨日の投稿でbのアドレスとFooのアドレスが表示されないと嘆いていたのですが、コメントにて教えていただけたのでまとめておこうと思います。

コード
スクリーンショット 2019-02-27 23.52.38.png
実行結果
スクリーンショット 2019-02-27 23.52.46.png

【bの原因】
char b[10]は、暗黙の型変換でchar*に書き換わる。
すると、 std::cout は、char*をポインタではなく文字列として認識する。
※std::coutではオーバーロード解決ルールによってvoid*型として受け入れられているが、char*の場合は例外で文字列を表示する方が優先的になってしまう。
そして、b[10]には文字が格納されていないため実行結果は空白となった。

【bの解決方法】
static_castを使用する。
(参照 https://www.yunabe.jp/docs/cpp_casts.html)
static_cast
ある型からある型への暗黙の変換が存在する時に(たとえばintからdoubleなど)、そこで暗黙の変換が行われることを明示する場合に行います。

【Fooの原因】
std::coutの中のルールでは、bool型との相性がよく1か0(true,false)の結果が帰ってくる
【Fooの解決策】
調べてみてもわからなかったので、理解が深まってから書き加えようと思います。
修正後コード
スクリーンショット 2019-02-28 16.07.42.png
修正後実行結果
スクリーンショット 2019-02-28 16.07.51.png
無事bのアドレスが表示できました。
オーバーロードって便利なようで結構めんどくさいんですね。

04-02 ポインタ天国1

アドレスを表示させるコード
スクリーンショット 2019-02-28 16.14.45.png
スクリーンショット 2019-02-28 16.15.01.png

*pの中にaのアドレスを入れている。
このように、ポインタはアドレスを格納することができる

04-03 ポインタ天国2

・参照とポインタの違い
参照は初期化時に一度参照先を決めたら二度と参照先を変えられない
ポインタはアドレスを再代入すれば何度でも変えられる
「参照とポインタを使い分けるのは人それぞれ」と書かれているが、実践を積んだら使い分けられるときが来ると信じる。
ポインタ宣言のときは型と変数名の間に"*"をつける。
int *a, b
この場合はbはポインタではない
変数宣言の場合には一度に2個以上の宣言をしないほうがいい。

04-04 配列再考

配列を引数に取る場合は要素数を指定する必要がない

void func(int array[]); //仮引数を宣言するときには[]がいる
int n[5];
func(n); //配列の要素数は書かない

仮引数は、関数を呼んだときに新しく変数が作られ、その変数が実引数のあたいで初期化される。しかし、仮引数が配列の場合は、新しく変数が作られ、その変数に配列の先頭アドレスを代入する。
そして、アドレスを介して変数を操作し配列変数を操作している。
これにより、関数呼び出しごとに大量のデータをコピーせずに済む。

//同じ意味
func(n)
func(&n[])

配列変数はポインタを使って関数に渡す。このことを「ポインタ渡し(pass by pointer)」という。
「配列の先頭アドレス」という値を渡しているので「値渡し」になると考えられるが、呼び出し側にある変数の値を変更することができる。ポインタ渡しは値渡しと参照渡しの両方の性質を持っている。

//同じ意味
void func(int array[]);
void func(int* array);

「値渡し」「参照渡し」「ポインタ渡し」同じような名前でややこしいので整理する。

「値渡し」・・・関数呼び出し時に作られた変数に値を渡す。もとの変数の値は変わらない。

「参照渡し」・・・関数呼び出し時に作られた変数にアドレスを渡す。一度参照渡しをしたら参照先の上書きができずにもう一度初期化して再び参照渡しをしなければならない。もとの変数の値は変わる。

「ポインタ渡し」・・・関数呼び出し時に作られた変数にアドレスを渡す。一度渡されたアドレスは別のアドレスに何度でも上書き可能である。もとの変数の値は変わる。

04-05 配列再々考

配列変数はメモリ上に連続して存在している

04-06 アドレスを計算?

アドレスに+1の足し算をすると、一つあとの隣のアドレスに場所が変わる。
&n[1] + 1 = &n[2]
アドレスに-1の引き算をすると、一つ前の隣のアドレスに場所が変わる。
&n[1] - 1 = &n[0]
しかし、アドレスは乗除算ができない。

注意
&n[0] - 1 ≠ &n[-1]
ヌルポインタになる可能性があるので気をつけること。
正常に動作がしなくなるみたい。

04-07 配列とポインタの切っても切れない関係

文字列の長さを取得する関数
スクリーンショット 2019-02-28 22.37.59.png
スクリーンショット 2019-02-28 22.38.09.png

(少し寄り道)
size_tに興味を持ったのでC++の日本語リファレンスを見てみた。
size_tは、オブジェクトのバイト数を表現できる程度に十分に大きい符号なし整数型である。
ブジェクトのバイト数やコンテナの要素数を表現するために用いられる。
スクリーンショット 2019-02-28 22.39.37.png
このように定義すれば、自分でdouble型やint型のようなものが作れるということかな。

上記のコードは、for文で'\0'になるまでi++をして計算している。

文字列の長さを取得する関数(ポインタ演算)
スクリーンショット 2019-02-28 23.05.48.png

文字列の長さを取得する関数(ポインタ演算2)
スクリーンショット 2019-02-28 23.09.15.png
*(str + i)str[i]は同じ意味

04-08 変えてくれるな1

ポインタ型や参照型の前にconstをつけると参照先の値を変更できなくなる。
ポインタや配列、参照を引数に取るときは、constを使う必要があるか常に考える癖をつける。
constはプログラムを安全に作成するためには非常に重要なものである。

特定の文字を文字列から見つけるプログラム
スクリーンショット 2019-02-28 23.25.37.png
スクリーンショット 2019-02-28 23.25.44.png

04-09 変えてくれるな2

constを定義するときの位置はどこでもいいが、普通は最初か最後に書く。
スクリーンショット 2019-02-28 23.40.37.png
スクリーンショット 2019-02-28 23.40.49.png

配列のサイズを変更しようとしたとき4行目を少し変更するだけでいいだけになる。
このように、変更に強いプログラムを作成することはバグを減らす手段として非常に有効である。
また、読みやすいプログラムを心がけることもバグを減らす手段として非常に有効である。
マジックナンバーの使用はできる限り避けるべきであり、ARRAY_SIZEのような表意定数を可能な限り使う。

終わりに

ポインタってややこしい。情報量が多すぎますね
クラスまでの道のり長すぎ。
1週間でロベール終わらすとかいってたけどあと一週間期間伸ばす。(これで足りればいいのだが)

3
2
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2