C++ の基礎 Day 2
1. 基本型
整数型
~ char
以外の整数型のサイズは規格で規定されていない。一般的なサイズを示す。
負数の表現には 2 の補数が使われる。
型名
サイズ(バイト)
略名
範囲
signed char
1
-128 ~ 127
unsigned char
1
0 ~ 255
signed short int
2
short
-32,768 ~ 32,767
unsigned short int
2
unsigned short
0 ~ 65,535
signed int
4
int
-2,147,483,648 ~ 2,147,483,647
unsigned int
4
unsigned
0 ~ 4,294,967,295
signed long int
LLP64: 4 LP64: 8
long
-2,147,483,648 ~ 2,147,483,647 / -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
unsigned long int
LLP64: 4 LP64: 8
unsigned long
0 ~ 4,294,967,295 / 0 ~ 18,446,744,073,709,551,615
signed long long int
8
long long
-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
unsigned long long int
8
unsigned long long
0 ~ 18,446,744,073,709,551,615
Windows は LLP64 を、Linux, macOS は LP64 モデルを採用している。
浮動小数点型
型名
サイズ(バイト)
説明
float
4
単精度浮動小数点数
double
8
倍精度浮動小数点数
long double
8 または 16
倍精度浮動小数点数 / 拡張倍精度浮動小数点数
文字型
型名
サイズ(バイト)
説明
char
1
wchar_t
2 または 4
ワイド文字型
char8_t
1
UTF-8 文字型
char16_t
2
UTF-16 文字型
char32_t
4
UTF-32 文字型
真偽値型
型名
サイズ(バイト)
値
bool
1
true
または false
#include <print>
int main ()
{
int a = 100 ;
std :: println ( "a = {}" , a );
unsigned long long b = 10000000000000000000ULL ;
std :: println ( "b = {}" , b );
double c = 3.14 ;
std :: println ( "c = {}" , c );
char d = 'A' ;
std :: println ( "d = {}" , d );
bool e = true ;
std :: println ( "e = {}" , e );
}
出力 a = 100
b = 10000000000000000000
c = 3.14
d = A
e = true
2. サイズが規定された整数型の別名
処理系による整数型のサイズの違いを吸収するため、ビット数が規定された整数型の別名が、標準ライブラリ <cstdint>
(シーエスティーディーイント)で提供される。
さまざまな環境を想定したコードを書く場合、これらの型名を使うことが推奨される。
型名
サイズ(バイト)
範囲
std::int8_t
1
-128 ~ 127
std::uint8_t
1
0 ~ 255
std::int16_t
2
-32,768 ~ 32,767
std::uint16_t
2
0 ~ 65,535
std::int32_t
4
-2,147,483,648 ~ 2,147,483,647
std::uint32_t
4
0 ~ 4,294,967,295
std::int64_t
8
-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
std::uint64_t
8
0 ~ 18,446,744,073,709,551,615
具体的には、基本型の別名として、std::int_Nt
や std::uint_Nt
が宣言される。
例 1 typedef signed char int8_t ;
typedef long long int64_t ;
例 2 typedef signed char int8_t ;
typedef long int64_t ;
3. 変数
基本型の変数は、明示的に値を初期化しない場合、不定値が入る。基本型の変数の値を読む前には、必ず初期化が必要である。
#include <print>
int main ()
{
int a = 42 ;
std :: println ( "a = {}" , a );
int b = 100 , c = 200 ; // 1 行で複数の変数
std :: println ( "b = {}, c = {}" , b , c );
int d ; // 初期化されていない
std :: println ( "d = {}" , d );
}
出力 a = 42
b = 100, c = 200
d = (不定値)
4. スコープ
スコープは、変数が有効な範囲を示す。C++ では、{ }
で囲まれたブロックがスコープを構成し、その中で宣言された変数はそのブロック内でのみ有効である。
#include <print>
int main ()
{
// ここではまだ a も b も使えない
int a = 42 ;
// ここでは a は使えるが、b はまだ使えない
{
// ここではまだ b は使えない
int b = 100 ;
// ここでは a も b も使える
std :: println ( "a = {}, b = {}" , a , b );
} // b はここでスコープ終了
// ここでは a だけが使える
std :: println ( "a = {}" , a );
}
特定のスコープの変数名と、その外側のスコープの変数名が同じ場合、スコープ内の変数が優先される。これを Shadowing(シャドウイング)という。
紛らわしさが生じるため、Shadowing はなるべく避けるべきである。
#include <print>
int main ()
{
int a = 42 ;
{
int a = 100 ; // 別の変数で Shadowing
std :: println ( "a = {}" , a );
}
std :: println ( "a = {}" , a );
}
次のようなコードは Shadowing ではない。
#include <print>
int main ()
{
{
int a = 42 ;
std :: println ( "a = {}" , a );
} // a はここでスコープ終了
{
int a = 100 ;
std :: println ( "a = {}" , a );
}
}
C++ では、変数のスコープが狭いほど、その変数が使われる範囲が限定されるため、プログラムのメンテナンス性が向上する。
5. 標準入力
標準入力からの入力は、<iostream>
の std::cin
(シーイン)を使う。std::cin
は、>>
(入力)演算子を使って、変数に値を読み込む。
#include <iostream>
#include <print>
int main ()
{
int a ; // 直後に cin を使うので、初期化しなくてもよい
std :: cin >> a ;
std :: println ( "a = {}" , a );
double b ;
std :: cin >> b ;
std :: println ( "b = {}" , b );
}
>>
演算子を連ねて、複数の変数に値を読み込むこともできる。
空白や改行による区切りか、パースできない文字が出るまで入力文字列を消費し、値を読み込む。
#include <iostream>
#include <print>
int main ()
{
int a , b ;
std :: cin >> a >> b ;
std :: println ( "a = {}, b = {}" , a , b );
}
#include <iostream>
#include <print>
int main ()
{
int a , b ;
char c ;
std :: cin >> a >> c >> b ;
std :: println ( "a = {}, b = {}" , a , b );
std :: println ( "c = {}" , c );
}
6. コメント
C++ のコメントは C 言語と同様に、//
(スラッシュ・スラッシュ)で始まる行コメントと、/*
(スラッシュ・アスタリスク)と */
で囲まれるブロックコメント。
ブロックコメントはネストできない。
int main ()
{
// 1 行コメント
/*
複数行コメント
*/
}
コメントのテクニック 1
先頭の /
の有無を変更するだけで、ブロックコメントを有効・無効にできる。
コメントのテクニック 2
先頭の /
の有無を変更するだけで、ブロックコメントの範囲を変更できる。
7. 演算子
7.1 算術演算子
演算子
名前
説明
+a
単項プラス
a の値をそのまま返す
-a
単項マイナス
a の符号を反転した値を返す
a + b
加算
a と b の和を返す
a - b
減算
a から b を引いた値を返す
a * b
乗算
a と b の積を返す
a / b
除算
a を b で割った商を返す。整数どうしの場合、小数以下は切り捨て。
a % b
剰余
a を b で割った余りを返す。浮動小数点数型には使えない。
#include <print>
int main ()
{
int a = 24 , b = 10 ;
std :: println ( "a + b = {}" , a + b );
std :: println ( "a - b = {}" , a - b );
std :: println ( "a * b = {}" , a * b );
std :: println ( "a / b = {}" , a / b );
std :: println ( "a % b = {}" , a % b );
}
出力 a + b = 34
a - b = 14
a * b = 240
a / b = 2
a % b = 4
7.2 ビット演算子
演算子
名前
説明
~a
ビット否定
a のビットを反転した値を返す
a & b
ビット積
a と b のビットごとの積を返す
a | b
ビット和
a と b のビットごとの和を返す
a ^ b
排他的論理和
a と b のビットごとの排他的論理和を返す
a << b
左シフト
a のビットを b ビット左にシフトする
a >> b
右シフト
a のビットを b ビット右にシフトする
#include <print>
int main ()
{
unsigned a = 0b1010 , b = 0b1100 ;
std :: println ( "~a = {:b}" , ~ a );
std :: println ( "a & b = {:04b}" , a & b );
std :: println ( "a | b = {:04b}" , a | b );
std :: println ( "a ^ b = {:04b}" , a ^ b );
std :: println ( "a << 2 = {:b}" , a << 2 );
std :: println ( "a >> 2 = {:b}" , a >> 2 );
}
出力 ~a = 11111111111111111111111111110101
a & b = 1000
a | b = 1110
a ^ b = 0110
a << 2 = 101000
a >> 2 = 10
符号付き整数に対する <<
は論理シフトとなり、対応する符号無し整数の左シフトとビット表現が同じになる。
符号付き整数に対する >>
は算術シフトとなり、対応する符号無し整数の右シフトとはビット表現が異なる。
#include <print>
int main ()
{
int a = 1 ;
int b = 1 << 31 ;
std :: println ( "{}" , a );
std :: println ( "{}" , b );
std :: println ( "{:b}" , ( unsigned ) a );
std :: println ( "{:b}" , ( unsigned ) b );
std :: println ( "--------------------" );
int c = -161 ;
int d = -161 >> 1 ;
std :: println ( "{}" , c );
std :: println ( "{}" , d );
std :: println ( "{:b}" , ( unsigned ) c );
std :: println ( "{:b}" , ( unsigned ) d );
}
出力 1
-2147483648
1
10000000000000000000000000000000
--------------------
-161
-81
11111111111111111111111101011111
11111111111111111111111110101111
7.3 比較演算子
演算子
名前
説明
a == b
等しい
a と b が等しい場合 true
a != b
等しくない
a と b が等しくない場合 true
a < b
より小さい
a が b より小さい場合 true
a <= b
以下
a が b 以下の場合 true
a > b
より大きい
a が b より大きい場合 true
a >= b
以上
a が b 以上の場合 true
a <=> b
三方比較
比較する型と値の大小に応じた定数
#include <print>
int main ()
{
int a = 42 , b = 100 ;
std :: println ( "a == b: {}" , a == b );
std :: println ( "a != b: {}" , a != b );
std :: println ( "a < b: {}" , a < b );
std :: println ( "a <= b: {}" , a <= b );
std :: println ( "a > b: {}" , a > b );
std :: println ( "a >= b: {}" , a >= b );
}
出力 a == b: false
a != b: true
a < b: true
a <= b: true
a > b: false
a >= b: false
7.4 論理演算子
演算子
名前
説明
!a
論理否定
a が false
の場合 true
a && b
論理積
a と b がともに true
の場合 true
a || b
論理和
a または b のどちらかが true
の場合 true
#include <print>
int main ()
{
bool a = true , b = false ;
std :: println ( "!a: {}" , ! a );
std :: println ( "a && b: {}" , a && b );
std :: println ( "a || b: {}" , a || b );
}
出力 !a: false
a && b: false
a || b: true
7.5 代入演算子
演算子
名前
説明
a = b
代入
a に b の値を代入する
#include <print>
int main ()
{
int a = 42 ;
a = 100 ;
std :: println ( "a = {}" , a );
}
7.6 複合代入演算子
演算子
名前
説明
a += b
加算代入
a + b の結果を a に代入する
a -= b
減算代入
a - b の結果を a に代入する
a *= b
乗算代入
a * b の結果を a に代入する
a /= b
除算代入
a / b の結果を a に代入する
a %= b
剰余代入
a % b の結果を a に代入する
a &= b
ビット積代入
a & b の結果を a に代入する
a |= b
ビット和代入
a | b の結果を a に代入する
a ^= b
排他的論理和代入
a ^ b の結果を a に代入する
a <<= b
左シフト代入
a << b の結果を a に代入する
a >>= b
右シフト代入
a >> b の結果を a に代入する
#include <print>
int main ()
{
int a = 42 ;
a += 100 ;
std :: println ( "a = {}" , a );
unsigned b = 0b1010 ;
b <<= 2 ;
std :: println ( "b = {:b}" , b );
}
7.7 インクリメント・デクリメント演算子
演算子
名前
説明
++a
前置(ぜんち)インクリメント
a の値を 1 増やしてから、その値を返す
a++
後置(こうち)インクリメント
a の値を返してから、その値を 1 増やす
--a
前置デクリメント
a の値を 1 減らしてから、その値を返す
a--
後置デクリメント
a の値を返してから、その値を 1 減らす
#include <print>
int main ()
{
int a = 100 ;
std :: println ( "++a = {}" , ++ a );
std :: println ( "a = {}" , a );
int b = 100 ;
std :: println ( "b++ = {}" , b ++ );
std :: println ( "b = {}" , b );
}
出力 ++a = 101
a = 101
b++ = 100
b = 101
7.8 添字(そえじ)演算子
演算子
名前
説明
a[b]
添字
a の b 番目の要素にアクセス
#include <print>
int main ()
{
int a [] = { 1 , 2 , 3 };
std :: println ( "a[0] = {}" , a [ 0 ]);
std :: println ( "a[1] = {}" , a [ 1 ]);
std :: println ( "a[2] = {}" , a [ 2 ]);
}
出力 a[0] = 1
a[1] = 2
a[2] = 3
7.9 間接参照演算子
演算子
名前
説明
*a
間接参照
a が指す要素へアクセス
#include <print>
int main ()
{
int a = 42 ;
int * p = & a ;
std :: println ( "*p = {}" , * p );
}
7.10 アドレス演算子
演算子
名前
説明
&a
アドレス
a のアドレスを返す
#include <print>
int main ()
{
int a = 42 ;
std :: println ( "&a = {}" , ( void * ) & a );
}
7.11 関数呼び出し演算子
演算子
名前
説明
a(b, c, ...)
関数呼び出し
b, c, ... を引数として関数 a を呼び出す
#include <print>
int Add ( int a , int b )
{
return a + b ;
}
int main ()
{
int a = 42 , b = 100 ;
std :: println ( "Add(a, b) = {}" , Add ( a , b ));
}
7.12 条件演算子
演算子
名前
説明
a ? b : c
条件演算子
a が true
なら b, false
なら c を返す
#include <print>
int main ()
{
int a = 42 , b = 100 ;
int c = ( a < b ) ? a : b ;
std :: println ( "c = {}" , c );
}
7.13 演算子の優先順位
表: C++ Operator Precedence
1 つの式に複数の演算子が含まれる場合、表中の演算子の優先順位に従って評価される。
( )
で囲むことで、演算子の優先順位を変更できる。
優先順位が同じ場合、表中の結合法則(Associativity)の順に評価される。
7.14 演算子のオーバーロード
7.1 ~ 7.12 の演算子は、ユーザー定義型に対しての挙動を定義できる。これを演算子のオーバーロードという。
のちの章で詳しく説明する。
8. 変数の名前の付け方
使える文字は、アルファベット、数字、アンダースコア _
大文字と小文字は区別される。
数字で始めることはできない。
予約語は使えない。
連続するアンダースコアを含んだり(例えば a__b
)、アンダースコアのあとに大文字から始まる名前(例えば _Cpp
)は、コンパイラや標準ライブラリ側が特別な用途で使うため避ける。
変数名の例
有効な名前か?
price
OK
Price
OK, ただし変数名は小文字から始まるのが一般的
itemPrice
OK
item_price
OK
itemPrice2
OK
item price
NG(間にスペースがあってはいけない)
2itemPrice
NG(数字から始まってはいけない)
itemPrice!
NG(!
は使えない)
item__price
NG(連続するアンダースコアは避ける)
_Price
NG(アンダースコアと大文字から始まる名前は避ける)
力試し
Day 2 までの知識で解ける練習問題。最後に解答例あり。
為替レートと、店舗が設定するスプレッド(手数料)が次の形式で標準入力から与えられる。
入力に基づいたレート表を、客にわかりやすいと思う任意の形式で出力せよ。
レート表に続いて、1 万円を各通貨に両替する場合の金額も任意の形式で出力せよ。
入力の形式 eur
jpy
gbp
aud
cad
cny
inr
chf
spread
入力データ
意味
eur
1 ユーロが何 US ドルであるか
jpy
1 日本円が何 US ドルであるか
gbp
1 スターリング・ポンドが何 US ドルであるか
aud
1 オーストラリアドルが何 US ドルであるか
cad
1 カナダドルが何 US ドルであるか
cny
1 人民元が何 US ドルであるか
inr
1 インドルピーが何 US ドルであるか
chf
1 スイスフランが何 US ドルであるか
spread
店舗のスプレッド(手数料の割合)
為替レートが 1 US ドル 150 円で、スプレッドが 0.01
(1%) であるとき、US ドルの売値は 151.5 円、買値は 148.5 円となる。
入力例 1.05286
0.00668
1.21488
0.63917
0.7339
0.13678
0.01202
1.09163
0.015
出力例 === Currency Exchange ===
We sell We buy
USD: 151.95 147.46
EUR: 159.98 155.25
...
客が 1 US ドルを買うには 151.95 円必要で、客が 1 US ドルを渡すと 147.46 円を受け取れること、1 ユーロを買うには 159.98 円必要で、1 ユーロを渡すと 155.25 円を受け取れることを伝える内容となっている。
解答例
#include <iostream>
#include <print>
int main ()
{
// 標準入力から 8 つの為替レートを受け取る
double eur , jpy , gbp , aud , cad , cny , inr , chf ;
std :: cin >> eur >> jpy >> gbp >> aud >> cad >> cny >> inr >> chf ;
// 標準入力からスプレッドを受け取る
double spread ;
std :: cin >> spread ;
// 各通貨の為替レート(JPY)を計算する
double usd_jpy = ( 1.0 / jpy );
double eur_jpy = ( usd_jpy * eur ); // (eur / jpy) でも同じ
double gbp_jpy = ( usd_jpy * gbp );
double aud_jpy = ( usd_jpy * aud );
double cad_jpy = ( usd_jpy * cad );
double cny_jpy = ( usd_jpy * cny );
double inr_jpy = ( usd_jpy * inr );
double chf_jpy = ( usd_jpy * chf );
// スプレッド
double sellSpread = ( 1.0 + spread );
double buySpread = ( 1.0 - spread );
std :: println ( "=== Currency Exchange ===" );
std :: println ( " We sell \t We buy" );
std :: println ( "USD: {:.5} \t {:.5}" , ( usd_jpy * sellSpread ), ( usd_jpy * buySpread ));
std :: println ( "EUR: {:.5} \t {:.5}" , ( eur_jpy * sellSpread ), ( eur_jpy * buySpread ));
std :: println ( "GBP: {:.5} \t {:.5}" , ( gbp_jpy * sellSpread ), ( gbp_jpy * buySpread ));
std :: println ( "AUD: {:.5} \t {:.5}" , ( aud_jpy * sellSpread ), ( aud_jpy * buySpread ));
std :: println ( "CAD: {:.5} \t {:.5}" , ( cad_jpy * sellSpread ), ( cad_jpy * buySpread ));
std :: println ( "CNY: {:.5} \t {:.5}" , ( cny_jpy * sellSpread ), ( cny_jpy * buySpread ));
std :: println ( "INR: {:.5} \t {:.5}" , ( inr_jpy * sellSpread ), ( inr_jpy * buySpread ));
std :: println ( "CHF: {:.5} \t {:.5}" , ( chf_jpy * sellSpread ), ( chf_jpy * buySpread ));
// 1 万円を各通貨に両替した場合の金額
std :: println ( " \n === Exchange 10,000 JPY ===" );
std :: println ( "USD: {:.2f}" , ( 10000.0 / ( usd_jpy * sellSpread )));
std :: println ( "EUR: {:.2f}" , ( 10000.0 / ( eur_jpy * sellSpread )));
std :: println ( "GBP: {:.2f}" , ( 10000.0 / ( gbp_jpy * sellSpread )));
std :: println ( "AUD: {:.2f}" , ( 10000.0 / ( aud_jpy * sellSpread )));
std :: println ( "CAD: {:.2f}" , ( 10000.0 / ( cad_jpy * sellSpread )));
std :: println ( "CNY: {:.2f}" , ( 10000.0 / ( cny_jpy * sellSpread )));
std :: println ( "INR: {:.2f}" , ( 10000.0 / ( inr_jpy * sellSpread )));
std :: println ( "CHF: {:.2f}" , ( 10000.0 / ( chf_jpy * sellSpread )));
}