コーディングの基礎 [Memo #000]
Table of Contents
コーディングに関するメモ
コーディングに必要な知識をまとめていく
前提知識
コーディングテストを解くための知識
- データ構造
- 連結リスト
- 木、トライ木、グラフ
- スタックとキュー
- ヒープ
- ベクタ/配列リスト
- ハッシュテーブル
- アルゴリズム
- 幅優先探索
- 深さ優先探索
- 二分木
- マージソート
- クイックソート
- 概念
- ビット操作
- メモリ
- 再帰
- 動的計画法
- ビック・オー記法、スペース
解答フローチャート
- 問題理解
- 問題文で与えられた条件、インプット、アウトプット等を正確に把握する
- ブルートフォース
- 効率が悪くても良いので、最も単純なアルゴリズムで解を得る
- 最適化
- 計算量を抑えた解法を探索する
- リファクタ
- 可読性の高いコードになるようにリファクタする
- テスト・提出
- テストを実行し、特別なケース・エッジケースに対応していることを確認する
C++コーティングメモ
参考サイト:https://fanblogs.jp/cplusplus/archive/391/0
画面への出力
std::cout
コード
// 画面への出力を行うプログラム
#include <iostream>
//↑画面やキーボードに対する入出力を行うためのライブラリ(input-output stream)に関する情報が格納されている<iostream>の内容を取り込む.
using namespace std;
//↑using指令
//↑詳細は9章を参照
int main() {
cout << "初めてのC++プログラム1\n";
cout << "画面に出力しています\n";
cout << "初めてのC++プログラム2\n画面に出力しています\n";
cout << "はじめまして.\n" << "こんにちは.\n";
//↑ストリームへの連続した出力
cout << "初めてのC++プログラム4\n"; cout
<< "画面に出力しています\n";
//↑自由形式記述の例
cout << "初めてのC++プログラム5\n"
"画面に出力しています\n";
//↑空白類(white space)を挟んで,分割して表記できる.
}
変数
コード
//
#include <iostream>
using namespace std;
int main() {
cout << "18と63の和は" << 18 + 63 << "です.\n";
//↑「18 + 63」のように,整数を表す定数のことを整数リテラル(integer liberal)と呼ぶ.
int x;
int y;
x = 63;
y = 18;
//↑変数の宣言時には,初期化子を与えて確実に初期化する.
while (0) {
int x = 63;
}
while (0) {
int x(63);
}
while (0) {
int x{ 63 };
}
while (0) {
int x = { 63 };
}
//↑他の形式の初期化
cout << "xの値は" << x << "です.\n";
cout << "yの値は" << y << "です.\n";
cout << "合計は" << x + y << "です.\n";
cout << "平均は" << (x + y) / 2 << "です.\n";
}
キーボードからの入力
std::cin
コード
//
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <string>
using namespace std;
int main() {
if (0) {
int x, y;
cout << "xとyを加減乗除します.\n";
cout << "xの値:";
cin >> x;
cout << "yの値:";
cin >> y;
cout << x + y << endl;
cout << x - y << endl;
cout << x * y << endl;
cout << x / y << endl;
cout << x % y << endl;
//↑演算の対象となる式のことをオペランド(operand)と呼ぶ.
//↑「+」などは演算子(operator)であり,一般に算術演算子(arithmetic operator)と呼ばれるもの.
cin >> x >> y;
//↑値の連続した読み込み
cout << x + y << endl;
cout << x - y << endl;
cout << x * y << endl;
cout << x / y << endl;
cout << x % y << endl;
int z = -x;
//↑単項の算術演算子
double d_x;
double d_y;
cin >> d_x >> d_y;
cout << d_x + d_y << endl;
cout << d_x - d_y << endl;
cout << d_x * d_y << endl;
cout << d_x / d_y << endl;
// cout << d_x % d_y << endl;
//↑実数型(double型)のオペランドには%演算子は適用できない.
const double PI = 3.1416;
//↑定値オブジェクト(const object)は以後書き換えることができない.
//↑プログラム中に埋め込まれた数値は,何を表すのかが理解しにくい(magic numberになりやすい)ので使う.
//↑const修飾子とvolatile修飾子の2種類を合わせてcv修飾子(qualifier)と呼ぶ.
/*
cv修飾子について-> http://exlight.net/devel/cpp/cv-qualifier.html
volatile修飾子について-> https://world-trigger.net/2018/03/14/volatile-c/
*/
double r;
cout << "半径:";
cin >> r;
cout << "円周の長さは" << 2 * PI * r << "です.\n";
cout << "面積は" << PI * r * r << "です.\n";
}
if (1) {
srand(time(NULL));
//↑乱数の種の値を変更するsrand関数.
//↑ここで生成される乱数は疑似乱数であることに注意.
int lucky = rand() % 10;
cout << "今日のラッキーナンバーは" << lucky << "です.\n";
char c;
cout << "文字を入力してください:";
cin >> c;
cout << "打ち込んだ文字は" << c << "です.\n";
string name;
cout << "お名前は:";
//cin >> name;
getline(cin, name);
//↑スペースも読み込む文字の読み込み
//↑リターンキーより前に打ち込んだすべての文字が,文字型の変数に格納される.
cout << "こんにちは" << name << "さん.\n";
}
}
if文
コード
#include <iostream>
#include <cmath>
using namespace std;
int main() {
if (0) {
int n;
cout << "整数値:";
cin >> n;
//関係演算子(relational operator)を使って大小関係を判定する
if (n > 0)
cout << "その値は正です。\n";
else
cout << "その値は0か負です。\n";
int a, b;
cout << "整数a:"; cin >> a;
cout << "整数b:"; cin >> b;
//等価演算子(equality operator)
if (a != b)
cout << "二つの値は等しくありません。\n";
else
cout << "二つの値は等しいです。\n";
}
if (0) {
int n;
cout << "整数値:";
cin >> n;
//論理否定演算子(logical negative operator)
if (!n)
cout << "その値はゼロです。\n";
else
cout << "その値はゼロではありません。\n";
}
if (0) {
int n;
cout << "整数値:";
cin >> n;
//論理積演算子(logical AND operator)
if (n == 0)
cout << "その値はゼロです。\n";
else if (n >= -9 && n <= 9)
cout << "その値は1桁です。\n";
else
cout << "その値は2桁以上です。\n";
//↑論理積演算子&&と論理和演算子||の評価では、短絡評価が行われる。
}
if (0) {
int a, b;
cout << "整数a:"; cin >> a;
cout << "整数b:"; cin >> b;
//条件演算子(conditional operator)、条件式(conditional expression)
int min = a < b ? a : b;
cout << "小さいほうの値は" << min << "です。\n";
}
if (1) {
int n;
cout << "整数値:";
cin >> n;
if (int mod = n % 10) {
cout << "その値は10で割り切れません。\n";
cout << "最下位桁は" << mod << "です。\n";
}
else {
cout << "その値は10で割り切れます。\n";
}
double x;
cout << "実数値:";
cin >> x;
//fmod関数
if (double m = fmod(x, 10)) {
cout << "その値は10で割り切れません。\n";
cout << "剰余は" << m << "です。\n";
}
else {
cout << "その値は10で割り切れます。\n";
}
}
}
switch文
コード
//switch分(switch statement)
#include <iostream>
using namespace std;
int main() {
int hand;
cout << "手を選んでください(0:グー / 1:チョキ / 2:パー):";
cin >> hand;
switch (hand) {
case 0:
cout << "グー\n"; break;
case 1:
cout << "チョキ\n"; break;
case 2:
cout << "パー\n"; break;
}
int n;
cout << "整数を入力せよ:";
cin >> n;
switch (n) {
case 0:
cout << "A";
cout << "B"; break;
case 2:
cout << "C"; break;
case 5:
cout << "D"; break;
case 6:
case 7:
cout << "E"; break;
default:
cout << "F"; break;
}
cout << "\n";
}
字句要素
コード
//C++で用意されたキーワード(keyword)はプログラム作成者が変数などの名前として利用することができない。
//キーワードに準ずる代替表現(alternative representation)がある。
#include <iostream>
using namespace std;
int main() {
int y;
cout << "年を入力せよ:";
cin >> y;
cout << "その年は閏年";
if (y % 4 == 0 and y % 100 != 0 or y % 400 == 0) {
cout << "です。\n";
}
else {
cout << "ではありません。\n";
}
}
//識別子(identifier)とは、変数・関数・クラスなどに与えられる名前です。
//先頭の文字は非数字で、2文字目以降は非数字または数字です。
//演算子(operator)には優先度(precedence)がある。
//同じ優先度の演算子が連続するときには結合規則(associativity)が適用される。
do文
コード
//do文
#include <string>
#include <iostream>
using namespace std;
int main() {
string retry;
do {
int month;
cout << "季節を求めます。\n何月ですか:";
cin >> month;
if (month >= 3 && month <= 5) {
cout << "それは春です。\n";
}
else if (month >= 6 && month <= 8) {
cout << "それは夏です。\n";
}
else if (month >= 9 && month <= 11) {
cout << "それは秋です。\n";
}
else if (month == 12 || month == 1 || month == 2) {
cout << "それは冬です。\n";
}
else {
cout << "そんな月はありませんよ。\n";
}
cout << "もう一度? Y…Yes/N…No:";
cin >> retry;
} while (retry == "Y" || retry == "y");
}
//do文を用いることで、一定範囲の値の読み込みを設定することができる。
//特定の値が出るまで繰り返す「数当てゲーム」等で使える。
while文
コード
//while文
//増分演算子と減分演算子
//x++ -> xの値をインクリメントする。生成するのは増加"前"の値。(後置増分演算子)(※左辺値にはなれない)
//x-- -> xの値をデクリメントする。 生成するのは減少"前"の値。(後置減分演算子)(※左辺値にはなれない)
//++x -> xの値をインクリメントする。生成するのは増加"後"の値。(前置増分演算子)
//--x -> xの値をデクリメントする。 生成するのは増加"後"の値。(前置減分演算子)
//do文とwhile文
//do文 -> 後判定繰り返し
//while文 -> 前判定繰り返し
//左辺値(lvalue)式と右辺値(rvalue)式
//複合代入演算子
#include <iostream>
using namespace std;
int main() {
int x;
cout << "正の整数値を逆から表示します。\n";
do {
cout << "正の整数値:";
cin >> x;
} while (x <= 0);
cout << "逆から読むと";
while (x > 0) {
cout << x % 10;
x /= 10;
}
cout << "です。\n";
}
//整数の和を求める
//条件におけるコンマ演算子の活用
for文
多重ループ
コード
//九九の表
//setw操作子(桁幅を指定)
#include <iomanip>
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
cout << setw(3) << i * j;
}
cout << '\n';
}
}
//直角三角形の表示
break文、continue文、goto文(ラベル付き文)
コード
#include <iostream>
using namespace std;
int main() {
//break文
if (0) {
int n;
cout << "整数を加算します。\n";
cout << "何個加算しますか:\n";
cin >> n;
int sum = 0;
for (int i = 0; i < n; i++) {
int t;
cout << "整数:";
cin >> t;
if (sum + t > 1000) {
cout << "合計が1000を超えました。\n最後の数値は無視します。\n";
break;
}
sum += t;
}
cout << "合計は" << sum << "です\n";
}
//continue文
if (0) {
int n;
cout << "整数を加算します。\n";
cout << "何個加算しますか:\n";
cin >> n;
int sum = 0;
for (int i = 0; i < n; i++) {
int t;
cout << "整数:";
cin >> t;
if (t < 0) {
cout << "負の数は加算しません。\n";
continue;
}
sum += t;
}
cout << "合計は" << sum << "です\n";
}
//continue文
if (0) {
int n;
cout << "整数を加算します。\n";
cout << "何個加算しますか:\n";
cin >> n;
cout << "9999で強制終了します。\n";
int sum = 0;
for (int i = 0; i < n; i++) {
int t;
cout << "整数:";
cin >> t;
if (t == 9999) {
goto Exit;
}
sum += t;
}
cout << "合計は" << sum << "です\n";
Exit:
; // ラベル付き文(labeled statement)
}
//面積がnで縦横が整数の長方形の辺の長さを列挙
if (1) {
int n;
cout << "面積は:";
cin >> n;
for (int i = 1; i < n; i++) {
if (i * i > n) break;
if (n % i != 0) continue;
cout << i << "×" << n / i << '\n';
}
}
}
拡張表記と操作子
コード
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
//拡張表記(escape sequence)
cout << "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
cout << "\r12345\n"; // \f -> 復帰:現表示位置がその行の先頭に移動する。
cout << "\n";
cout << "\\ \? \' \" \n";
cout << "二重引用符\"で囲まれた\"ABC\"は文字列リテラルです。\n";
cout << "単一引用符\'で囲まれた\'A\'は文字リテラルです。\n";
//三つ組と二つ組
//操作子(manipulator)
cout << oct << 1234 << '\n'; // 8進数
cout << dec << 1234 << '\n'; // 10進数
cout << hex << 1234 << '\n'; // 16進数
cout << showbase;
cout << oct << 1234 << '\n';
cout << dec << 1234 << '\n';
cout << hex << 1234 << '\n';
cout << setw(10) << internal << "abc\n";
cout << setw(10) << left << "abc\n";
cout << setw(10) << right << "abc\n";
cout << setbase(10); // 整数の入出力をn進数で行う。
cout << setw(10) << internal << -123 << '\n'; // 詰め物文字を中間位置に入れて出力する。
cout << setw(10) << left << -123 << '\n';
cout << setw(10) << right << -123 << '\n';
cout << setfill('*'); // 詰め物文字を*に設定する。
cout << setw(10) << internal << -123 << '\n';
cout << setw(10) << left << -123 << '\n';
cout << setw(10) << right << -123 << '\n';
cout << fixed << setw(10) << setprecision(2) << 123.5 << endl;
// fixed -> 浮動小数点数を固定小数点記法で出力する。
// setprecision -> 精度をn桁に指定する。
// endl -> 改行文字を出力してバッファをフラッシュする。
cout << scientific << setw(10) << setprecision(2) << 123.5 << endl;
// scientific -> 浮動小数点数を指数付き記法で出力する。
}
//std::internalは、符号を内部に配置する際によく使用される。
12345FGHIJKLMNOPQRSTUVWXYZ
\ ? ' "
二重引用符"で囲まれた"ABC"は文字列リテラルです。
単一引用符'で囲まれた'A'は文字リテラルです。
2322
1234
4d2
02322
1234
0x4d2
abc
abc
abc
- 123
-123
-123
-******123
-123******
******-123
****123.50
**1.24e+02
算術型
コード
//算術型
#include <climits>
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main() {
//整数型
//符号なし整数型(unsigned integer type) -> 0と正の整数値を表現
//符号付き整数型(signed integer type) -> 負と0の正の整数値を表現
//型指定子(type specifier)による型指定 以下は汎整数型(integral type)あるいは整数型(integer type)
char c1 = 65;
signed char sc1 = 65;
unsigned char uc1 = 65;
short int si1 = -32768;
int i1 = -32768;
long int li1 = -2147483648;
unsigned short int usi1 = 65535;
unsigned int ui1 = 65535;
unsigned long int uli1 = 4294967295;
cout << c1 << endl;
cout << (char)(c1 + 1) << endl;
cout << sc1 << endl;
cout << (char)(sc1 + 1) << endl;
cout << uc1 << endl;
cout << (char)(uc1 + 1) << endl;
cout << si1 << endl;
cout << (short int)(si1 - 1) << endl;
cout << i1 << endl;
cout << (int)(i1 - 1) << endl; // intはlong intの範囲を持つ
cout << li1 << endl;
cout << (long int)(li1 - 1) << endl;
cout << usi1 << endl;
cout << (unsigned short int)(usi1 + 1) << endl;
cout << ui1 << endl;
cout << (unsigned int)(ui1 + 1) << endl;
cout << uli1 << endl;
cout << (unsigned long int)(uli1 + 1) << endl;
long long int ulli1 = 9223372036854775807;
cout << (long long int)ulli1 << endl;
cout << (long long int)(ulli1 + 1) << endl;
//オブジェクト形式マクロ
//#define指令(オブジェクト形式マクロ(object-like macro))
cout << "long intの最大値は" << LONG_MAX << endl; // <climits>で定義される // マクロ名(macro name):LONG_MAX
cout << "\n\nこの処理系の整数型で表現できる値\n";
cout << "char :" << CHAR_MIN << "~" << CHAR_MAX << endl;
cout << "signed char :" << SCHAR_MIN << "~" << SCHAR_MAX << endl;
cout << "unsigned char:" << 0 << "~" << UCHAR_MAX << endl;
cout << "short int:" << SHRT_MIN << "~" << SHRT_MAX << endl;
cout << "int :" << INT_MIN << "~" << INT_MAX << endl;
cout << "long int :" << LONG_MIN << "~" << LONG_MAX << endl;
cout << "unsigned short int:" << 0 << "~" << USHRT_MAX << endl;
cout << "unsigned int :" << 0 << "~" << UINT_MAX << endl;
cout << "unsigned long int :" << 0 << "~" << ULONG_MAX << endl;
//C++では、処理系特性を表すnumeric_limitsクラステンプレートが<limits>ヘッダで提供される。
cout << "short int:" << numeric_limits<short>::min() << "~" << numeric_limits<short>::max() << endl;
cout << "int :" << numeric_limits<int>::min() << "~" << numeric_limits<int>::max() << endl;
cout << "long int :" << numeric_limits<long>::min() << "~" << numeric_limits<long>::max() << endl;
//文字型
wchar_t wmoji1 = L'A';
wchar_t wmoji2 = L'あ';
wchar_t wstr[] = L"ABCあいう";
cout << (char)wmoji1 << ":" << sizeof(wmoji1) << endl; // 2バイト
cout << (char)wmoji2 << ":" << sizeof(wmoji2) << endl; // 2バイト
cout << (wchar_t)wstr[0] << endl << (wchar_t)wstr[1] << endl << (wchar_t)wstr[2] << endl << (wchar_t)wstr[3] << endl << (wchar_t)wstr[4] << endl << (wchar_t)wstr[5] << endl
<< ":" << sizeof(wstr) << endl; // 7文字 * 2バイト = 14バイト
printf("%ls\n", wstr); // wchar_tの出力
printf("%s\n", wstr); // charの出力
//<cctype>ヘッダによる関数呼び出し式(表示可能かを判定するisprint関数など)
//符号付き整数型と符号無し整数型
//基数変換
//整数リテラル
//整数接尾語(integer suffix)と整数リテラルの型
//組込み型(built-in type)(charやint、longなどのC++が言語体系中にもつ型)
//オブジェクトとsizeof演算子
//size_t型とtypedef宣言
//typedef宣言(typedef declaration)は、型の同義語、すなわち型に対して別の名前を与える宣言。
//size_t型は、符号なし整数型の同義語となるようtypedef宣言された型である。
//typeid演算子(typeid operator)
//型に関する情報を生成する。型情報に関する文字列は typeid(型 or 式).name() によって取得できる。
//整数の内部
//オブジェクト(object)は、値を表現するための記憶域であり、ビット(bit)の集まりとして記憶域上に格納される。
//整数型の内部のビット表現として採用されているのは、純2進数記法(pure binary numeration system)。
//符号無し整数の内部表現
//値の2進表現をそのままビットに対応させたもの。
//オブジェクト内のバイトを並べる順序が処理系に依存しているのが注意点。
//符号付き整数の内部表現
//2の補数表現:多くの処理系で用いられている
//1の補数表現:2の補数表現よりも1つだけ少なくなる
//符号付き絶対値表現:表現できる数値の範囲は1の補数表現と同じ
//bool型
//浮動小数点型(floating point type)
float a = 123456789.0;
double b = 12345678901234567890.0;
long double c = 123456789012345678901234567890.0;
cout << "a = " << setprecision(30) << a << '\n';
cout << "b = " << setprecision(30) << b << '\n';
cout << "c = " << setprecision(30) << c << '\n';
//指数部と仮数部のビット数は、型と処理系に依存する。
//浮動小数点リテラル(floating-point literal)
//浮動小数点接尾語(floating suffix)
cout << .5 << '\n' << 12. << '\n' << .5F << '\n' << 1.F << '\n' << 1.23E4 << '\n' << 89.3E-5;
//算術型(arithmetic type)
//最大の丸め誤差
}
unsigned short int:0~65535
unsigned int :0~4294967295
unsigned long int :0~4294967295
short int:-32768~32767
int :-2147483648~2147483647
long int :-2147483648~2147483647
A:2
B:2
65
66
67
12354
12356
12358
:14
ABCA
a = 123456792
b = 12345678901234567168
c = 123456789012345677877719597056
0.5
12
0.5
1
12300
0.000893000000000000022058743720521
演算と型
コード
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
//演算と型
//暗黙の型変換(implicit type conversion)
cout << "15 / 2 = " << 15 / 2 << '\n';
cout << "15.0 / 2.0 = " << 15.0 / 2.0 << '\n';
cout << "15.0 / 2 = " << 15.0 / 2 << '\n';
cout << "15 / 2.0 = " << 15 / 2.0 << '\n';
//明示的型変換(explicit type conversion)
//キャスト記法による明示的型変換
cout << "int(15 / 2.0) = " << int(15 / 2.0) << '\n'; // 関数的記法
cout << "(int)(15 / 2.0) = " << (int)(15 / 2.0) << '\n'; // キャスト記法
//static_cast演算子による明示的型変換 <- 型変換には積極的にこれを使う
cout << static_cast<int>(15 / 2.0) << '\n';
//動的キャスト演算子dynamic_castはダウンキャストを行う。http://yohshiy.blog.fc2.com/blog-entry-15.html
//強制キャスト演算子reinterpret_castは奥の手のキャスト。http://yohshiy.blog.fc2.com/blog-entry-18.html
//定値性キャスト演算子const_castはconst修飾子をはずすためのキャスト。http://yohshiy.blog.fc2.com/blog-entry-16.html
//繰り返しの制御(正しい例ではない。できるだけ判定の変数には整数を利用するように。)
float sum = 0.0F;
cout << fixed << setprecision(6);
for (float x = 0.0F; x <= 1.0F; x += 0.001F) {
cout << "x = " << x << '\n';
sum += x;
}
cout << "sum = " << sum << '\n';
//↓繰り返しのたびにiを1000で割った値をxとするので、誤差が累積しない点で優れている
float sum2 = 0.0F;
cout << fixed << setprecision(6);
for (int i = 0; i <= 1000; i++) {
float x = static_cast<float>(i) / 1000;
cout << "x = " << x << '\n';
sum2 += x;
}
cout << "sum = " << sum << '\n';
//型変換の規則 P154参照
//例:char,signed char,unsigned char,short int,unsigned short int -> int の変換は可能。
}
15 / 2 = 7
15.0 / 2.0 = 7.5
15.0 / 2 = 7.5
15 / 2.0 = 7.5
int(15 / 2.0) = 7
(int)(15 / 2.0) = 7
7
x = 0.000000
x = 0.001000
x = 0.002000
x = 0.003000
x = 0.004000
x = 0.005000
x = 0.006000
x = 0.007000
x = 0.008000
x = 0.009000
...
x = 0.990991
x = 0.991991
x = 0.992991
x = 0.993991
x = 0.994991
x = 0.995991
x = 0.996991
x = 0.997991
x = 0.998991
x = 0.999991
sum = 500.496674
列挙型
コード
#include <iostream>
using namespace std;
int main() {
//列挙体(enumeration)
//列挙体名(enum-name)animalと列挙子(enumerator)Dog,Cat,Monkey,Invalid。
//各列挙子には、先頭から純に0,1,2,3の整数値が自動的に割り振られる。
enum animal { Dog, Cat, Monkey, Invalid};
int type;
do {
cout << "0->犬 1->猫 2->猿 3->終了 :";
cin >> type;
} while (type < Dog || type > Invalid);
if (type != Invalid) {
animal selected = static_cast<animal>(type); // selectedは0,1,2,3の値を取り得る変数となる。
switch (selected) {
case Dog: cout << "ワンワン!\n"; break;
case Cat: cout << "ニャーオ!\n"; break;
case Monkey: cout << "ウキウキ!\n"; break;
}
}
//名前のない列挙体
//列挙体は定値オブジェクトとして定義することもできるというメリットがある。
//列挙体で表せそうな整数値の集まりは、列挙体として定義するべき。
}
配列
コード
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
//配列(array)
//配列内の個々の要素のアクセスに利用するのが、添字演算子(subscript operator)。
//x[y]は配列xの先頭からy個後ろの要素をアクセスする。xとyは一方がポイント型で、もう一方が整数型。
//for文による配列の走査(traverse) = 配列の要素を1つずつなぞっていくこと。
int a[5];
for (int i = 0; i < 5; i++) {
a[i] = i + 1;
}
for (int i = 0; i < 5; i++) {
cout << "a[" << i << "] = " << a[i] << endl;
}
//配列の初期化
//配列の要素数
cout << "要素数:" << sizeof(a) / sizeof(a[0]) << endl;
//配列によつ成績処理
//配列型の情報の取得
cout << "配列aの型:" << typeid(a).name() << endl;
cout << "aの要素型:" << typeid(a[0]).name() << endl;
//配列の要素の並びの反転
//配列のコピー 繰返し文などを用いた全要素の逐次代入
}
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
要素数:5
配列aの型:int [5]
aの要素型:int
多次元配列
コード
#include <iostream>
using namespace std;
int main() {
//多次元配列
//C++では多次元配列の構成要素は、末尾側の添字が優先的に増えていく順位並ぶ。
//例:a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]
//多次元配列の要素数
int a[4][3];
cout << "配列aは" << sizeof(a) / sizeof(a[0]) << "行"
<< sizeof(a[0]) / sizeof(a[0][0]) << "列です。\n";
cout << "構成要素は" << sizeof(a) / sizeof(a[0][0]) << "個です。\n";
//多次元配列型の情報の取得
//初期化子
}
配列aは4行3列です。
構成要素は12個です。
関数
コード
#include <iostream>
using namespace std;
double power(double x, int n) {
double tmp = 1.0;
while (n-- > 0) {
tmp *= x;
}
return tmp;
}
void put_stars(int n) {
while (n-- > 0) {
cout << '*';
}
}
void put_nchar(char c, int n) {
while (n-- > 0) {
cout << c;
}
}
bool confirm_retry() {
int retry;
do {
cout << "もう一度?<Yes->1/No->0>";
cin >> retry;
} while (retry != 0 && retry != 1);
return static_cast<bool>(retry);
}
int main() {
//関数(function)
//関数定義(function definition)
//関数呼出し
//main関数
//main関数でreturn文を実行すると、プログラムの実行が終了する。
//関数宣言(function declaration)
//前方に定義のない関数を呼び出すには、関数宣言が必要である。
//値渡し(pass by value)
if (0) {
double a;
int b;
cout << "aのb乗を求めます。\n";
cout << "実数a:"; cin >> a;
cout << "実数b:"; cin >> b;
cout << a << "の" << b << "乗は" << power(a, b) << "です。\n";
}
//void関数
if (0) {
int n;
cout << "左下直角の二等辺三角形を表示します\n";
cout << "段数は:";
cin >> n;
for (int i = 1; i <= n; i++) {
put_stars(i);
cout << '\n';
}
}
//関数の汎用性
//関数は、なるべく汎用性が高くなるように設計する。
if (0) {
int n;
cout << "左下直角の二等辺三角形を表示します\n";
cout << "段数は:";
cin >> n;
for (int i = 1; i <= n; i++) {
put_nchar(' ', n - i);
put_nchar('*', i);
cout << '\n';
}
}
//他の関数の呼び出し
//実引数と仮引数の型
//仮引数とは異なる型の実引数を渡すと、必要に応じて暗黙の型変換が行われる。
//プログラム終了のための標準ライブラリ(<cstdlib>ヘッダ)
//異常プログラム終了 -> abort();
//正常プログラム終了 -> exit();
//引数を受け取らない関数
if (1) {
srand(time(NULL));
cout << "暗算トレーニング開始\n";
do {
int x = rand() % 900 + 100;
int y = rand() % 900 + 100;
int z = rand() % 900 + 100;
while (true) {
int k;
cout << x << " + " << y << " + " << z << " = ";
cin >> k;
if (k == x + y + z) {
break;
}
cout << "違います。\n";
}
} while (confirm_retry());
}
//再帰呼出し(recursive call)
//デフォルト実引数(default argument)
//関数定義あるいは関数宣言でデフォルト実引数を与えておけば、関数呼出し時に実引数を省略できる。
//ビット単位の論理演算を行う関数
//シフト演算
//論理シフト(logical shift)と算術シフト(arithmetic shift)
//引数を受け取らない関数の宣言
//整数型のビット数
//numeric_limitsクラステンプレート
}
参照と参照渡し
コード
#include <iostream>
using namespace std;
void swap(int& x, int& y) {
int t = x;
x = y;
y = t;
}
int main() {
//参照と参照渡し(reference and pass by reference)
//値渡しの限界
//参照(reference)
if (0) {
int x = 1;
int y = 2;
int& a = x; // aはxを参照する
cout << "a = " << a << '\n';
cout << "x = " << x << '\n';
cout << "y = " << y << '\n';
a = 5;
cout << "a = " << a << '\n';
cout << "x = " << x << '\n';
cout << "y = " << y << '\n';
}
//参照渡し
if (1) {
int a, b;
cout << "変数a:"; cin >> a;
cout << "変数b:"; cin >> b;
swap(a, b);
cout << "変数aとbの値を交換しました。\n";
cout << "変数aの値は" << a << "です。\n";
cout << "変数bの値は" << b << "です。\n";
}
//値渡しと参照渡し
//参照渡しは不用心に用いるべきではない。
//三値のソート
}
スコープと記憶域期間
コード
#include <iostream>
using namespace std;
int x = 75; // ファイル有効範囲(file scope) -> 大域的(global)
int fx; // 静的記憶域期間(0で初期化される)
const int a_size = 5;
void print_x() {
cout << "x = " << x << '\n';
}
int& ref() { // xへの参照を返却
static int x; // 静的記憶域期間
return x;
}
int& r(int idx) { // a[idx]への参照を返却
static int a[a_size];
return a[idx]; // a[idx]への参照を返却
}
int main() {
//有効範囲
cout << "x = " << x << '\n';
int x = 999; // ブロック有効範囲(block scope) -> 局所的(local)
cout << "x = " << x << '\n';
for (int i = 1; i <= 5; i++) {
int x = i * 11; // ブロック有効範囲(block scope)
cout << "x = " << x << '\n';
}
cout << "x = " << x << '\n';
cout << "::x = " << ::x << '\n'; // 有効範囲解決演算子(scope resolution operator)※1
//※1 -> ブロック有効範囲をもつ同名の変数に隠されているファイル有効範囲の変数を覗く働きをする。
//::x -> 大域的なxにアクセスする。
//x::y -> 名前空間xの中のyにアクセスする。
print_x(); // 関数print_x()より前方で宣言されているxは、ファイル有効範囲をもつx=75。
//記憶域期間(storage duration)
static int sx; // 静的記憶域期間(0で初期化される) -> 寿命はプログラム実行中は永遠与えられる。
int ax; // 自動記憶域期間(不定値で初期化される) -> 寿命は宣言を囲むブロックの終点まで与えられる。
//cout << "ax = " << ax << '\n'; // 不定値のためビルドエラーが起こる。
cout << endl;
cout << "sx = " << sx << '\n';
cout << "fx = " << fx << '\n';
//静的記憶域期間をもつオブジェクトの初期化
//C++では静的記憶域期間をもつオブジェクトを、定数以外の値で初期化することができる。
//参照を返却する関数
ref() = 5;
cout << endl;
cout << "ref() = " << ref() << '\n';
cout << endl;
for (int i = 0; i < a_size; i++) {
r(i) = i;
}
for (int i = 0; i < a_size; i++) {
cout << "r(" << i << ") = " << r(i) << '\n';
}
}
関数の多重定義とインライン関数
コード
#include <iostream>
using namespace std;
#define sqr(x) ((x)*(x))
inline int max(int a, int b) {
return a > b ? a : b;
}
int max(int a, int b, int c) {
int max = a;
if (b > max) {
max = b;
}
if (c > max) {
max = c;
}
return max;
}
int main() {
//関数の多重定義(overloading)
//多重定義する関数は、シグネチャ(signature)が異なっていなければならない。
int x, y, z;
cout << "xの値:"; cin >> x;
cout << "yの値:"; cin >> y;
cout << "xとyの最大値は" << max(x, y) << "です。\n";
cout << "zの値:"; cin >> z;
cout << "x, y, zの最大値は" << max(x, y, z) << "です。\n";
//インライン関数(inline function)
//実行効率が要求される小規模な関数は、インライン関数として実行する。
//関数形式マクロ(function-like macro)
//マクロの副作用や、定義時の余分な空白、利用している演算子の優先順位による制限に注意。
cout << endl;
cout << "11.3の2乗は" << sqr(11.3) << "です。\n";
}
ポインタ
コード
#include <iostream>
using namespace std;
int main() {
//オブジェクトとアドレス
//アドレス演算子によるアドレスの取得
int n;
double x;
cout << "nのアドレス:" << &n << '\n'; // アドレス演算子(address operator)
cout << "xのアドレス:" << &x << '\n';
//ポインタ
//アドレス演算子によるポインタの生成
//Type型のオブジェクトxにアドレス演算子&を適用した&xは、Type*型のポインタであり、その値はxのアドレスである。
int n2 = 135;
cout << "n:" << n2 << '\n';
cout << "&n:" << &n2 << "番地\n";
int* ptr = &n2;
cout << "ptr:" << ptr << "番地\n";
cout << "*ptr:" << *ptr << '\n';
cout << "n2:" << n2 << " = " << "*(&n2):" << *(&n2) << '\n';
//間接演算子
//*ptrがnを表すことを、*ptrはnのエイリアス(alias)であると表現する。(参照外しと呼ぶ。)
//アクセス先(読み書き先)の決定は、プログラムのコンパイル時に静的(static)に行われるのではなく、プログラム実行時に動的(dynamic)に行われる。
//アドレス演算子と間接演算子を適用した式の評価
//ポインタへのポインタ
cout << "&ptr:" << &ptr << "番地\n"; // &(&n2)はできない。
//ポインタの大きさ
cout << "sizeof(int) = " << sizeof(int) << '\n';
cout << "sizeof(int*) = " << sizeof(int*) << '\n';
cout << "sizeof(int**) = " << sizeof(int**) << '\n';
}
/* 参考サイト */
//ポインタののメリットと必要性 https://monozukuri-c.com/langc-pointer-necessity/
ポインタの値渡し(関数呼び出し)
コード
#include <iostream>
using namespace std;
void sum_mul(int x, int y, int* sum, int* mul) {
*sum = x + y;
*mul = x * y;
}
int main() {
//ポインタの値渡し
int a, b;
int wa = 0, seki = 0;
cout << "整数a:"; cin >> a;
cout << "整数b:"; cin >> b;
sum_mul(a, b, &wa, &seki); // aとbの和と積を求める
cout << "和は" << wa << "です。\n";
cout << "積は" << seki << "です。\n";
}
ポインタと配列
コード
#include <iostream>
#include <iomanip>
using namespace std;
int maxof(const int a[], int n) { // 配列の要素の値は、関数内で書き換えられないように型修飾子(type qualifier)constをつけている。
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return max;
}
//ちなみにconst int a[]はconst int a[5]でもconst int* aでも可。
void fill(int (*a)[3], int n, int v) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < 3; j++) {
a[i][j] = v;
}
}
}
int main() {
//ポインタと配列
//原則として、配列名は、その配列の先頭要素へのポインタと解釈される。
//配列名が先頭要素へのポインタとみなされない文脈
//(1)sizeof演算子およびtypeid演算子のオペランドとして現れたとき(sizeof(配列名)は配列全体の大きさを生成する。)
//(2)アドレス演算子&のオペランドとして現れたとき(&配列名は配列全体へのポインタとなり、先頭要素へのポインタへのポインタとはならない。)
//間接演算子と添字演算子
int a[5] = { 1, 2, 3, 4, 5 };
int* p = a;
for (int i = 0; i < 5; i++) {
cout << "a[" << i << "] = " << a[i] << " *(a+" << i << ") = " << *(a + i) << " "
<< "p[" << i << "] = " << p[i] << " *(p+" << i << ") = " << *(p + i) << "\n";
}
for (int i = 0; i < 5; i++) {
cout << "&a[" << i << "] = " << &a[i] << " a+" << i << " = " << a + i << " "
<< "&p[" << i << "] = " << &p[i] << " p+" << i << " = " << p + i << "\n";
}
//要素を指すポインタの範囲
//要素数nの配列にはa[n]が存在しないにもかかわらず、&a[n]は有効なポインタとして解釈される。
//添字演算子のオペランド
//Type型の配列aの先頭要素a[0]をType*型のpが指すとき、ポインタpはあたかも配列aそのものであるかのように振る舞う。
//ポインタと数値の加算は行えるが、ポインタどうしの加算は行えない。
//配列とポインタの相違点
//配列名を代入演算子の左オペランドとすることはできない。(例:int a[5]; int b[5]; a = b; // エラー)
//関数間の配列の受渡し
//constポインタ型の仮引数
const int ninzu = 5;
int height[ninzu], weight[ninzu];
cout << '\n' << ninzu << "人の身長と体重を入力せよ。\n";
for(int i = 0; i < ninzu; i++) {
cout << i + 1 << "番目の身長:";
cin >> height[i];
cout << i + 1 << "番目の体重:";
cin >> weight[i];
}
int hmax = maxof(height, ninzu);
int wmax = maxof(weight, ninzu);
int hmax2 = maxof(&height[2], 3);
cout << "身長の最大値:" << hmax << "cm\n";
cout << "体重の最大値:" << wmax << "kg\n";
cout << "身長の最大値(3から5要素):" << hmax2 << "cm\n";
//関数間の多次元配列の受渡し
//多次元配列を受け取る関数は、最も先頭の添字に相当する(最も高い次元である)n次元の要素数のみが可変であり、(n - 1)次元以下の要素数は固定である。
int no;
int x[2][3] = { 0 };
int y[4][3] = { 0 };
cout << "全構成要素に代入する値:";
cin >> no;
fill(x, 2, no);
fill(y, 4, no);
cout << "--- x ---\n";
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
cout << setw(3) << x[i][j];
}
cout << '\n';
}
cout << "--- y ---\n";
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
cout << setw(3) << y[i][j];
}
cout << '\n';
}
}
ポインタによる配列要素の走査
コード
#include <iostream>
using namespace std;
void fill_zero(int* p, int n) {
while (n-- > 0) {
*p = 0;
p++;
}
}
int seq_search(int* a, int n, int key) {
for (int i = 0; i < n; i++) {
if (*a++ == key) {
return i;
}
}
return -1;
}
int main() {
//ポインタによる配列要素の走査
int x[5] = { 1, 2, 3, 4, 5 };
int x_size = sizeof(x) / sizeof(x[0]);
fill_zero(x, x_size);
cout << "全要素に0を代入しました。\n";
for (int i = 0; i < x_size; i++) {
cout << "x[" << i << "] = " << x[i] << '\n';
}
//線形探索(linear search)
int key, idx;
int x2[7];
int x2_size = sizeof(x2) / sizeof(x2[0]);
for (int i = 0; i < x2_size; i++) {
cout << "x2[" << i << "] : ";
cin >> x2[i];
}
cout << "探す値は:";
cin >> key;
if ((idx = seq_search(x2, x2_size, key)) != -1) {
cout << "その値をもつ要素はx2[" << idx << "]です。\n";
}
else {
cout << "見つかりませんでした\n";
}
//ポインタどうしの減算によって得られる数値の型は、int型でなく、ptrdiff_t型という型になる。
}
動的記憶域期間(オブジェクトの動的な生成)
コード
//オブジェクトの動的な生成
#include <iostream>
#include <new>
#include <cstddef>
using namespace std;
int main() {
//自動記憶域期間と静的記憶域期間
//これらのオブジェクトは、その寿命をプログラムの流れに委ねる。
//動的記憶域期間(dynamic storage duration)
//new演算子(new operator)
//delete演算子(delete operator)
//プログラム実行中に必要となったオブジェクトは、new演算子で確保・生成して(借りて)、delete演算子で解放・破棄する(返す)。
if (0) {
int* x = new int;
cout << "整数:";
cin >> *x;
cout << "*x = " << *x << '\n';
delete x;
}
if (1) {
int* x = new int(5); // 初期化子付き
cout << "*x = " << *x << '\n';
delete x;
}
//オブジェクトの初期化
//配列オブジェクトの動的生成
//new演算子で配列を動的に生成すると、要素数を実行時に決定できる。生成した配列の破棄は、delete演算子ではなくdelete[]演算子で行う。
int asize;
cout << "要素数:";
cin >> asize;
int* a = new int[asize];
for (int i = 0; i < asize; i++) {
a[i] = i;
}
for (int i = 0; i < asize; i++) {
cout << "a[" << i << "] = " << a[i] << '\n';
}
delete[] a;
//オブジェクト生成の失敗と例外処理(exception handling)
if (1) {
cout << "要素数30000のdouble型配列を繰返し生成します。\n";
while (true) {
try {
double* a = new double[30000];
}
catch (bad_alloc) { // オブジェクトの生成に失敗したことを示すbad_allocは<new>ヘッダで定義されている。
cout << "配列の生成に失敗したのでプログラムを中断します。\n";
return 1;
}
}
}
//ポインタと整数間のキャスト(非推奨)
//空ポインタ
if (0) {
cout << "要素数30000のdouble型配列を繰り返し生成します。\n";
while (true) {
double* a = new(nothrow) double[30000];
if (a == NULL) { // <cstddef>ヘッダでは、空ポインタを表す空ポインタ定数(null pointer constant)がオブジェクト形式マクロNULLとして定義されている。
cout << "配列の生成に失敗したのでプログラムを中断します。\n";
return 1;
}
}
}
//C言語での動的なオブジェクト生成(malloc関数, free関数)
//voidへのポインタ(pointer to void)
int* pi;
void* pv;
pv = pi;
//pi = pv; // エラー
pi = reinterpret_cast<int*>(pv); // OK : 強制キャスト
pi = (int*)(pv); // OK : キャスト記法
//pi = int*(pv); // エラー
//C++11では、空ポインタを表すnullptrが導入された。その型はnullptr_t型であって、sizeof(nullptr_t)はsizeof(void*)と一致する。
//C言語の空ポインタ定数NULLとvoidへのポインタ
}
文字列とポインタ
コード
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
//文字列リテラル
//文字列リテラルの型と値
//文字列リテラルは、const char型の配列に格納され、後ろにナル文字(null character)が自動付加される。
cout << "■文字列リテラル\"ABC\"\n";
cout << " 型:" << typeid("ABC").name() << " 大きさ:" << sizeof("ABC") << "\n\n";
cout << "■文字列リテラル\"\"\n";
cout << " 型:" << typeid("").name() << " 大きさ:" << sizeof("") << "\n\n";
cout << "■文字列リテラル\"ABC\\0DEF\"\n";
cout << " 型:" << typeid("ABC\0DEF").name() << " 大きさ:" << sizeof("ABC\0DEF") << "\n\n";
//文字列リテラルの評価
//文字列リテラルの評価で得られるのは、型がconst char*で、値は先頭文字へのポインタ(アドレス)となる。
//文字列リテラルの記憶域期間 -> 静的記憶域期間
//配列による文字列
char s[4];
//配列による文字列の初期化
s[0] = 'A'; s[1] = 'B'; s[2] = 'C'; s[3] = '\0';
//char s1[] = {'A', 'B', 'c', '\0'};
//char s2[] = {"ABC"};
//char s3[] = "ABC";
cout << "配列aに入っている文字列は\"" << s << "\"です。\n";
//空文字列(null string)
//キーボードからの文字列の読込み
char name[36];
cout << "お名前は:";
cin >> name;
cout << "こんにちは、" << name << "さん!\n\n";
//関数間の文字列の受渡し
//toupper関数、tolower関数
//<cctype>ヘッダ
//両関数とも、返却値はchar型ではなくint型なので、char型にキャストした上で表示する。
//ポインタによる文字列
//2種類の文字列の相違点
//文字列リテラルを示すポインタには、別の文字列リテラルへのポインタを代入できる。
//<string>ヘッダと<cstring>ヘッダ
//文字列の配列
const char a[][5] = { "LISP", "C", "Ada" }; // 2次元配列
const char* p[] = { "PAUL", "X", "MAC" }; // ポインタの配列 -> 文字列の配置の順序や連続性は保証されない。
for (int i = 0; i < 3; i++) {
cout << "a[" << i << "] = \"" << a[i] << "\"\n";
}
for (int i = 0; i < 3; i++) {
cout << "p[" << i << "] = \"" << p[i] << "\"\n";
}
cout << "sizeof(a):" << sizeof(a) << "\n";
cout << "sizeof(p) + sizeof(\"PAUL\") + sizeof(\'X\') + sizeof(\"MAC\"):"
<< sizeof(p) << "+" << sizeof("PAUL") << "+" << sizeof('X') << "+" << sizeof("MAC") << "="
<< sizeof(p) + sizeof("PAUL") + sizeof('X') + sizeof("MAC") << "\n";
cout << "sizeof(char):" << sizeof(char) << '\n' << "sizeof(char*):" << sizeof(char*) << '\n';
//コマンドライン引数
//int main(int argc, char** argv){} のようにmain関数を定義すると、プログラムの起動時にコマンドラインから与えられるパラメータを、文字列の配列として受け取れるようになる。
//第1引数 argc(argument count)
//プログラム名とプログラム仮引数をあわせた個数をint型で受け取る。
//第2引数 argv(argument vector)
//charへのポインタの配列を受け取るポインタ。型はcharへのポインタのポインタとなる。
}
cstringライブラリ
コード
関数テンプレート
コード
分割コンパイルと結合
コード
名前空間
コード
クラス
コード
クラスの実現
コード
クラスの作成
コード
メンバとしてのクラス
コード
カウンタクラス
コード
真理値クラス
コード
複素数クラス
コード
静的データメンバ
コード
静的メンバ関数
コード
コンストラクタとデストラクタ
コード
代入演算子とコピーコンストラクタ
コード
例外処理
コード
クラスによるカプセル化
コード
日付クラス
コード
整数配列クラス
コード
整数型とビット
コード
符号付き整数と符号なし整数
コード
ビットベクトルによる集合クラス
コード
関数へのポインタと動的な関数呼び出し
コード
関数へのポインタの配列
コード
汎用ユーティリティ関数(クイックソート)
コード
汎用ユーティリティ関数の作成
コード
メンバへのポインタ
コード
派生と継承
コード
is-Aの関係
コード
private派生とアクセス権の調整
コード
仮想関数と多相性(ポリモーフィズム)
コード
実行時型情報と動的キャスト
コード
抽象クラス
コード
純粋仮想関数の設計
コード
多重継承
コード
仮想派生
コード
例外処理の基本
コード
例外の再送出
コード
例外クラスの階層化
コード
例外処理のためのライブラリ(exceptionクラス)
コード
クラステンプレート
コード
配列クラステンプレート
コード
スタッククラステンプレート
コード
抽象クラステンプレート
コード
ベクトル(vector<>)
コード
反復子とアルゴリズム
コード
文字列クラス(string)
コード
文字列の配列
コード
標準ストリーム
コード
#include <iostream>
using namespace std;
// 標準ストリーム
//coutは標準出力ストリーム(standard output stream)と呼ばれ、cinは標準入力ストリーム(standard input stream)と呼ばれる。
//他にも、標準エラーストリーム(standard error stream)と呼ばれる2つの出力ストリームcerrとclogがある。
//C言語で定義された標準ストリームstdin、stdout、stderrと結び付けられている。
//cinのみが入力ストリームを表すistream型のオブジェクトであり、それ以外の標準ストリームは出力ストリームを表すostream型のオブジェクトである。
//wchar_t型の文字を扱うためのワイド文字用の標準ストリームも4種類提供されている。
//char用にbasic_istream<>とbasic_ostream<>を特殊化したストリームはナローストリーム(narrow stream)と呼ばれ、
//wchar_t用にbasic_istream<>とbasic_ostream<>を特殊化したストリームはワイドストリーム(wide stream)と呼ばれる。
//整数値のビットシフト演算子>>、<<がbasic_istream、basic_ostreamで多重定義されており、返却値型はbasic_istream&型、basic_ostream&型である。
//putとwriteのメンバ関数は、挿入子ではないものの出力を行う。
// C言語の標準入出力ストリームとの同期
// リダイレクト
//UNIXやMS_WindowsなどのOSでは、リダイレクト(redirect)によって、標準入力ストリームと標準出力ストリームの接続先が変更可能。
//標準入力ストリームの変更:記号<に続いて入力元を与える。
//標準出力ストリームの変更:記号>に続いて出力先を与える。
//↓inputファイル、outputファイルに関する例
//例:コマンドライン> list0111
//例:コマンドライン> list0111 < input
//例:コマンドライン> list0111 < input > output
int main() {
int x; // 加減乗除する値
int y; // 加減乗除する値
cout << "xとyを加減乗除します。\n";
cout << "xとyの値:"; // xとyの値の入力を促す
cin >> x >> y; // xとyに整数値を読み込む
cout << "x + yは" << x + y << "です。\n"; // x + yの値を表示
cout << "x - yは" << x - y << "です。\n"; // x - yの値を表示
cout << "x * yは" << x * y << "です。\n"; // x * yの値を表示
cout << "x / yは" << x / y << "です。\n"; // x / yの値を表示(商)
cout << "x % yは" << x % y << "です。\n"; // x % yの値を表示(剰余)
}
ファイルストリーム
コード
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
// ファイルストリーム
//リダイレクトを使うことなくファイルを読み書きするときに利用する
//fstream:入力と出力を両方行えるファイルストリーム用のクラス
//ifstream:入力のみ
//ofstream:出力のみ
// ファイルのオープン
//ファイルを開く操作がオープン(open)
//コンストラクタによるオープン:ofstream fs("abc"); // ファイル"abc"をオープンしてfsと結びつける(引数"abc"の型はconst char*なので文字列リテラルをそのまま渡せる。)
//メンバ関数openによるオープン:ofstream fs; fs.open("abc");
// ファイルを正しくオープンできたかどうかの判定
//if(fs.is_open())、if(!fs.fail)、if(fs)、if(!fs)
// ファイルのクローズ
//ファイルとストリームとの結びつきを切り離すクローズ(close)。自動的にデストラクタが呼び出される。
//fs.close()
//下の例では、fisの生存期間が付きて、自動的にデストラクタが呼び出されてクローズされる。
// ファイルの存在の確認
//--- 名前がfilenameであるファイルが存在するかどうかを判定 ---//
bool file_exist(const char* filename) {
ifstream fis(filename); // 入力ストリームとしてオープン
return fis.is_open(); // オープンに成功したか?
}
int main() {
string file_name;
cout << "存在を確認したいファイルの名前:";
cin >> file_name;
cout << "そのファイルは存在"
<< (file_exist(file_name.c_str()) ? "します。\n" : "しません。\n");
}
#include <fstream>
#include <iostream>
using namespace std;
// ファイルストリームに対する読み書き
int main() {
ofstream fos("HELLO"); // "HELLO"を出力ストリームとしてオープン
if (!fos)
cerr << "\aファイル\"HELLO\"をオープンできません。\n";
else {
fos << "Hello!\n";
fos << "How are you?\n";
}
}
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
// ファイルストリームに対する読み書き
//●ストリーム関連のライブラリでは、基本型とC文字列に対する挿入子と抽出子は定義されているが、string型に対する挿入子や抽出子は定義されていないので、
//<string>ヘッダをインクルードする必要がある
//●string型に適用した抽出子が空白を呼び飛ばすことに注意
int main() {
ifstream fis("HELLO"); // "HELLO"を入力ストリームとしてオープン
if (!fis)
cerr << "\aファイル\"HELLO\"をオープンできません。\n";
else {
while (true) {
string text;
fis >> text; // ストリームから文字列を読み込んで
if (!fis) break;
cout << text << '\n'; // その文字列を画面に表示
}
}
}
Hello!
How
are
you?
#include <string>
#include <fstream>
#include <iostream>
using namespace std;
// ファイルストリームに対する読み書き
//各行を丸ごと文字列に読み込むようにする
int main() {
ifstream fis("HELLO"); // "HELLO"を入力ストリームとしてオープン
if (!fis)
cerr << "\aファイル\"HELLO\"をオープンできません。\n";
else {
while (true) {
string text;
getline(fis, text); // ストリームから1行を読み込んで
if (!fis) break;
cout << text << '\n'; // その文字列を画面に表示
}
}
}
//getline()は行末の改行文字は切り捨ててしまい、文字列に格納されないので、本プログラムではcoutによる出力時に改行文字を付加している。
Hello!
How are you?
#include <fstream>
#include <iostream>
using namespace std;
// 出力ストリームのオープンモード
//出力ストリームのオープンの際に、コンストラクタあるいはopen関数の第2引数にios_base::appを指定すると、追加モードと呼ばれる
//オープンモード(open mode)でファイルがオープンされる。このモードはファイルの既存の内容は消さずに、ファイルの終端位置からの書込みを行う。
//第2引数を省略した場合は、ios_base::truncが規定値として渡される。これは切捨てモード。
int main() {
ofstream fos("HELLO", ios_base::app); // "HELLO"を追加モードでオープン
if (!fos)
cerr << "\aファイル\"HELLO\"をオープンできません。\n";
else {
fos << "Fine, thanks.\n";
fos << "And you?\n";
}
}
#include <ctime>
#include <fstream>
#include <iostream>
using namespace std;
// 前回実行時の情報を取得
char fname[] = "lasttime.txt"; // ファイル名
//--- 前回の日付・時刻を取得・表示 ---//
void get_data() {
ifstream fis(fname);
if (fis.fail())
cout << "本プログラムを実行するのは初めてですね。\n";
else {
int year, month, day, h, m, s;
fis >> year >> month >> day >> h >> m >> s;
cout << "前回は" << year << "年" << month << "月" << day << "日"
<< h << "時" << m << "分" << s << "秒でした。\n";
}
}
//--- 今回の日付・時刻を書き込む ---//
void put_data() {
ofstream fos(fname);
if (fos.fail())
cout << "\aファイルをオープンできません。\n";
else {
time_t t = time(NULL);
struct tm local;
errno_t error;
error = localtime_s(&local, &t);
fos << local.tm_year + 1900 << ' ' << local.tm_mon + 1 << ' '
<< local.tm_mday << ' ' << local.tm_hour << ' '
<< local.tm_min << ' ' << local.tm_sec << '\n';
/*
time_t t = time(NULL);
struct tm* local = localtime(&t);
fos << local->tm_year + 1900 << ' ' << local->tm_mon + 1 << ' '
<< local->tm_mday << ' ' << local->tm_hour << ' '
<< local->tm_min << ' ' << local->tm_sec << '\n';
*/
}
}
int main() {
get_data(); // 前回の日付・時刻を取得・表示
put_data(); // 今回の日付・時刻を書き込む
}
ストリームライブラリ
コード
#include <iostream>
using namespace std;
// ストリームライブラリの構成
//入出力先として、ファイル以外に文字列もサポートされている。
// ios_baseクラス:ストリーム制御に必須となる基本的な型・定数・メンバ関数が定義されている
// fmtflags型:整数の入出力を何進数で行うのか、浮動小数点数の入出力をどういった形式で行うのかを表す型
// iostate型:入出力の状態を表す型
// openmode:出力ストリームのオープン時に、追加モードを指定できる
// seekdir:シークの方向を表す被列挙型
// 書式の設定
//下の例では、読込み文字数をcin.width(10)によって10文字に制限している。
int main() {
char str[10];
cout << "文字列を10文字未満で入力せよ:";
cin.width(10);
cin >> str;
cout << "str = "; cout.width(12); cout << str << '\n';
cout << "str = " << str << '\n';
}
文字列を10文字未満で入力せよ:klsajdfklfjakkfas
str = klsajdfkl
str = klsajdfkl
#include <iostream>
using namespace std;
//基数の設定
int main() {
int n, flag;
cout << "整数値:";
cin >> n;
cout << "基数(0…非表示/1…表示):";
cin >> flag;
if (flag) cout.setf(ios_base::showbase); // 基数表示を付加
cout.setf(ios_base::oct, ios_base::basefield); cout << n << '\n'; // 8進数
cout.setf(ios_base::dec, ios_base::basefield); cout << n << '\n'; // 10進数
cout.setf(ios_base::hex, ios_base::basefield); cout << n << '\n'; // 16進数
}
整数値:12
基数(0…非表示/1…表示):1
014
12
0xc
#include <iostream>
using namespace std;
//浮動小数点数の書式
//出力の形式はsetfで指定し、精度はprecisionで指定する。精度の既定値は6。
int main() {
int precision; // 精度
double x; // 表示する値
cout << "実数値:";
cin >> x;
cout << "精度:";
cin >> precision;
cout.precision(precision); // 精度は最後まで有効
cout.setf(ios_base::scientific, ios_base::floatfield);
cout << "科学形式:" << x << '\n';
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << "固定形式:" << x << '\n';
cout.setf(0, ios_base::floatfield);
cout << "通常表示:" << x << '\n';
}
実数値:3.141592
精度:4
科学形式:3.1416e+00
固定形式:3.1416
通常表示:3.142
#include <iostream>
using namespace std;
//flags関数とwidth関数とprecision関数が返却する"現在設定されている書式の情報"を利用すると、いったん書式を変更して出力したあとに、
//もとの書式を復元するといったことができる。
//--- double型配列の全要素を#######.##形式で各行に1要素ずつ表示 ---//
void put_ary(double ary[], int n) {
// 設定する書式(右揃え+10進数+固定小数点記法)
ios_base::fmtflags flags = ios_base::right | ios_base::dec | ios_base::fixed;
// 現在の書式と最小幅を保存
ios_base::fmtflags old_flags = cout.flags(); // 現在の書式
streamsize old_size = cout.width(); // 現在の最小幅
// 精度を設定するとともに現在の精度を保存
streamsize old_prec = cout.precision(2); // 精度は2桁
for (int i = 0; i < n; i++) {
cout.width(10); // 最小幅を10に設定
cout.flags(flags); // 書式をflagsに設定
cout << ary[i] << '\n';
}
cout.flags(old_flags); // フラグを戻す
cout.width(old_size); // 最小幅を戻す
cout.precision(old_prec); // 精度を戻す
}
int main() {
double a[] = { 1234.235, 5568.6205, 78999.312 };
cout << 0.00001234567890 << "\n\n"; // 通常表示
put_ary(a, sizeof(a) / sizeof(a[0]));
cout << '\n';
cout << 0.00001234567890 << '\n'; // 通常表示
}
1.23457e-05
1234.23
5568.62
78999.31
1.23457e-05
#include <iomanip>
#include <iostream>
using namespace std;
// 操作子
//書式の設定を操作子(manipulator)で行える
int main() {
cout << oct << 1234 << '\n'; // 8進数
cout << dec << 1234 << '\n'; // 10進数
cout << hex << 1234 << '\n'; // 16進数
cout << showbase;
cout << oct << 1234 << '\n'; // 8進数
cout << dec << 1234 << '\n'; // 10進数
cout << hex << 1234 << '\n'; // 16進数
cout << setw(10) << internal << "abc\n";
cout << setw(10) << left << "abc\n";
cout << setw(10) << right << "abc\n";
cout << setbase(10);
cout << setw(10) << internal << -123 << '\n';
cout << setw(10) << left << -123 << '\n';
cout << setw(10) << right << -123 << '\n';
cout << setfill('*');
cout << setw(10) << internal << -123 << '\n';
cout << setw(10) << left << -123 << '\n';
cout << setw(10) << right << -123 << '\n';
cout << setfill(' ');
cout << fixed << setw(10) << setprecision(2) << 123.5 << endl;
cout << scientific << setw(10) << setprecision(2) << 123.5 << endl;
}
//出力すべき文字はバッファに蓄えられており、バッファが満杯になったときなどに実際の出力が行われる。
//バッファ内にたまっている未出力の文字を強制的に出力(フラッシュ)するのがendlとfluch。
2322
1234
4d2
02322
1234
0x4d2
abc
abc
abc
- 123
-123
-123
-******123
-123******
******-123
123.50
1.24e+02
#include <iomanip>
#include <fstream>
#include <iostream>
using namespace std;
// テキストモードでの実数値の読み書き
int main() {
double pi = 3.14159265358979323846264338327950288;
ofstream fos("pi.txt");
if (!fos)
cout << "\aファイルをオープンできません。\n";
else {
fos << pi;
fos.close();
}
ifstream fis("pi.txt", ios_base::binary);
if (!fis)
cout << "\aファイルをオープンできません。\n";
else {
fis >> pi;
cout << "piの値は" << fixed << setprecision(20) << pi << "です。\n";
fis.close();
}
}
//挿入子<<が浮動小数点数を6桁の精度で出力するため、それ以降の桁のデータは失われる。
//これを解決するには、ファイルの入出力をバイナリモードに変更することによって可能。
//テキストモード(text mode):データを文字の並びで表現する。整数値357は3つの文字の並びで3バイトになる。バイト数は数値の桁数に依存する。
//バイナリモード(binary mode):データをビットの並びで表現する。int型の整数値の大きさは、必ずsizeof(int)になる。バイト数は数値の桁数に依存しない。
piの値は3.14158999999999988262です。
#include <iomanip>
#include <fstream>
#include <iostream>
using namespace std;
// バイナリモードでの実数値の読み書き
//ストリームのオープン時に、ios_base::binaryを指定することによって、ファイル"pi.bin"をバイナリモードでオープンする。
//ファイルへの書込みで利用しているwriteは、basic_ostream<>クラステンプレートで定義されたメンバ関数で、
//第1引数には書き出すデータの先頭番地へのポインタを受け取り、第2引数にはデータの大きさをバイト数で受け取る。
int main() {
double pi = 3.14159265358979323846;
ofstream fos("pi.bin", ios_base::binary);
if (!fos)
cout << "\aファイルをオープンできません。\n";
else {
fos.write(reinterpret_cast<char*>(&pi), sizeof(double));
fos.close();
}
ifstream fis("pi.bin", ios_base::binary);
if (!fis)
cout << "\aファイルをオープンできません。\n";
else {
fis.read(reinterpret_cast<char*>(&pi), sizeof(double));
cout << "piの値は" << fixed << setprecision(20) << pi << "です。\n";
fis.close();
}
}
// streamsize型
//streamsize型は、入出力操作によって転送された文字数や、入出力バッファの大きさを表すのに用いられる型。
piの値は3.14159265358979311600です。
#include <string>
#include <cctype>
#include <fstream>
#include <iomanip>
#include <iostream>
using namespace std;
// ファイルのダンプ
//バイナリモードで書き出したファイルの中身を文字コードで表示するプログラムを作る。
int main() {
string fname; // ファイル名
cout << "ファイル名:";
cin >> fname;
ifstream fs(fname.c_str(), ios_base::binary);
if (!fs)
cout << "\aファイルをオープンできません。\n";
else {
unsigned long count = 0;
while (true) {
int n;
unsigned char buf[16];
fs.read(reinterpret_cast<char*>(buf), 16);
if ((n = fs.gcount()) == 0) break;
cout << hex << setw(8) << setfill('0') << count << ' '; // アドレス
for (int i = 0; i < n; i++) // 16進数
cout << hex << setw(2) << setfill('0')
<< static_cast<unsigned>(buf[i]) << ' ';
if (n < 16)
for (int i = n; i < 16; i++) cout << " ";
for (int i = 0; i < n; i++) // 文字
cout << (isprint(buf[i]) ? static_cast<char>(buf[i]) : '.');
cout << '\n';
if (n < 16) break;
count += 16;
}
}
}
コード