Last Update 2023/12/15
概要
記憶域(storage)クラス指定子
型(type)修飾子
名前空間
属性構文
記憶域(storage)クラス指定子 static
sample.cpp
#include <iostream>
int n1; // グローバル変数を宣言
static int n2; // static指定子によりグローバル変数を宣言
void sampfunc()
{
int n3 = 20; // sampfunc()内で変数を宣言
static int n4; // sampfunc()内でstatic指定子により変数を宣言
n1++;
n2++;
n3++;
n4++;
std::cout << "sampfunc() --->" << std::endl;
std::cout << " n1 = " << n1 << std::endl;
std::cout << " n2 = " << n2 << std::endl;
std::cout << " n3 = " << n3 << std::endl;
std::cout << " n4 = " << n4 << std::endl;
std::cout << "<--- sampfunc()" << std::endl;
}
int main()
{
n1 = 10;
n2 = 10;
int n3 = 10; // main()内で変数を宣言
int n4 = 10; // main()内で変数を宣言
std::cout << "n1(1) = " << n1 << std::endl;
std::cout << "n2(1) = " << n2 << std::endl;
std::cout << "n3(1) = " << n3 << std::endl;
std::cout << "n4(1) = " << n4 << std::endl;
sampfunc();
std::cout << "n1(2) = " << n1 << std::endl;
std::cout << "n2(2) = " << n2 << std::endl;
std::cout << "n3(2) = " << n3 << std::endl;
std::cout << "n4(2) = " << n4 << std::endl;
sampfunc();
std::cout << "n1(3) = " << n1 << std::endl;
std::cout << "n2(3) = " << n2 << std::endl;
std::cout << "n3(3) = " << n3 << std::endl;
std::cout << "n4(3) = " << n4 << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
n1(1) = 10 <--- static宣言無しのグローバル変数
n2(1) = 10 <--- static宣言されたのグローバル変数
n3(1) = 10 <--- 関数内で宣言されたローカル変数
n4(1) = 10 <--- 関数内でstatic指定子により変数を宣言
sampfunc() --->
n1 = 11
n2 = 11
n3 = 21
n4 = 1
<--- sampfunc()
n1(2) = 11
n2(2) = 11
n3(2) = 10
n4(2) = 10
sampfunc() --->
n1 = 12
n2 = 12
n3 = 21
n4 = 2 <--- 宣言した関数内での静的変数
<--- sampfunc()
n1(3) = 12 <--- static宣言無しのグローバル変数も静的変数として機能
n2(3) = 12 <--- static宣言されたのグローバル変数
n3(3) = 10 <--- main()のローカル変数(呼び出し関数の影響なし)
n4(3) = 10 <--- main()のローカル変数(呼び出し関数の影響なし)
記憶域(storage)クラス指定子 extern
sample1.cpp
#include <iostream>
// 関数外でのextern指定子による宣言
extern int extern_n1;
void func()
{
// func関数内でのextern指定子による宣言
extern int extern_n2;
extern_n1++;
extern_n2 += 10;
std::cout << "func() --- extern_n1 = " << extern_n1
<< " : extern_n2 =" << extern_n2 << std::endl;
}
int main()
{
// main関数内でのextern指定子による宣言
extern int extern_n2;
for (int i=0; i<3; i++)
{
func();
extern_n2++;
std::cout << "extern_n2 = " << extern_n2 << std::endl;
}
}
sample2.cpp
// sample2.cpp - 変数初期化
int extern_n1 = 0;
int extern_n2 = 10;
実行結果
extern指定子による宣言は、場所を問わずに静的記憶域期間を持つ
extern指定子による宣言は、場所を問わずに静的記憶域期間を持つ
$ gcc -Wall sample1.cpp sample2.cpp -lstdc++
$ ./a.out
func() --- extern_n1 = 1 : extern_n2 =20
extern_n2 = 21
func() --- extern_n1 = 2 : extern_n2 =31
extern_n2 = 32
func() --- extern_n1 = 3 : extern_n2 =42
extern_n2 = 43
typedef
sample.cpp
#include <iostream>
// typedef名td_intをintの同義語として宣言
typedef int td_int;
// typedef名td_cをクラスSamp_cの同義語として宣言
typedef struct Samp_c
{
int n;
} td_class;
void func0(int , const char *);
void func1(int , const char *);
/* typedef名td_funcを戻り値の無い
引数(int, const char*)を伴う関数へのポインタの同義語として宣言 */
typedef void (*td_func)(int, const char*);
// 引数にtypedef名td_funcを使用する関数のプロトタイプ宣言
void func10(int , td_func);
int main()
{
td_int sample1 = 1;
td_class sample2;
sample2.n = 2;
std::cout << "sample1 = " << sample1 << std::endl;
std::cout << "sample2.n = " << sample2.n << std::endl;
func10(10, func0);
func10(10, func1);
}
void func0(int i, const char *s)
{
std::cout << "func0(" << i * 10 << ", \"" << s << "\"))" << std::endl;
}
void func1(int i, const char *s)
{
std::cout << "func1(" << i * 100 << ", \"" << s << "\"))" << std::endl;
}
void func10(int i, td_func func)
{
std::cout << "func10(" << i << ", ";
(*func)(i, "abc");
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
sample1 = 1
sample2.n = 2
func10(10, func0(100, "abc"))
func10(10, func1(1000, "abc"))
using宣言
sample.cpp
#include <iostream>
#include <iomanip>
// 基底クラス
class S0
{
long double d;
public:
void get1(long double x) { std::cout << x << std::endl; }
void get2(long double x) { std::cout << x << std::endl; }
};
struct Smpl1 : public S0
{
// 基底クラスのメンバをusing宣言
using S0::get1;
void get1(int n)
{
std::cout << n << std::endl;
get1(static_cast<long double>(n)); // 基底クラスの関数を呼び出し
// using宣言が無い場合はSmpl内で再帰呼び出し
}
};
// 基底クラスのアクセス制御がprivate
struct Smpl2 : private S0
{
// 基底クラスのメンバをusing宣言
using S0::get2;
};
namespace Smpl3
{
void get3(long double x) { std::cout << x << std::endl; }
}
namespace Smpl4
{
using Smpl3::get3; // Smpl3::get3()を参照
}
int main()
{
Smpl1 s1;
Smpl2 s2;
std::cout << std::scientific << std::setprecision(17);
s1.get1(123);
s2.get2(1.23456789012345678L);
Smpl4::get3(2.34567890123456789L); // Smpl3::get3()の呼び出し
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
123
1.23000000000000000e+02
1.23456789012345678e+00
2.34567890123456789e+00
usingディレクティブ
sample.cpp
#include <iostream>
int n = 1;
int x = 5;
namespace Nam1
{
int n = 10;
void f1() { std::cout << "Nam1::f1() : n = " << ++n << std::endl; }
void f2() { std::cout << "Nam1::f2() : x = " << ++x << std::endl; }
}
namespace Nam2
{
int n = 100;
void f3() { std::cout << "Nam2::f3() : n = " << ++n << std::endl; }
namespace Nam3
{
int n = 1000;
void f4() { std::cout << "Nam2::Nam3::f4() : n = " << ++n << std::endl; }
}
}
int main()
{
std::cout << "変数初期値" << std::endl;
std::cout << "n = " << n << std::endl;
std::cout << "x = " << x << std::endl;
std::cout << "Nam1::n = " << Nam1::n << std::endl;
std::cout << "Nam2::n = " << Nam2::n << std::endl;
std::cout << "Nam2::Nam3::n = " << Nam2::Nam3::n << std::endl;
std::cout << std::endl;
// usingディレクティブで指定された名前空間の名前が、
// 以降のスコープ内で使用可能となる
using namespace Nam1;
using namespace Nam2;
using namespace Nam2::Nam3;
f1();
f2();
f3();
f4();
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
変数初期値
n = 1
x = 5
Nam1::n = 10
Nam2::n = 100
Nam2::Nam3::n = 1000
Nam1::f1() : n = 11
Nam1::f2() : x = 6
Nam2::f3() : n = 101
Nam2::Nam3::f4() : n = 1001
usingによるエイリアス宣言(typedefとの比較)
sample.cpp
#include <iostream>
// typedefによる宣言
typedef int int_typedef;
typedef void (*f_typedef)(int);
// usingによるエイリアス宣言
using int_using = int;
using f_using = void (*)(int);
void f(int n) { std::cout << "f(n) = " << "f(" << n << ")" << std::endl; }
int main()
{
int_typedef int1 = 100;
int_using int2 = 101;
f_typedef f1 = &f;
f1(int1);
f_using f2 = &f;
f2(int2);
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
f(n) = f(100)
f(n) = f(101)
型(type)修飾子 const
sample1.cpp
型修飾子const無し
型修飾子const無し
#include <iostream>
int main()
{
int samp_int = 1;
std::cout << "samp_int = " << samp_int << std::endl;
samp_int = 2;
std::cout << "samp_int = " << samp_int << std::endl;
char samp_str1[] = "abc";
std::cout << "samp_str1 = " << samp_str1 << std::endl;
samp_str1[1] = 'd';
std::cout << "samp_str1 = " << samp_str1 << std::endl;
char samp_str2[] = "aaa";
char *samp_p1 = samp_str2;
samp_p1++;
*samp_p1 = 'e';
std::cout << "samp_p1 = " << samp_p1 << std::endl;
char *samp_p2 = samp_str2;
samp_p2++;
*samp_p2 = 'e';
std::cout << "samp_p2 = " << samp_p2 << std::endl;
}
実行結果
$ gcc -Wall sample1.cpp -lstdc++
$ ./a.out
samp_int = 1
samp_int = 2
samp_str1 = abc
samp_str1 = adc
samp_p1 = ea
samp_p2 = ea
sample2.cpp
#include <iostream>
int main()
{
const int samp_int = 1;
std::cout << "samp_int = " << samp_int << std::endl;
samp_int = 2;
std::cout << "samp_int = " << samp_int << std::endl;
const char samp_str1[] = "abc";
std::cout << "samp_str1 = " << samp_str1 << std::endl;
samp_str1[1] = 'd';
std::cout << "samp_str1 = " << samp_str1 << std::endl;
char samp_str2[] = "aaa";
const char *samp_p1 = samp_str2;
samp_p1++;
*samp_p1 = 'e';
std::cout << "samp_p1 = " << samp_p1 << std::endl;
char *const samp_p2 = samp_str2;
samp_p2++;
*samp_p2 = 'e';
std::cout << "samp_p2 = " << samp_p2 << std::endl;
}
実行結果
$ gcc -Wall sample2.cpp -lstdc++
sample2.cpp: In function ‘int main()’:
sample2.cpp:8:18: error: assignment of read-only variable ‘samp_int’
8 | samp_int = 2; <--- 変数 samp_int はconstにより変更不可
| ~~~~~~~~~^~~
sample2.cpp:14:22: error: assignment of read-only location ‘samp_str1[1]’
14 | samp_str1[1] = 'd'; <--- 変数 samp_str1 はconstにより変更不可
| ~~~~~~~~~~~~~^~~~~
sample2.cpp:21:18: error: assignment of read-only location ‘* samp_p1’
21 | *samp_p1 = 'e'; <--- ポインタ samp_p1 が指すオブジェクトはconstにより変更不可
| ~~~~~~~~~^~~~~
sample2.cpp:25:9: error: increment of read-only variable ‘samp_p2’
25 | samp_p2++; <--- ポインタ samp_p2 そのものはconstにより変更不可
| ^~~~~~~
型指定子
sample.cpp
#include <iostream>
#include <typeinfo>
int main()
{
std::cout << "各種型指定子および実装定義型名" << std::endl;
std::cout << std::endl;
std::cout << "char : " << typeid(char).name() << std::endl;
std::cout << "unsigned_char : " << typeid(unsigned char).name() << std::endl;
std::cout << "signed_char : " << typeid(signed char).name() << std::endl;
std::cout << "char16_t : " << typeid(char16_t).name() << std::endl;
std::cout << "char32_t : " << typeid(char32_t).name() << std::endl;
std::cout << "bool : " << typeid(bool).name() << std::endl;
std::cout << "unsigned : " << typeid(unsigned).name() << std::endl;
std::cout << "unsigned int : " << typeid(unsigned int).name() << std::endl;
std::cout << "signed : " << typeid(signed).name() << std::endl;
std::cout << "signed int : " << typeid(signed int).name() << std::endl;
std::cout << "int : " << typeid(int).name() << std::endl;
std::cout << "unsigned short int : " << typeid(unsigned short int).name() << std::endl;
std::cout << "unsigned short : " << typeid(unsigned short).name() << std::endl;
std::cout << "unsigned long int : " << typeid(unsigned long int).name() << std::endl;
std::cout << "unsigned long : " << typeid(unsigned long).name() << std::endl;
std::cout << "unsigned long long int : " << typeid(unsigned long long int).name() << std::endl;
std::cout << "unsigned long long : " << typeid(unsigned long long).name() << std::endl;
std::cout << "signed long int : " << typeid(signed long int).name() << std::endl;
std::cout << "signed long : " << typeid(signed long).name() << std::endl;
std::cout << "signed long long int : " << typeid(signed long long int).name() << std::endl;
std::cout << "signed long long : " << typeid(signed long long).name() << std::endl;
std::cout << "long long int : " << typeid(long long int).name() << std::endl;
std::cout << "long long : " << typeid(long long).name() << std::endl;
std::cout << "long int : " << typeid(long int).name() << std::endl;
std::cout << "long : " << typeid(long).name() << std::endl;
std::cout << "signed short int : " << typeid(signed short int).name() << std::endl;
std::cout << "signed short : " << typeid(signed short).name() << std::endl;
std::cout << "short int : " << typeid(short int).name() << std::endl;
std::cout << "short : " << typeid(short).name() << std::endl;
std::cout << "wchar_t : " << typeid(wchar_t).name() << std::endl;
std::cout << "float : " << typeid(float).name() << std::endl;
std::cout << "double : " << typeid(double).name() << std::endl;
std::cout << "long double : " << typeid(long double).name() << std::endl;
std::cout << "void : " << typeid(void).name() << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
各種型指定子および実装定義型名
char : c
unsigned_char : h
signed_char : a
char16_t : Ds
char32_t : Di
bool : b
unsigned : j
unsigned int : j
signed : i
signed int : i
int : i
unsigned short int : t
unsigned short : t
unsigned long int : m
unsigned long : m
unsigned long long int : y
unsigned long long : y
signed long int : l
signed long : l
signed long long int : x
signed long long : x
long long int : x
long long : x
long int : l
long : l
signed short int : s
signed short : s
short int : s
short : s
wchar_t : w
float : f
double : d
long double : e
void : v
enum(列挙型)
sample.cpp
#include <iostream>
// 識別子・初期化子無し
enum { ABC, DEF, GHI };
// 識別子無し・初期化子有り・リスト終端コンマ
enum { JKL = 10, MNO, };
// 識別子有り
enum en1 { PQR = 20, STU };
// スコープ付き列挙(class)
enum class E1 : int { ABC = 100, DEF, GHI };
// スコープ付き列挙(struct)
enum struct E2 : int { ABC = 200, DEF, GHI };
struct S
{
// クラススコープで宣言された列挙
enum es {ABCD = 300, EFGI};
// クラススコープ列挙のクラスメンバ
es x;
S() : x(EFGI) {}
};
void f()
{
// f()内スコープ
enum { f_ABC = 40, f_DEF };
int samp_int1 = DEF;
int samp_int2 = JKL;
int samp_int3 = f_DEF;
en1 samp_en1 = STU;
std::cout << " --- samp_int1 = " << samp_int1 << std::endl;
std::cout << " --- samp_int2 = " << samp_int2 << std::endl;
std::cout << " --- samp_int3 = " << samp_int3 << std::endl;
std::cout << " --- samp_en1 = " << samp_en1 << std::endl;
}
int main()
{
std::cout << "enum(列挙型)" << std::endl;
std::cout << std::endl;
// main()内スコープ
enum { m_ABC = 30, m_DEF };
// 識別子・初期化子無し列挙を整数変数に代入
int samp_int1 = DEF;
int samp_int2 = JKL;
int samp_int3 = m_DEF;
// 識別子の有る列挙型オブジェクト
en1 samp_en1 = STU;
// スコープ付き列挙型のオブジェクト
E1 samp_e1 = E1::DEF;
E2 samp_e2 = E2::DEF;
// クラススコープの列挙型をもつクラスオブジェクト
S s;
// 列挙子を格納した整数の出力
std::cout << "samp_int1 = " << samp_int1 << std::endl;
std::cout << "samp_int2 = " << samp_int2 << std::endl;
std::cout << "samp_int3 = " << samp_int3 << std::endl;
std::cout << "samp_en1 = " << samp_en1 << std::endl;
// スコープ付き列挙型オブジェクトの出力
// スコープ付きは明示的型変換が必要
std::cout << "samp_e1(class) = " << static_cast<int>(samp_e1) << std::endl;
std::cout << "samp_e2(struct) = " << static_cast<int>(samp_e2) << std::endl;
// クラススコープ列挙のクラスメンバの出力
std::cout << "s.x = " << s.x << std::endl;
// クラス内の列挙子の参照
std::cout << "s.ABCD = " << s.ABCD << std::endl; // クラスオブジェクトとドット演算子
std::cout << "S::EFGI = " << S::EFGI << std::endl; // クラス名とスコープ解決演算子
std::cout << std::endl;
f();
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
enum(列挙型)
samp_int1 = 1
samp_int2 = 10
samp_int3 = 31
samp_en1 = 21
samp_e1(class) = 101
samp_e2(struct) = 201
s.x = 301
s.ABCD = 300
S::EFGI = 301
--- samp_int1 = 1
--- samp_int2 = 10
--- samp_int3 = 41
--- samp_en1 = 21
名前空間使用例
sample.cpp
#include <iostream>
int n = 1;
int x = 5;
namespace Nam1
{
int n = 10;
// Nam1内で宣言されたnを出力する関数
void f1() { std::cout << "Nam1::f1() : n = " << ++n << std::endl; }
// グローバル変数として宣言されたxを出力する関数
void f2() { std::cout << "Nam1::f2() : x = " << ++x << std::endl; }
}
namespace Nam2
{
int n = 100;
// Nam2内で宣言されたnを出力する関数
void f1() { std::cout << "Nam2::f1() : n = " << ++n << std::endl; }
namespace Nam3
{
// Nam2内で宣言されたnを出力する関数
void f2() { std::cout << "Nam2::Nam3::f2() : n = " << ++n << std::endl; }
int n = 1000;
// Nam3内で宣言されたnを出力する関数
void f3() { std::cout << "Nam2::Nam3::f3() : n = " << ++n << std::endl; }
// Nam3内で宣言されたnを出力する関数(宣言のみ)
void f5();
}
// Nam1内で宣言されたnを出力する関数
// スコープ解決演算子使用
void f4() { std::cout << "Nam2::f4() : Nam1::n = " << ++Nam1::n << std::endl; }
// Nam3内で宣言されたf5の関数定義
void Nam3::f5() { std::cout << "Nam2::Nam3::f5() : n = " << ++n << std::endl; }
}
// グローバル変数として宣言されたnを出力する関数
void f1() { std::cout << "f1() : n = " << n << std::endl; }
// Nam2内で宣言されたnを出力する関数
// スコープ解決演算子使用
void f2() { std::cout << "f2() : Nam2::n = " << ++Nam2::n << std::endl; }
int main()
{
std::cout << "変数初期値" << std::endl;
std::cout << "n = " << n << std::endl;
std::cout << "x = " << x << std::endl;
std::cout << "Nam1::n = " << Nam1::n << std::endl;
std::cout << "Nam2::n = " << Nam2::n << std::endl;
std::cout << "Nam2::Nam3::n = " << Nam2::Nam3::n << std::endl;
std::cout << std::endl;
f1();
f2();
Nam1::f1();
Nam1::f2();
Nam2::f1();
Nam2::Nam3::f2();
Nam2::Nam3::f3();
Nam2::f4();
Nam2::Nam3::f5();
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
変数初期値
n = 1
x = 5
Nam1::n = 10
Nam2::n = 100
Nam2::Nam3::n = 1000
f1() : n = 1
f2() : Nam2::n = 101
Nam1::f1() : n = 11
Nam1::f2() : x = 6
Nam2::f1() : n = 102
Nam2::Nam3::f2() : n = 103
Nam2::Nam3::f3() : n = 1001
Nam2::f4() : Nam1::n = 12
Nam2::Nam3::f5() : n = 1002
名前空間エイリアス
sample.cpp
「名前空間使用例」のsample.cppをエイリアスを使用して書き換え
「名前空間使用例」のsample.cppをエイリアスを使用して書き換え
#include <iostream>
int n = 1;
int x = 5;
namespace Nam1
{
int n = 10;
void f1();
void f2();
}
namespace Nam2
{
int n = 100;
void f1();
namespace Nam3
{
// 親の名前空間(Nam2)で宣言されたnを出力する関数
// 名前空間エイリアスを使用してスコープ外で定義した場合は、
// Nam3内のnが使用されるため、この位置で宣言・定義
void f2() { std::cout << "C::f2() : n = " << ++n << std::endl; }
// f2()の宣言・定義後に宣言された変数n
int n = 1000;
void f3();
void f5();
}
void f4();
}
// グローバル変数として宣言されたnを出力する関数
void f1() { std::cout << "f1() : n = " << n << std::endl; }
// 名前空間エイリアスの宣言
namespace A = Nam1;
namespace B = Nam2;
namespace C = Nam2::Nam3;
// B内で宣言されたnを出力する関数
// スコープ解決演算子使用
void f2() { std::cout << "f2() : B::n = " << ++B::n << std::endl; }
// A内で宣言されたnを出力する関数
void A::f1() { std::cout << "A::f1() : n = " << ++n << std::endl; }
// グローバル変数として宣言されたxを出力する関数
void A::f2() { std::cout << "A::f2() : x = " << ++x << std::endl; }
// B内で宣言されたnを出力する関数
void B::f1() { std::cout << "B::f1() : n = " << ++n << std::endl; }
// C内で宣言されたnを出力する関数
void C::f3() { std::cout << "C::f3() : n = " << ++n << std::endl; }
// C内で宣言されたf5の関数定義
void C::f5() { std::cout << "C::f5() : n = " << ++n << std::endl; }
// Nam1内で宣言されたnを出力する関数
void B::f4() { std::cout << "B::f4() : A::n = " << ++A::n << std::endl; }
int main()
{
std::cout << "変数初期値" << std::endl;
std::cout << "n = " << n << std::endl;
std::cout << "x = " << x << std::endl;
std::cout << "A::n = " << A::n << std::endl;
std::cout << "B::n = " << B::n << std::endl;
std::cout << "C::n = " << C::n << std::endl;
std::cout << std::endl;
f1();
f2();
A::f1();
A::f2();
B::f1();
C::f2();
C::f3();
B::f4();
C::f5();
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
変数初期値
n = 1
x = 5
A::n = 10
B::n = 100
C::n = 1000
f1() : n = 1
f2() : B::n = 101
A::f1() : n = 11
A::f2() : x = 6
B::f1() : n = 102
C::f2() : n = 103
C::f3() : n = 1001
B::f4() : A::n = 12
C::f5() : n = 1002
名前なし名前空間
sample1.cpp
#include <iostream>
namespace
{
// 名前なし名前空間
int n = 100;
int m = 10;
void f1() { std::cout << "sample1.cpp --- f1() --- n = " << n << std::endl; }
}
extern void f2();
int main()
{
std::cout << "sample1.cpp --- m = " << m << std::endl;
std::cout << "sample1.cpp --- n = " << n << std::endl;
f1();
std::cout << std::endl;
f2();
}
sample2.cpp
#include <iostream>
namespace
{
// 名前なし名前空間
int n = 200;
int m = 20;
void f1() { std::cout << "sample2.cpp --- f1() --- n = " << n << std::endl; }
}
void f2()
{
std::cout << "sample2.cpp --- f2() --- m = " << m << std::endl;
std::cout << "sample2.cpp --- f2() --- n = " << n << std::endl;
f1();
}
実行結果
$ gcc -Wall sample1.cpp sample2.cpp -lstdc++
$ ./a.out
sample1.cpp --- m = 10
sample1.cpp --- n = 100
sample1.cpp --- f1() --- n = 100
sample2.cpp --- f2() --- m = 20
sample2.cpp --- f2() --- n = 200
sample2.cpp --- f1() --- n = 200
名前なし名前空間を使用せずに sample1.cpp と sample2.cpp をコンパイル
sample1.cpp(変更後)
#include <iostream>
int n = 100;
int m = 10;
void f1() { std::cout << "sample1.cpp --- f1() --- n = " << n << std::endl; }
extern void f2();
int main()
{
std::cout << "sample1.cpp --- m = " << m << std::endl;
std::cout << "sample1.cpp --- n = " << n << std::endl;
f1();
std::cout << std::endl;
f2();
}
sample2.cpp(変更後)
#include <iostream>
int n = 200;
int m = 20;
void f1() { std::cout << "sample2.cpp --- f1() --- n = " << n << std::endl; }
void f2()
{
std::cout << "sample2.cpp --- f2() --- m = " << m << std::endl;
std::cout << "sample2.cpp --- f2() --- n = " << n << std::endl;
f1();
}
実行結果
$ gcc -Wall sample1.cpp sample2.cpp -lstdc++
/usr/bin/ld: /tmp/ccIo7WUU.o:(.data+0x0): multiple definition of `n'; /tmp/ccyTh54v.o:(.data+0x0): first defined here
/usr/bin/ld: /tmp/ccIo7WUU.o:(.data+0x4): multiple definition of `m'; /tmp/ccyTh54v.o:(.data+0x4): first defined here
/usr/bin/ld: /tmp/ccIo7WUU.o: in function `f1()':
sample2.cpp:(.text+0x0): multiple definition of `f1()'; /tmp/ccyTh54v.o:sample1.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
複数定義に関するエラー発生
inline キーワード
sample.cpp
#include <iostream>
namespace M
{
int m1 = 0;
int n1 = 0;
int m2 = 0;
int n2 = 0;
}
inline namespace N
{
// inline により N の外側へ
int m1 = 10;
int n1 = 100;
namespace N1
{
int n2 = 200;
inline namespace N12
{
// inline により N12 の外側へ
int m2 = 20;
}
}
inline namespace N2
{
// inline により N2 の外側へ ---> N の外側へ
int m2 = 40;
namespace N21
{
int n2 = 400;
}
inline namespace N22
{
// inline により N22 の外側へ ---> N2 の外側へ ---> N の外側へ
int n2 = 500;
}
}
}
int main()
{
std::cout << "m1 = " << m1 << std::endl;
std::cout << "n1 = " << n1 << std::endl;
std::cout << "m2 = " << m2 << std::endl;
std::cout << "n2 = " << n2 << std::endl;
std::cout << std::endl;
std::cout << "M::m1 = " << M::m1 << std::endl;
std::cout << "M::n1 = " << M::n1 << std::endl;
std::cout << "M::m2 = " << M::m2 << std::endl;
std::cout << "M::n2 = " << M::n2 << std::endl;
std::cout << std::endl;
std::cout << "N::m1 = " << N::m1 << " --- inline" << std::endl;
std::cout << "N::n1 = " << N::n1 << " --- inline" << std::endl;
std::cout << "N::m2 = " << N::m2 << std::endl;
std::cout << "N::n2 = " << N::n2 << std::endl;
std::cout << std::endl;
std::cout << "N1::m2 = " << N1::m2 << std::endl;
std::cout << "N1::n2 = " << N1::n2 << std::endl;
std::cout << "N1::N12::m2 = " << N1::N12::m2 << std::endl;
std::cout << std::endl;
std::cout << "N2::m2 = " << N2::m2 << " --- inline" << std::endl;
std::cout << "N2::n2 = " << N2::n2 << std::endl;
std::cout << "N21::n2 = " << N21::n2 << std::endl;
std::cout << "N22::n2 = " << N22::n2 << std::endl;
std::cout << std::endl;
std::cout << "N2::N21::n2 = " << N2::N21::n2 << std::endl;
std::cout << "N2::N22::n2 = " << N2::N22::n2 << " --- inline" << std::endl;
std::cout << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
m1 = 10
n1 = 100
m2 = 40
n2 = 500
M::m1 = 0
M::n1 = 0
M::m2 = 0
M::n2 = 0
N::m1 = 10 --- inline
N::n1 = 100 --- inline
N::m2 = 40
N::n2 = 500
N1::m2 = 20 <--- inline の効果は N1 内まで
N1::n2 = 200
N1::N12::m2 = 20
N2::m2 = 40 --- inline
N2::n2 = 500
N21::n2 = 400
N22::n2 = 500
N2::N21::n2 = 400
N2::N22::n2 = 500 --- inline
言語リンケージに関する挙動確認
sample.cpp
#include <iostream>
int main()
{
// C言語で記述された関数
puts("sample --- puts()");
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
sample --- puts()
$ gcc -Wall sample.cpp -lstdc++ -E > sample_e1.txt
上記の実行結果で生成された sample_e1.txt
1 # 0 "sample.cpp"
2 # 0 "<built-in>"
3 # 0 "<command-line>"
︙
22439 # 1 "/usr/include/bits/libc-header-start.h" 1 3 4
22440 # 28 "/usr/include/stdio.h" 2 3 4
22441
22442 extern "C" {
22443
22444
22445
22446 # 1 "/usr/lib/gcc/x86_64-pc-linux-gnu/12.2.0/include/stddef.h" 1 3 4
22447 # 34 "/usr/include/stdio.h" 2 3 4
︙
23095 extern int __uflow (FILE *);
23096 extern int __overflow (FILE *, int);
23097 # 909 "/usr/include/stdio.h" 3 4
23098 }
23099 # 43 "/usr/include/c++/12.2.0/cstdio" 2 3
︙
33735 # 3 "sample.cpp"
33736 int main()
33737 {
33738
33739 puts("sample --- puts()");
33740 }
生成結果のファイルをコピー
$ cp sample_e1.txt sample_e2.txt
sample_e2.txt の以下の言語リンケージを削除
22442 extern "C" {
↓
22442 // extern "C" {
23098 }
↓
23098 // }
sample_e1.txt と sample_e2.txt をそれぞれコンパイル
$ gcc -Wall -x c++ sample_e1.txt -lstdc++ <--- エラー無しでコンパイル
$ ./a.out
sample --- puts()
$ gcc -Wall -x c++ sample_e2.txt -lstdc++
/usr/bin/ld: /tmp/cc2AuHFW.o: in function `main':
sample_e2.txt:(.text+0xa): undefined reference to `puts(char const*)'
collect2: error: ld returned 1 exit status <--- extern "C"を削除した場合(デフォルト)は関数への未定義参照エラーが発生
sample_e2.txt の以下の箇所に言語リンケージを追加
22942 extern int puts (const char *__s);
↓
22942 extern "C" int puts (const char *__s);
sample_e2.txt をコンパイル
$ gcc -Wall -x c++ sample_e2.txt -lstdc++ <--- エラー無しでコンパイル
$ ./a.out
sample --- puts()
sample_e2.txt の以下の言語リンケージを "C++" に変更
22942 extern "C" int puts (const char *__s);
↓
22942 extern "C++" int puts (const char *__s);
sample_e2.txt をコンパイル
$ gcc -Wall -x c++ sample_e2.txt -lstdc++
/usr/bin/ld: /tmp/ccfhyNPI.o: in function `main':
sample_e2.txt:(.text+0xa): undefined reference to `puts(char const*)'
collect2: error: ld returned 1 exit status <--- "C++"とした場合はデフォルトと同じく関数への未定義参照エラーが発生
アライメント指定子 alignas
sample.cpp
#include <iostream>
int main()
{
std::cout << "1バイト境界でアラインメント" << std::endl;
alignas(1) unsigned char buf11[] = { 1, 2, 3 };
alignas(1) unsigned char buf12[] = { 1, 2 };
alignas(1) unsigned char buf13[] = { 1, 2, 3 };
alignas(1) unsigned char buf14[] = { 1 };
std::cout << &buf11 << std::endl;
std::cout << &buf12 << std::endl;
std::cout << &buf13 << std::endl;
std::cout << &buf14 << std::endl;
std::cout << std::endl;
std::cout << "4バイト境界でアラインメント" << std::endl;
alignas(4) unsigned char buf21[] = { 1, 2, 3 };
alignas(4) unsigned char buf22[] = { 1, 2 };
alignas(4) unsigned char buf23[] = { 1, 2, 3 };
alignas(4) unsigned char buf24[] = { 1 };
std::cout << &buf21 << std::endl;
std::cout << &buf22 << std::endl;
std::cout << &buf23 << std::endl;
std::cout << &buf24 << std::endl;
std::cout << std::endl;
std::cout << "long int型のアラインメントを適用" << std::endl;
alignas(long int) unsigned char buf31[] = { 1, 2, 3 };
alignas(long int) unsigned char buf32[] = { 1, 2 };
alignas(long int) unsigned char buf33[] = { 1, 2, 3 };
alignas(long int) unsigned char buf34[] = { 1 };
std::cout << &buf31 << std::endl;
std::cout << &buf32 << std::endl;
std::cout << &buf33 << std::endl;
std::cout << &buf34 << std::endl;
std::cout << std::endl;
std::cout << "16バイト境界でアラインメント" << std::endl;
alignas(16) unsigned char buf41[] = { 1, 2, 3 };
alignas(16) unsigned char buf42[] = { 1, 2 };
alignas(16) unsigned char buf43[] = { 1, 2, 3 };
alignas(16) unsigned char buf44[] = { 1 };
std::cout << &buf41 << std::endl;
std::cout << &buf42 << std::endl;
std::cout << &buf43 << std::endl;
std::cout << &buf44 << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
1バイト境界でアラインメント
0x7fffcd04f8dd
0x7fffcd04f8db
0x7fffcd04f8d8
0x7fffcd04f8d7
4バイト境界でアラインメント
0x7fffcd04f8d4
0x7fffcd04f8d0
0x7fffcd04f8cc
0x7fffcd04f8c8
long int型のアラインメントを適用
0x7fffcd04f8c0
0x7fffcd04f8b8
0x7fffcd04f8b0
0x7fffcd04f8a8
16バイト境界でアラインメント
0x7fffcd04f8a0
0x7fffcd04f890
0x7fffcd04f880
0x7fffcd04f870
deprecated 属性
sample.cpp
#include <iostream>
[[deprecated("非推奨の根拠を説明する文字列リテラル")]] void viewstr(char *s)
{
printf("<cstdio>(<iostream>) - printf() ---> %s\n", s);
}
int main()
{
char s[] = "sample";
viewstr(s);
std::cout << "<iostream> - std::cout ---> " << s << std::endl;
std::cout << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++ <--- コンパイル時に「非推奨」の警告が発生
sample.cpp: In function ‘int main()’:
sample.cpp:12:16: warning: ‘void viewstr(char*)’ is deprecated: 非推奨の根拠を説明する文字列リテラル [-Wdeprecated-declarations]
12 | viewstr(s);
| ~~~~~~~^~~
sample.cpp:3:61: note: declared here
3 | [[deprecated("非推奨の根拠を説明する文字列リテラル")]] void viewstr(char *s)
| ^~~~~~~
$ ./a.out <--- エラーの発生は無く実行ファイルは生成される
<cstdio>(<iostream>) - printf() ---> sample
<iostream> - std::cout ---> sample
fallthrough 属性
sample.cpp
break文によりフォールスルーの発生は無し
break文によりフォールスルーの発生は無し
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
if (argc > 1)
{
std::string sarg = argv[1];
char& c = sarg[0];
if ((c >= '0') && (c <='4'))
{
switch (c - '0')
{
case 0:
std::cout << "a --- ";
break;
case 1:
std::cout << "b --- ";
break;
case 2:
std::cout << "c --- ";
break;
case 3:
std::cout << "d --- ";
break;
case 4:
std::cout << "e --- ";
break;
default:
std::cout << c;
}
}
std::cout << std::endl;
}
else
{
std::cout << "引数無し" << std::endl;
}
}
実行結果
$ gcc -Wall sample.cpp -lstdc++ <--- フォールスルーに関するオプション無しでコンパイル
$ ./a.out 0
a ---
$ gcc -Wall -Wimplicit-fallthrough=5 sample.cpp -lstdc++ <--- フォールスルーに関するオプション(属性のみが警告を抑制)でコンパイル
$ ./a.out 0
a ---
sample.cpp の全てのbreak文をコメントアウト
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
if (argc > 1)
{
std::string sarg = argv[1];
char& c = sarg[0];
if ((c >= '0') && (c <='4'))
{
switch (c - '0')
{
case 0:
std::cout << "a --- ";
// break;
case 1:
std::cout << "b --- ";
// break;
case 2:
std::cout << "c --- ";
// break;
case 3:
std::cout << "d --- ";
// break;
case 4:
std::cout << "e --- ";
// break;
default:
std::cout << c;
}
}
std::cout << std::endl;
}
else
{
std::cout << "引数無し" << std::endl;
}
}
実行結果
$ gcc -Wall sample.cpp -lstdc++ <--- フォールスルーに関するオプション無しでコンパイル
$ ./a.out 0
a --- b --- c --- d --- e --- 0
$ gcc -Wall -Wimplicit-fallthrough=5 sample.cpp -lstdc++ <--- フォールスルーに関するオプション(属性のみが警告を抑制)でコンパイル
sample.cpp: In function ‘int main(int, char**)’:
sample.cpp:16:46: warning: this statement may fall through [-Wimplicit-fallthrough=] <--- 以下、フォールスルー発生に対する警告
16 | std::cout << "a --- ";
| ^~~~~~~~
sample.cpp:19:25: note: here
19 | case 1:
| ^~~~
sample.cpp:20:46: warning: this statement may fall through [-Wimplicit-fallthrough=]
20 | std::cout << "b --- ";
| ^~~~~~~~
sample.cpp:23:25: note: here
23 | case 2:
| ^~~~
sample.cpp:24:46: warning: this statement may fall through [-Wimplicit-fallthrough=]
24 | std::cout << "c --- ";
| ^~~~~~~~
sample.cpp:27:25: note: here
27 | case 3:
| ^~~~
sample.cpp:28:46: warning: this statement may fall through [-Wimplicit-fallthrough=]
28 | std::cout << "d --- ";
| ^~~~~~~~
sample.cpp:31:25: note: here
31 | case 4:
| ^~~~
sample.cpp:32:46: warning: this statement may fall through [-Wimplicit-fallthrough=]
32 | std::cout << "e --- ";
| ^~~~~~~~
sample.cpp:35:25: note: here
35 | default:
| ^~~~~~~
$ ./a.out 0
a --- b --- c --- d --- e --- 0
コメントアウトしたbreakの位置にfallthrough 属性を追加
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
if (argc > 1)
{
std::string sarg = argv[1];
char& c = sarg[0];
if ((c >= '0') && (c <='4'))
{
switch (c - '0')
{
case 0:
std::cout << "a --- ";
[[fallthrough]];
// break;
case 1:
std::cout << "b --- ";
[[fallthrough]];
// break;
case 2:
std::cout << "c --- ";
[[fallthrough]];
// break;
case 3:
std::cout << "d --- ";
[[fallthrough]];
// break;
case 4:
std::cout << "e --- ";
[[fallthrough]];
// break;
default:
std::cout << c;
}
}
std::cout << std::endl;
}
else
{
std::cout << "引数無し" << std::endl;
}
}
実行結果
$ gcc -Wall -Wimplicit-fallthrough=5 sample.cpp -lstdc++ <--- 警告無しでコンパイル終了
$ ./a.out 0
a --- b --- c --- d --- e --- 0
maybe_unused 属性
sample.cpp
#include <iostream>
int main()
{
// 使用される変数
int i0 = 1;
// 未使用変数
int i1 = 2;
// maybe_unused 属性のある未使用変数
[[maybe_unused]] int i2 = 3;
std::cout << "i0 = " << i0 << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp: In function ‘int main()’:
sample.cpp:9:13: warning: unused variable ‘i1’ [-Wunused-variable] <--- 未使用・[[maybe_unused]]無しの場合、警告発生
9 | int i1 = 2;
| ^~
$ ./a.out
i0 = 1
nodiscard 属性
sample.cpp
#include <iostream>
// nodiscard 属性の指定無し
int f0()
{
return 0;
}
// nodiscard 属性の指定有り
[[nodiscard]] int f1()
{
return 0;
}
int main()
{
// 関数の戻り値を破棄
f0();
f1();
// 関数の戻り値を使用
std::cout << "f0() = " << f0() << std::endl;
std::cout << "f1() = " << f1() << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp: In function ‘int main()’: ↓↓↓ 属性指定・戻り値破棄の場合、警告発生
sample.cpp:19:11: warning: ignoring return value of ‘int f1()’, declared with attribute ‘nodiscard’ [-Wunused-result]
19 | f1();
| ~~^~
sample.cpp:10:19: note: declared here
10 | [[nodiscard]] int f1()
| ^~
noreturn 属性
sample.cpp
#include <iostream>
// ********** 返らない関数 **********
// noreturn 属性の指定無し
void e0()
{
std::exit(0);
}
// noreturn 属性の指定有り
[[noreturn]] void e1()
{
std::exit(0);
}
// ************ 空の関数 ************
// noreturn 属性の指定無し
void e2()
{
}
// noreturn 属性の指定有り
[[noreturn]] void e3()
{
}
// ******** 戻り値の有る関数 ********
// noreturn 属性の指定無し
int e4()
{
return 0;
}
// noreturn 属性の指定有り
[[noreturn]] int e5()
{
return 0;
}
int main()
{
e0();
e1();
e2();
e3();
e4();
e5();
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
sample.cpp: In function ‘int e5()’: ↓↓↓ noreturn属性を指定した関数にreturn文がある事に対する警告
sample.cpp:40:16: warning: function declared ‘noreturn’ has a ‘return’ statement
40 | return 0;
| ^
sample.cpp: In function ‘void e3()’:
sample.cpp:27:1: warning: ‘noreturn’ function does return <--- noreturn属性を指定した関数が「戻る」事に対する警告
27 | }
| ^
sample.cpp: In function ‘int e5()’:
sample.cpp:40:16: warning: ‘noreturn’ function does return <--- noreturn属性を指定した関数が「戻る」事に対する警告
40 | return 0;
| ^
ポインタ
sample.cpp
#include <iostream>
int main()
{
int i0 = 0; // int型
int i1 = 1; // int型
int a0[5] = { 5, 6, 7 }; // int型配列
const int i2 = 8; // int型(const)
// int型へのポインタ
int *pi0 = &i0;
// int型(配列)へのポインタ
int *pa0 = &a0[0];
int *pa1 = &a0[2];
// int型(const)へのポインタ
// ポインタが指すオブジェクトの値(*pi2)の変更は不可
const int *pi2 = &i2;
// int型へのポインタ定数
// ポインタが指すオブジェクト(pi3)の変更は不可
int *const pi3 = &i0;
// int型へのポインタのポインタ
int **pp0 = &pa0;
// int型へのポインタ定数へのポインタ
// 「ポインタのポインタ」が指す「ポインタのオブジェクト(*pp1)」の変更は不可
int i3 = 9, *const pi4 = &i3, *const *pp1 = &pi4;
std::cout << "i0 = " << i0 << " (" << &i0 << ")" << std::endl;
std::cout << "i1 = " << i1 << " (" << &i1 << ")" << std::endl;
std::cout << "a0[0] = " << a0[0] << " (" << &a0[0] << ")" << std::endl;
std::cout << "a0[1] = " << a0[1] << " (" << &a0[1] << ")" << std::endl;
std::cout << "a0[2] = " << a0[2] << " (" << &a0[2] << ")" << std::endl;
std::cout << "i3 = " << i3 << " (" << &i3 << ")" << std::endl;
std::cout << std::endl;
std::cout << "*pi0 = " << *pi0 << " (pi0 : " << pi0 << ")" << std::endl;
std::cout << "*pa0 = " << *pa0 << " (pa0 : " << pa0 << ")" << std::endl;
std::cout << "*pa1 = " << *pa1 << " (pa1 : " << pa1 << ")" << std::endl;
std::cout << "*pi2 = " << *pi2 << " (pi2 : " << pi2 << ")" << std::endl;
std::cout << "**pp0 = " << **pp0 << " (*pp0 : " << *pp0 << ")" << std::endl;
std::cout << "*pp0 = " << *pp0 << " (pp0 : " << pp0 << ")" << std::endl;
std::cout << "*pi4 = " << *pi4 << " (pi4 : " << pi4 << ")" << std::endl;
std::cout << "**pp1 = " << **pp1 << " (*pp1 : " << *pp1 << ")" << std::endl;
std::cout << "*pp1 = " << *pp1 << " (pp1 : " << pp1 << ")" << std::endl;
std::cout << std::endl;
// ポインタが指すオブジェクトを変更
pi0 = &i1;
std::cout << "pi0 = &i1 ---> *pi0 = " << *pi0 << " (pp0 : " << pi0 << ")" << std::endl;
// ポインタの加算・減算
pa0++;
pa1--;
std::cout << "pa0++ ---> *pa0 = " << *pa0 << " (pa0 : " << pa0 << ")" << std::endl;
std::cout << "pa1-- ---> *pa1 = " << *pa1 << " (pa1 : " << pa1 << ")" << std::endl;
// int型(const)へポインタが指すオブジェクトを変更
pi2 = &i1;
std::cout << "pi2 = &i1 ---> *pi2 = " << *pi2 << " (pi2 : " << pi2 << ")" << std::endl;
// ポインタ定数が指すオブジェクトの値を変更
(*pi3)++;
std::cout << "(*pi3)++ ---> *pi3 = " << *pi3 << " (pi3 : " << pi3 << ")" << std::endl;
// 「ポインタのポインタ」が指すポインタを変更
pp0 = &pa1;
std::cout << "pp0 = &pa1 ---> **pp0 = " << **pp0 << " (*pp0 : " << *pp0 << ")" << std::endl;
std::cout << "pp0 = &pa1 ---> *pp0 = " << *pp0 << " (pp0 : " << pp0 << ")" << std::endl;
// 「ポインタ定数へのポインタ」が指すポインタを変更
pp1 = &pa1;
std::cout << "pp1 = &pa1 ---> **pp1 = " << **pp1 << " (*pp1 : " << *pp1 << ")" << std::endl;
std::cout << "pp1 = &pa1 ---> *pp1 = " << *pp1 << " (pp1 : " << pp1 << ")" << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
i0 = 0 (0x7ffd3c82da04)
i1 = 1 (0x7ffd3c82da00)
a0[0] = 5 (0x7ffd3c82d9e0)
a0[1] = 6 (0x7ffd3c82d9e4)
a0[2] = 7 (0x7ffd3c82d9e8)
i3 = 9 (0x7ffd3c82d9c4)
*pi0 = 0 (pi0 : 0x7ffd3c82da04)
*pa0 = 5 (pa0 : 0x7ffd3c82d9e0)
*pa1 = 7 (pa1 : 0x7ffd3c82d9e8)
*pi2 = 8 (pi2 : 0x7ffd3c82d9dc)
**pp0 = 5 (*pp0 : 0x7ffd3c82d9e0)
*pp0 = 0x7ffd3c82d9e0 (pp0 : 0x7ffd3c82d9d0)
*pi4 = 9 (pi4 : 0x7ffd3c82d9c4)
**pp1 = 9 (*pp1 : 0x7ffd3c82d9c4)
*pp1 = 0x7ffd3c82d9c4 (pp1 : 0x7ffd3c82d9b8)
pi0 = &i1 ---> *pi0 = 1 (pp0 : 0x7ffd3c82da00)
pa0++ ---> *pa0 = 6 (pa0 : 0x7ffd3c82d9e4)
pa1-- ---> *pa1 = 6 (pa1 : 0x7ffd3c82d9e4)
pi2 = &i1 ---> *pi2 = 1 (pi2 : 0x7ffd3c82da00)
(*pi3)++ ---> *pi3 = 1 (pi3 : 0x7ffd3c82da04)
pp0 = &pa1 ---> **pp0 = 6 (*pp0 : 0x7ffd3c82d9e4)
pp0 = &pa1 ---> *pp0 = 0x7ffd3c82d9e4 (pp0 : 0x7ffd3c82d9c8)
pp1 = &pa1 ---> **pp1 = 6 (*pp1 : 0x7ffd3c82d9e4)
pp1 = &pa1 ---> *pp1 = 0x7ffd3c82d9e4 (pp1 : 0x7ffd3c82d9c8)
リファレンス(参照)
sample.cpp
#include <iostream>
// 参照渡しと値渡しとして引数を受け取る関数
void f0(int& i, int j)
{
++i;
++j;
}
// 引数を左辺値参照として受け取る関数
void f1(int& i)
{
std::cout << "lvalue reference ---> i = " << i << std::endl;
}
// 引数を右辺値参照として受け取る関数
void f1(int&& i)
{
std::cout << "rvalue reference ---> i = " << i << std::endl;
}
int main()
{
int i0 = 0;
int i1 = 1;
int i2 = 1;
std::cout << "各変数の初期値とアドレス" << std::endl;
std::cout << "i0 = " << i0 << " (&i0 : " << &i0 << ")" << std::endl;
std::cout << "i1 = " << i1 << " (&i1 : " << &i1 << ")" << std::endl;
std::cout << "i2 = " << i2 << " (&i2 : " << &i2 << ")" << std::endl;
std::cout << std::endl;
// i0 のリファレンス(参照)を作成(初期設定必須)
int& r0 = i0;
std::cout << "r0 = " << r0 << " (&r0 : " << &r0 << ")" << std::endl;
std::cout << std::endl;
// r0 を使用した演算(i0 に関する演算)
r0++;
std::cout << "r0++ を実行" << std::endl;
std::cout << " i0 = " << i0 << " (&i0 : " << &i0 << ")" << std::endl;
std::cout << " r0 = " << r0 << " (&r0 : " << &r0 << ")" << std::endl;
std::cout << std::endl;
// j0 を r0 を初期値として生成
int j0 = r0;
std::cout << "j0 = " << j0 << " (&j0 : " << &j0 << ")" << std::endl;
std::cout << std::endl;
// j0 を使用した演算(初期値としての r0 には影響無し)
j0++;
std::cout << "j0++ を実行" << std::endl;
std::cout << " i0 = " << i0 << " (&i0 : " << &i0 << ")" << std::endl;
std::cout << " r0 = " << r0 << " (&r0 : " << &r0 << ")" << std::endl;
std::cout << " j0 = " << j0 << " (&j0 : " << &j0 << ")" << std::endl;
std::cout << std::endl;
// 参照渡しと値渡しとして引数を受け取る関数の実行
f0(i1, i2);
std::cout << "f0(i1, i2) を実行" << std::endl;
std::cout << " void f0(int& i, int j)" << std::endl;
std::cout << " ++i;" << std::endl;
std::cout << " ++j;" << std::endl;
std::cout << " i1 = " << i1 << " (&i1 : " << &i1 << ")" << std::endl;
std::cout << " i2 = " << i2 << " (&i2 : " << &i2 << ")" << std::endl;
std::cout << std::endl;
// 左辺値を引数として関数 f1 を実行
std::cout << "f1(i1) を実行" << std::endl;
f1(i1);
// 右辺値を引数として関数 f1 を実行
std::cout << "f1(1) を実行" << std::endl;
f1(1);
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
各変数の初期値とアドレス
i0 = 0 (&i0 : 0x7ffca99f5be0)
i1 = 1 (&i1 : 0x7ffca99f5bdc)
i2 = 1 (&i2 : 0x7ffca99f5bd8)
r0 = 0 (&r0 : 0x7ffca99f5be0)
r0++ を実行
i0 = 1 (&i0 : 0x7ffca99f5be0)
r0 = 1 (&r0 : 0x7ffca99f5be0)
j0 = 1 (&j0 : 0x7ffca99f5bd4)
j0++ を実行
i0 = 1 (&i0 : 0x7ffca99f5be0)
r0 = 1 (&r0 : 0x7ffca99f5be0)
j0 = 2 (&j0 : 0x7ffca99f5bd4)
f0(i1, i2) を実行
void f0(int& i, int j)
++i;
++j;
i1 = 2 (&i1 : 0x7ffca99f5bdc)
i2 = 1 (&i2 : 0x7ffca99f5bd8)
f1(i1) を実行
lvalue reference ---> i = 2
f1(1) を実行
rvalue reference ---> i = 1
メンバへのポインタ
sample.cpp
#include <iostream>
struct Samp
{
int n0;
int n1;
Samp(int i1, int i2) : n0(i1), n1(i2) { }
int g0() { return n0;}
int g1() { return n1;}
};
int main()
{
// クラスメンバへのポインタを宣言
int Samp::* p = &Samp::n0;
int (Samp::* pf)() = &Samp::g0;
// Sampクラスのオブジェクトを生成
Samp s(5, 10);
// Sampクラスへのポインタを生成
Samp *ps = &s;
// int型メンバへのポインタで、メンバ n0、n1 へアクセス
std::cout << "int Samp::* p = &Samp::n0" << std::endl;
std::cout << " s.*p = " << s.*p << std::endl;
std::cout << " ps->*p = " << ps->*p << std::endl;
p = &Samp::n1;
std::cout << "p = &Samp::n1" << std::endl;
std::cout << " s.*p = " << s.*p << std::endl;
std::cout << " ps->*p = " << ps->*p << std::endl;
std::cout << std::endl;
// メンバ関数へのポインタで、メンバ関数 g0, g1 を呼び出し
std::cout << "int (Samp::* pf)() = &Samp::g0" << std::endl;
std::cout << " (s.*pf)() = " << (s.*pf)() << std::endl;
std::cout << " (ps->*pf)() = " << (ps->*pf)() << std::endl;
pf = &Samp::g1;
std::cout << "pf = &Samp::g1" << std::endl;
std::cout << " (s.*pf)() = " << (s.*pf)() << std::endl;
std::cout << " (ps->*pf)() = " << (ps->*pf)() << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
int Samp::* p = &Samp::n0
s.*p = 5
ps->*p = 5
p = &Samp::n1
s.*p = 10
ps->*p = 10
int (Samp::* pf)() = &Samp::g0
(s.*pf)() = 5
(ps->*pf)() = 5
pf = &Samp::g1
(s.*pf)() = 10
(ps->*pf)() = 10
配列
sample.cpp
#include <iostream>
#include <iomanip>
// バイト列表示用関数
void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname)
{
std::cout << std::uppercase << std::hex;
std::cout << tname << " = ";
for (std::size_t i=0; i<nlen; i++)
{
std::cout << std::setw(2) << std::setfill('0')
<< static_cast<int>(cp[i]) << " ";
}
std::cout << std::endl;
}
struct Samp
{
int n0;
int n1;
Samp() : n0(0x1234), n1(0xABCD) {}
int g0() { return n0;}
int g1() { return n1;}
};
int main(int argc, char *argv[])
{
constexpr std::size_t n0 = 5;
unsigned char n1 = 3;
// 要素数「5」で配列を宣言
char a0[3];
a0[0] = 1; // 要素「0」に1を代入
a0[2] = 3; // 要素「2」に3を代入
byteseq(reinterpret_cast<unsigned char *>(a0), sizeof(a0), "a0[]");
std::cout << std::endl;
// constexpr 指定したstd::size_t型変数を要素数として宣言
// {} で初期化
char a1[n0] = { 'a', 'b', 'c' };
std::cout << "n0 = " << n0 << std::endl;
byteseq(reinterpret_cast<unsigned char *>(a1), sizeof(a1), "a1[]");
std::cout << std::endl;
// unsigned char型変数を要素数として宣言
char a2[n1] = { 'a', 'b', 'c' };
std::cout << "n1 = " << static_cast<int>(n1) << std::endl;
byteseq(reinterpret_cast<unsigned char *>(a2), sizeof(a2), "a2[]");
std::cout << std::endl;
// main()関数の引数 argc を要素数としてchar へのポインタの配列を宣言
char *a3[argc];
for (int i=0; i<argc; i++)
{
a3[i] = argv[i];
std::cout << "a3[" << i << "] = " << a3[i] << std::endl;
}
std::cout << std::endl;
// 多次元配列を宣言
int a4[2][2] = {{1, 2}, {3, 4}};
byteseq(reinterpret_cast<unsigned char *>(a4), sizeof(a4), "a4[][]");
std::cout << std::endl;
// クラスを要素とする配列を宣言
Samp C0[2];
byteseq(reinterpret_cast<unsigned char *>(C0), sizeof(C0), "C0[]");
std::cout << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out aaa bbb
a0[] = 01 00 03
n0 = 5
a1[] = 61 62 63 00 00
n1 = 3
a2[] = 61 62 63
a3[0] = ./a.out
a3[1] = aaa
a3[2] = bbb
a4[][] = 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
C0[] = 34 12 00 00 CD AB 00 00 34 12 00 00 CD AB 00 00
初期化子
sample.cpp
#include <iostream>
#include <iomanip>
// バイト列表示用関数
void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname)
{
std::cout << std::uppercase << std::hex;
std::cout << tname << " = ";
for (std::size_t i=0; i<nlen; i++)
{
std::cout << std::setw(2) << std::setfill('0')
<< static_cast<int>(cp[i]) << " ";
}
std::cout << std::endl;
}
struct Samp
{
int n0;
int n1;
};
// ゼロ初期化
char c0;
double d0;
unsigned int u0;
int main()
{
byteseq(reinterpret_cast<unsigned char *>(&c0), sizeof(c0), "c0");
byteseq(reinterpret_cast<unsigned char *>(&d0), sizeof(d0), "d0");
byteseq(reinterpret_cast<unsigned char *>(&u0), sizeof(u0), "u0");
// a0[3]~ ゼロ初期化
char a0[10] = { 'a', 'b', 'c' };
byteseq(reinterpret_cast<unsigned char *>(&a0), sizeof(a0), "a0");
// ゼロ初期化
int i0{};
char *p0{};
byteseq(reinterpret_cast<unsigned char *>(&i0), sizeof(i0), "i0");
byteseq(reinterpret_cast<unsigned char *>(&p0), sizeof(p0), "p0");
// ポインタをnullptrで初期化(比較用)
char *pn = nullptr;
byteseq(reinterpret_cast<unsigned char *>(&pn), sizeof(pn), "pn");
// デフォルト初期化
Samp s0;
byteseq(reinterpret_cast<unsigned char *>(&s0), sizeof(s0), "s0");
// コピー初期化
int i1 = 2;
byteseq(reinterpret_cast<unsigned char *>(&i1), sizeof(i1), "i1");
// クラスを初期化子リストで初期化
Samp s1{1, 2};
byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1");
Samp s2 = {3, 4};
byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2");
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
c0 = 00
d0 = 00 00 00 00 00 00 00 00
u0 = 00 00 00 00
a0 = 61 62 63 00 00 00 00 00 00 00
i0 = 00 00 00 00
p0 = 00 00 00 00 00 00 00 00
pn = 00 00 00 00 00 00 00 00
s0 = 00 00 00 00 00 00 00 00
i1 = 02 00 00 00
s1 = 01 00 00 00 02 00 00 00
s2 = 03 00 00 00 04 00 00 00
初期化子 - 文字配列
sample.cpp
#include <iostream>
#include <iomanip>
// バイト列表示用関数
void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname)
{
std::cout << std::uppercase << std::hex;
std::cout << tname << " = ";
for (std::size_t i=0; i<nlen; i++)
{
std::cout << std::setw(2) << std::setfill('0')
<< static_cast<int>(cp[i]) << " ";
}
std::cout << std::endl;
}
int main()
{
// ナロー文字型配列の初期化
char s_chr[] = "abcde";
// char16_t型配列の初期化
char16_t s_c16[] = u"あいうえお";
// char32_t型配列の初期化
char32_t s_c32[] = U"あいうえお";
// wchar_t型配列の初期化
wchar_t s_wct[] = L"あいうえお";
byteseq(reinterpret_cast<unsigned char *>(&s_chr), sizeof(s_chr), "s_chr");
byteseq(reinterpret_cast<unsigned char *>(&s_c16), sizeof(s_c16), "s_c16");
byteseq(reinterpret_cast<unsigned char *>(&s_c32), sizeof(s_c32), "s_c32");
byteseq(reinterpret_cast<unsigned char *>(&s_wct), sizeof(s_wct), "s_wct");
std::cout << std::endl;
// ナロー文字・ワイド文字における「\0」のバイト列
char sn0 = '\0';
wchar_t sw0 = '\0';
byteseq(reinterpret_cast<unsigned char *>(&sn0), sizeof(char), "sn0");
byteseq(reinterpret_cast<unsigned char *>(&sw0), sizeof(wchar_t), "sw0");
std::cout << std::endl;
// 配列要素に満たない文字列による初期化
char s1[10] = "abcde";
char32_t s2[5] = U"あいう";
byteseq(reinterpret_cast<unsigned char *>(&s1), sizeof(s1), "s1");
byteseq(reinterpret_cast<unsigned char *>(&s2), sizeof(s2), "s2");
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
s_chr = 61 62 63 64 65 00
s_c16 = 42 30 44 30 46 30 48 30 4A 30 00 00
s_c32 = 42 30 00 00 44 30 00 00 46 30 00 00 48 30 00 00 4A 30 00 00 00 00 00 00
s_wct = 42 30 00 00 44 30 00 00 46 30 00 00 48 30 00 00 4A 30 00 00 00 00 00 00
sn0 = 00 <--- charにおける「\0」
sw0 = 00 00 00 00 <--- wchar_tにおける「\0」
s1 = 61 62 63 64 65 00 00 00 00 00 <--- 明示的に初期化されていな要素は「\0」で初期化
s2 = 42 30 00 00 44 30 00 00 46 30 00 00 00 00 00 00 00 00 00 00 <--- 〃
初期化子 - リファレンス
sample.cpp
#include <iostream>
#include <iomanip>
// バイト列表示用関数
void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname)
{
std::cout << std::uppercase << std::hex;
std::cout << tname << " = ";
for (std::size_t i=0; i<nlen; i++)
{
std::cout << std::setw(2) << std::setfill('0')
<< static_cast<int>(cp[i]) << " ";
}
std::cout << std::endl;
}
void f(int& i, int j)
{
++i;
++j;
}
int main()
{
int i = 1;
int& ri = i;
std::cout << "初期化実行後" << std::endl;
std::cout << "i = " << i << std::endl;
std::cout << "&i = " << &i << std::endl;
std::cout << "ri = " << ri << std::endl;
std::cout << "&ri = " << &ri << std::endl;
byteseq(reinterpret_cast<unsigned char *>(&ri), sizeof(ri), "ri");
std::cout << std::endl;
std::cout << "リファレンスへの代入" << std::endl;
ri = 4;
std::cout << "i = " << i << std::endl;
std::cout << "ri = " << ri << std::endl;
std::cout << std::endl;
std::cout << "リファレンスへのポインタ" << std::endl;
int *pi = &ri;
std::cout << "*pi = " << *pi << std::endl;
std::cout << "pi = " << pi << std::endl;
std::cout << std::endl;
std::cout << "配列へのリファレンス" << std::endl;
unsigned char u[3] = {1, 2, 3};
unsigned char (&ru)[3] = u;
byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u");
byteseq(reinterpret_cast<unsigned char *>(&ru), sizeof(ru), "ru");
std::cout << "「ru[0] = 4」 を実行" << std::endl;
ru[0] = 4;
byteseq(reinterpret_cast<unsigned char *>(&u), sizeof(u), "u");
byteseq(reinterpret_cast<unsigned char *>(&ru), sizeof(ru), "ru");
std::cout << std::endl;
std::cout << "関数へのリファレンス" << std::endl;
void (&rf)(int&, int) = f;
int j = 2;
std::cout << "「rf(ri, j)」 を実行" << std::endl;
rf(ri, j);
std::cout << "i = " << i << std::endl;
std::cout << "j = " << j << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
初期化実行後
i = 1
&i = 0x7ffc7b9c8478
ri = 1
&ri = 0x7ffc7b9c8478
ri = 01 00 00 00
リファレンスへの代入
i = 4
ri = 4
リファレンスへのポインタ
*pi = 4
pi = 0x7ffc7b9c8478
配列へのリファレンス
u = 01 02 03
ru = 01 02 03
「ru[0] = 4」 を実行
u = 04 02 03
ru = 04 02 03
関数へのリファレンス
「rf(ri, j)」 を実行
i = 5
j = 2
初期化子 - リスト初期化
sample.cpp
#include <iostream>
#include <iomanip>
// バイト列表示用関数
void byteseq(const unsigned char *cp, std::size_t nlen, const char *tname)
{
std::cout << std::uppercase << std::hex;
std::cout << tname << " = ";
for (std::size_t i=0; i<nlen; i++)
{
std::cout << std::setw(2) << std::setfill('0')
<< static_cast<int>(cp[i]) << " ";
}
std::cout << std::endl;
}
struct Samp
{
Samp(int i, int j) : m(i), n(j) {}
private:
int m;
int n;
};
int main()
{
// リスト初期化
std::cout << "変数定義の初期化" << std::endl;
int i0{1};
int i1 = {2};
std::cout << "i0 = " << i0 << std::endl;
std::cout << "i1 = " << i1 << std::endl;
std::cout << std::endl;
std::cout << "new演算子の初期化" << std::endl;
Samp *psamp = new Samp{-1, 1};
byteseq(reinterpret_cast<unsigned char *>(psamp), sizeof(Samp), "psamp");
std::cout << std::endl;
std::cout << "for文における範囲ベースの初期化" << std::endl;
for (int n : {1, 2, 3})
{
std::cout << "for n = " << n << std::endl;
}
std::cout << std::endl;
}
実行結果
$ gcc -Wall sample.cpp -lstdc++
$ ./a.out
変数定義の初期化
i0 = 1
i1 = 2
new演算子の初期化
psamp = FF FF FF FF 01 00 00 00
for文における範囲ベースの初期化
for n = 1
for n = 2
for n = 3
実行環境
GNU bash, version 5.1.16
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
GCC-12.2.0
GNU C Library 2.36
GNU Binutils 2.39
コード例・出力内容中の表記
・実行例中の太字表記部分は、コマンドなどの入力された文字列を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。
・「︙」や「...」の着色省略表記は、 実際のソースコードや出力内容などを省略加工した部分を示します。