decl-specifier identifier [] decl-specifier identifier [ constant-expression ] decl-specifier identifier [][ constant-expression ] ... decl-specifier identifier [ constant-expression ][ constant-expression ] ...配列 (C++) | MSDN
int p[10];
要素の数は定数式で指定しなければなりません。
const int num1 = 10; int p1[num1]; // OK int num2 = 10; int p2[num2]; // C2131 式は定数に評価されませんでした (expression did not evaluate to a constant)
これを変数で指定するならば、new演算子で生成します。
int num2 = 10; int* p2 = new int[num2]; // 配列を使用する処理 delete[] p2; // 不要になったならば、明示的に解放する
作成時に指定した要素数は固定のため、これを可変としたければvectorを用います。
初期値を与える場合には、要素数を指定する必要はありません。
int p[] = { 1, 2, 3 };
要素数を指定しても良いですが、与えられた要素の数が指定の要素数を超えると、エラーとなります。
int p1[3] = { 1, 2, 3 }; // OK int p2[2] = { 1, 2, 3 }; // エラー
一方で指定の要素数に満たないと、残りの要素は既定値で初期化されます。
int p1[3] = { 1, 0, 0 }; // 1, 0, 0 が格納される int p2[3] = { 1 }; // 1, 0, 0 int p3[3] = {}; // 0, 0, 0 int p4[3]{}; // 0, 0, 0 char ch[2] = {}; // '\0', '\0' int* ip[2] = {}; // 0x00000000, 0x00000000配列の初期化 | MSDN
Visual C++では初期化していない領域に、次の様に値が割り当てられます。
int* i = new int[3];
インデックス | 値 | 値 (10進) | 型 |
---|---|---|---|
0x00 (i[-3]) | 0x00000000 ※1 | 0 | int |
0x01 (i[-2]) | 0x00000042 ※1 | 66 | int |
0x02 (i[-1]) | 0xfdfdfdfd | -33686019 | int |
0x03 (i[0]) | 0xcdcdcdcd | -842150451 | int |
0x04 (i[1]) | 0xcdcdcdcd | -842150451 | int |
0x05 (i[2]) | 0xcdcdcdcd | -842150451 | int |
0x06 (i[3]) | 0xfdfdfdfd | -33686019 | int |
0x07 (i[4]) | 0x00000000 ※1 | 0 | int |
double* d = new double[3];
インデックス | 値 | 型 |
---|---|---|
0x00 (d[-3]) | 2.121995790965e-314#DEN ※1 | double |
0x01 (d[-2]) | 1.185757550019e-322#DEN ※1 | double |
0x02 (d[-1]) | -7.8459077160212854e+298 ※1 | double |
0x03 (d[0]) | -6.2774385622041925e+66 | double |
0x04 (d[1]) | -6.2774385622041925e+66 | double |
0x05 (d[2]) | -6.2774385622041925e+66 | double |
0x06 (d[3]) | 2.105352686232e-314#DEN ※1 | double |
0x07 (d[4]) | 0.00000000000000000 ※1 | double |
インデックス | 値 | 型 |
---|---|---|
0x00 | 0x00000000 ※1 | int |
0x01 | 0x00000043 ※1 | int |
0x02 | 0xfdfdfdfd | int |
0x03 | 0xcdcdcdcd | int |
0x04 | 0xcdcdcdcd | int |
0x05 | 0xcdcdcdcd | int |
0x06 | 0xcdcdcdcd | int |
0x07 | 0xcdcdcdcd | int |
0x08 | 0xcdcdcdcd | int |
0x09 | 0xfdfdfdfd | int |
0x0a | 0x00000000 ※1 | int |
ある配列pがあるとき、
であることから、sizeof( p ) / sizeof( *p )で要素数が求まります。
int p[] = { 1,2,3,4,5 }; int s1 = sizeof( p ); // 20 int s2 = sizeof( *p ); // 4 int s3 = sizeof( p ) / sizeof( *p ); // 5
一方で、関数の引数に渡されるのは配列ではなくポインタであるため、関数の引数に対してはこの方法は適用できません。
int main() { int p[] = { 1,2,3,4,5 }; // pは配列の先頭要素へのポインタ Func(p); } void Func( int p[] ) // pはポインタ { int s1 = sizeof( p ); // 4 (int*のバイト数) int s2 = sizeof( *p ); // 4 int s3 = sizeof( p ) / sizeof( *p ); // 1 }
配列の名前は、その配列の先頭要素へのポインタです。よって次のp1とp2は同一のアドレスを示します。
int* p1 = a; int* p2 = &a[0];
また配列の要素はアドレス順に配置されるため、ポインタを1つ進めた位置の値は、[1]とした位置の値と同一です。
int x1 = *(a + 1); int x2 = a[1];
int p[3]としたとき、この配列へは下表のようにアクセスできます。
記述 | 型 | 意味 |
---|---|---|
&p | int(*)[3] | 配列全体を指すポインタ |
p | int* | 配列の要素p[0]を指すポインタ |
*&p | ||
p+1 | int* | 配列の要素p[1]を指すポインタ |
p+2 | int* | 配列の要素p[2]を指すポインタ |
*p | int | 配列の要素p[0]の値 |
p[0] | ||
*(p+1) | int | 配列の要素p[1]の値 |
p[1] |
このような配列を示すポインタは、Visual Studioでは「p,n
」の形式でウォッチ ウィンドウでデバッグできます。
多次元配列は、配列の配列として表現されます。
int p2[3][2];
初期値を与える場合には、最初の要素数以外は省略できません。
int p2[][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
int p3[][3][2] = { { { 1, 2 }, { 3, 4 }, { 5, 6 } }, { { 7, 8 }, { 9, 10 }, { 11, 12 } } };
for( int i = 0; i < 3; i++ ) { for( int k = 0; k < 2; k++ ) { p2[i][k] = 10 * i + k; } }
多次元配列は、メモリ上では1次元の連続した領域として確保されます。たとえば3×2の配列は、次のように格納されます。
要素 | [0][0] | [0][1] | [1][0] | [1][1] | [2][0] | [2][1] |
---|---|---|---|---|---|---|
1次元 | 要素が2個ずつ | 要素が2個ずつ | 要素が2個ずつ | |||
2次元 | 配列が3個 |
int t[3][2]としたとき、この配列へは下表のようにアクセスできます。
記述 | 型 | 意味 |
---|---|---|
&t | int(*)[3][2] | 配列t全体を指すポインタ |
t | int(*)[2] | 配列の要素t[0]を指すポインタ |
t+0 | ||
t+1 | int(*)[2] | 配列の要素t[1]を指すポインタ |
*t | int * | 配列の要素t[0][0]を指すポインタ |
t[0] | ||
*(t+1) | int * | 配列の要素t[1][0]を指すポインタ |
t[1] | ||
**t (ダブルポインタ) |
int | 配列の要素t[0][0]の値 |
*t[0] | ||
t[0][0] | ||
*(*t+1) | int | 配列の要素t[0][1]の値 |
*(*(t+1)) | int | 配列の要素t[1][0]の値 |
*t[1] | ||
t[1][0] | ||
*(*(t+1)+1) | int | 配列の要素t[1][1]の値 |
memcpy()では、バッファ間でバイトをコピーできます。
void *memcpy( void *dest, // コピー先のバッファ const void *src, // コピー元のバッファ size_t count // コピーする文字数 );memcpy、wmemcpy | MSDN
戻り値はdestの値です。
int p1[] = { 1,2,3 }; int p2[3]; memcpy(p2, p1, sizeof(int) * 3);
配列コピー時に犯しやすい誤りに注意する ――C/C++セキュアコーディング入門(6) (1/2):CodeZine(コードジン) 富樫 一哉 (2010/02/15)
これはstd::copy()で、次のようにも記述できます。
int p1[] = { 1,2,3 }; int p2[3]; std::copy(p1, p1 + 3, p2);Is there a function to copy an array in C/C++? - Stack Overflow
この関数は次のように宣言されています。
template<class InputIterator, class OutputIterator> OutputIterator copy( InputIterator _First, InputIterator _Last, OutputIterator _DestBeg );copy - <algorithm> functions | MSDN
std::copy()で異なる型へコピーすることで、異なる型に変換できます。
double p1[] = { 1.0,1.5,2.0 }; int p2[3]; std::copy(p1, p1 + 3, p2); // p2[0] … 1 // p2[1] … 1 // p2[2] … 2c++ typecast array - Stack Overflow
文字列リテラルとは、ダブルクォートで囲まれた文字の連続です。その型は適切な数のconst文字の配列であり、たとえば"ABC"
はconst char[4]
です。
次の4つは同一の文字列に初期化されます。
char s1[4] = { 'A','B','C','\0' }; char s2[4] = "ABC"; char s3[] = { 'A','B','C','\0' }; char s4[] = "ABC";
要素数を指定するならば、終端のnullに注意します。
char str1[4] = "ABC"; // OK char str2[3] = "ABC"; // error C2117: 'str2': 指定された配列には、初期化子が多すぎます。
Cとの互換性のために、文字列リテラルはchar*
に代入できます。しかし実体はconst char*
であるため、
char* p = "ABC"; p[0] = 'X'; // アクセス違反 *p = 'X'; // アクセス違反
とすると、処理系によっては実行時エラーとなります。ですので変更する必要があるならば、
char p[] = "ABC"; p[0] = 'X';
のように、配列に代入するようにします。文字列リテラルの変更 - 文字列リテラルと文字リテラル (C++) | MSDN
ワイド文字リテラルは、L'a'のように「L」という接頭辞をつけて表現します。
wchar_t* str = L"SAMPLE";
「L」接頭辞を付けるかどうかを、処理系に合わせて適用できます。
void TEXT( LPTSTR string );TEXT macro (Windows) | MSDN
このマクロはWinNT.hで次のように定義されており、#defineディレクティブで"UNICODE"と定義されているときに有効となります。
#ifdef UNICODE // r_winnt #define TEXT(quote) __TEXT(quote) // r_winnt #define __TEXT(quote) L##quote // r_winnt
ところでマクロの引数であるLPTSTRとは、UNICODEが定義されていればwchar_t*
typedef LPWSTR PTSTR, LPTSTR;
typedef _Null_terminated_ WCHAR *NWPSTR, *LPWSTR, *PWSTR;
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
定義されていなければchar*となります。
typedef LPSTR PTSTR, LPTSTR, PUTSTR, LPUTSTR; typedef _Null_terminated_ CHAR *NPSTR, *LPSTR, *PSTR; typedef char CHAR;
エスケープが必要な文字を、エスケープせずに記述できます。
R"(文字列)"
char s1[] = R"("A")"; // "A" // s1[0] 0x22 '\"' // s1[1] 0x41 'A' // s1[2] 0x22 '\'' // s1[3] 0x00 '\0' char s2[] = R"(\r\n)"; // \r\n char s3[] = R"(c:\dir\text.txt)"; // c:\dir\text.txt
型 | 表記 |
---|---|
char (UTF-8) | u8R"( ... )" |
wchar_t | LR"( ... )" |
wchar16_t (UTF-16) | uR"( ... )" |
wchar32_t (UTF-32) | UR"( ... )" |
void Func1(int p[], int size) { for (int i = 0; i < size; i++) p[i] = i; }
引数として渡されるのは実際にはポインタのため、次のように記述しても同じです。
void Func2(int *p, int size) { for (int i = 0; i < size; i++) p[i] = i * 2; }
これらの関数は、次のように呼び出せます。
int p[3]; Func1(p, 3); Func2(p, 3);