C++ の基礎 Day 2
1. 基本型
整数型
~ char
以外の整数型のサイズは規格で規定されていない。一般的なサイズを示す。
負数の表現には 2 の補数が使われる。
Windows は LLP64 を、Linux, macOS は LP64 モデルを採用している。
浮動小数点型
文字型
真偽値型
#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::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 算術演算子
#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 ビット演算子
#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 比較演算子
#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 論理演算子
#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 代入演算子
#include <print>
int main ()
{
int a = 42 ;
a = 100 ;
std :: println ( "a = {}" , a );
}
7.6 複合代入演算子
#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 インクリメント・デクリメント演算子
#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 添字(そえじ)演算子
#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 間接参照演算子
#include <print>
int main ()
{
int a = 42 ;
int * p = & a ;
std :: println ( "*p = {}" , * p );
}
7.10 アドレス演算子
#include <print>
int main ()
{
int a = 42 ;
std :: println ( "&a = {}" , ( void * ) & a );
}
7.11 関数呼び出し演算子
#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 条件演算子
#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
)は、コンパイラや標準ライブラリ側が特別な用途で使うため避ける。
力試し
Day 2 までの知識で解ける練習問題。最後に解答例あり。
為替レートと、店舗が設定するスプレッド(手数料)が次の形式で標準入力から与えられる。
入力に基づいたレート表を、客にわかりやすいと思う任意の形式で出力せよ。
レート表に続いて、1 万円を各通貨に両替する場合の金額も任意の形式で出力せよ。
入力の形式 eur
jpy
gbp
aud
cad
cny
inr
chf
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 )));
}