そろそろC++やります
そろそろC++をやります。そろそろC++をやりたいからです。
何回やるかはわかりません。基礎を理解するまではやろうと思います。
という感じでやっています。
※ 初心者がメモレベルで記録するので、技術記事としてはお力になれないかもしれません。
内容
- 配列について触れていく予定が、定数式について実験を行いました。
配列
配列とは、連続したメモリ領域を占領する、同じ方のオブジェクトシーケンスです。
従来のC言語スタイルの配列は、一般的ですが多くのバグの原因となっています。今回登場するCスタイルの配列より、std::array
やstd::vector
を使うことをお勧めします。
スタックの宣言
配列の宣言は{型名} {変数名}[要素数]
です。基本型も自作定義型も配列にすることができます。
int a[100]; MyClass myClass[100];
要素の数は、整数リテラルまたは定数式で指定する必要があります。
定数式とは
定数式とは、コンパイル時に計算できる式のことです。int a = 13 + 57;
があった時に、コンパイル時にint a = 70:
にしてくれるようなものです。
(ここからは配列とは関係ない内容です。面白かったのでいろいろやって、長くなりました。)
例えば以下のmain.cpp
があったとします。
// main.cpp int main() { int a = 13 + 57; int b = 24 + 37; int c = a + b; cout << c << endl; return 0; }
これを実行すると131
が出力されます。これはプログラムを実行しなくても計算できる値になります。
何が起こっているかアセンブリで確認してみます。M1 MacBookで arm64
のアーキテクチャを使用しています。
$ g++ -S main.cpp
生成されたmain.s
の計算該当部分を抜き出してみます。
mov w8, #70 stur w8, [x29, #-8] mov w8, #61 stur w8, [x29, #-12] ldur w8, [x29, #-8] ldur w9, [x29, #-12] add w8, w8, w9
これをみると、70
は int a = 13 + 57;
の計算結果、61
はint b = 24 + 37;
の計算結果ということが分かります。これら計算をコンパイラがやっていて、結果をアセンブリに書いています。このアセンブリの時に計算される式を(13+57や24+37)を定数式と呼びます。
このアセンブリではこのようなことを行っています。
1. w8レジスタに70を書き込む 2. w8レジスタの内容をオフセット-8のメモリにコピーする 3. w8レジスタに61を書き込む 4. w8レジスタの内容をオフセット-12メモリにコピーする 5. オフセット-8の内容を、w8レジスタにロードする 6. オフセット-9の内容を、w9レジスタにロードする 7. w8レジスタとw9レジスタの内容を足して、w8に入れる
このアセンブリを実行してみると、先ほどと同じ結果を得ることができます。
$ g++ main.s
$ ./a.out
131
このアセンブリは「レジスタに書き込んで、メモリにコピーして、またレジスタにロードする」とずいぶん面倒なことを行っています。
最終的に、「7. w8レジスタとw9レジスタの内容を足して、w8に入れる」ができれば良いので、アセンブリを書き換えて直接レジスタに代入したのを計算に使ってみます。
mov w8, #70 mov w9, #61 add w8, w8, w9
70をw8レジスタに書き込み、61をw9に書き込みますそれを足してw8に書き込みます。
$ g++ main.s
$ ./a.out
131
実行しても同じ131が得られたことが分かります。
と、関係ないアセンブリの話に入ってしまいましたが、言いたいことは、定数式はコンパイルしてアセンブリになった時点で、計算された状態になるもの、ということです。
これでいいように思えますが、一度メモリに保存していないということは、変数a
の値を失っているということです。今回の場合はこのような最適化が行えましたが、行えない場合もあるので注意が必要です。
脱線してしまいましたが、キリがよいので今回はここで終わりにします。
参考リンク
感想
配列を行うつもりが、定数式について考えていました。今回C++では初めてアセンブリに書き起こしました。久しぶりにアセンブリ見たのですが、やはり少し読める言語を解読しているみたいで楽しですね。
次回は、絶対に配列に入ろうと思います。std::array
やstd::vector
も使ってみたいなと思いました。