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] … 2
c++ 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);