topazの遊び場

いろいろやってみる

そろそろC++やるぞパート24 演算子 「sizeof」「後置++, --」

そろそろC++やります

そろそろC++をやります。そろそろC++をやりたいからです。
何回やるかはわかりません。基礎を理解するまではやろうと思います。

という感じでやっています。

※ 初心者がメモレベルで記録するので、技術記事としてはお力になれないかもしれません。

内容

  • 今回は「sizeof演算子」と「後置インクリメント/デクリメント」を行っていきます。

sizeof 演算子 sizeof

構文

sizeof unary-expression
sizeof  ( type-name )

charのサイズに対する相対的になサイズが返されます。charのサイズは基本的に1となっています。

演算結果としてsize_tが返されます。

int i = 100;
size_t si = sizeof(i);
cout << si << endl;

long l = 100;
size_t sl = sizeof(l);
cout << sl << endl;

Output

4
8

sizeofを重ねて、こういうこともできます。

int i = 100;
size_t si = sizeof(i);
size_t s = sizeof(sizeof(sizeof(sizeof(sizeof(si)))));

配列の要素数の取得

sizeof array / sizeof array[0]

sizeof array は配列に使用しているメモリ全て、 sizeof array[0] は配列の型の大きさを表します。

int ar[2] = {13, 17};
size_t arSize = sizeof(ar) / sizeof(ar[0]);
cout << arSize << endl;

Output

2

前置インクリメント/デクリメント演算子: ++. --

構文

++ unary-expression
-- unary-expression

後置インクリメント/デクリメントと同様に、1単位分増減させます。

int i = 100;
cout << i << endl;
++i;
cout << i << endl;
--i;
cout << i << endl;

Output

100
101
100

前置と後置の違い

同じ「1単位分増減させる」演算子ですが、それぞれ定義されていることには理由があります。それが、評価と使用の順番です。それぞれこのようになっています。

前置 インクリメント/デクリメント

  1. 評価
  2. 使用

後置インクリメント/デクリメント

  1. 使用
  2. 評価

何を意味しているのかソースコードで見てみます。

int i = 100;
int j = i++;
cout << i << endl;
cout << j << endl;

int i2 = 100;
int j2 = ++i2;
cout << i2 << endl;
cout << j2 << endl;

Output

101
100
101 
101

int j = i++;のところは先にint j = i;として代入されて、使用された後にi++が評価されて101に変わります。
一方で、int j2 = ++i2;は先に++i2が評価されて101になって、int j2 = 101;となり、j2は101になります

アセンブリ

前置と後置でコンパイルした結果を見てみます。以下の2つのソースコードで試します。

main.cpp

#include <iostream>
using namespace std;

int main()
{
    int i = 100;
    int j = i++;
}

main2.cpp

#include <iostream>
using namespace std;

int main()
{
    int i = 100;
    int j = ++i;
}

コンパイルには-Sをつけてアセンブリに、ファイルの比較にはdiffコマンドを使用します。

$ diff main.cpp main2.cpp 
7c7
<     int j = i++;
---
>     int j = ++i;
$ g++ -S main.cpp -o a.s 
$ g++ -S main2.cpp -o b.s
$ diff a.s b.s           
13,14c13,14
<       add     w9, w8, #1
<       str     w9, [sp, #12]
---
>       add     w8, w8, #1
>       str     w8, [sp, #12]

アセンブリにした時に差分は2行現れました。これだけでは分かりにくいので、前後を少し取り出してみます。

a.s

mov  w8, #100
str w8, [sp, #12]
ldr w8, [sp, #12]
add w9, w8, #1
str w9, [sp, #12]
str w8, [sp, #8]
  1. 100を w8 レジスタに入れる
  2. w8レジスタの内容を #12 に保存
  3. #12の内容を w8 レジスタに読み込み
  4. w8 と 1 を足した値をw9に入れる
  5. w9レジスタの内容を #12 に保存
  6. w8レジスタの内容を #8 に保存

b.s

mov  w8, #100
str w8, [sp, #12]
ldr w8, [sp, #12]
add w8, w8, #1
str w8, [sp, #12]
str w8, [sp, #8]
  1. 100を w8 レジスタに入れる
  2. w8レジスタの内容を #12 に保存
  3. #12の内容を w8 レジスタに読み込み
  4. w8 と 1 を足した値をw8に入れる
  5. w8レジスタの内容を #12 に保存
  6. #8の内容をw8レジスタに読み込み。

後置インクリメントの場合は、w9レジスタに値に計算した値を一度入れています。#12にはインクリメントされた101、#8にはインクリメント前の100が入っている状態になります。

このように少し違いがあるので使用する際には注意が必要です。

参考リスト

感想

インクリメントとデクリメントが間違えずに使わないとエラーが出そうです。sizeof演算子はあまり使わないと思います。配列の要素数もこれを用いて求めるよりは初期化した値を取っておく方がよいかなと思いました。

次回も引き続き演算子を行おうと思います。