質問
この例は問題なく動作します:
static char *daytab[] = {
"hello",
"world"
};
これはしません:
static char *daytab[] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
見たところ、最初の例では、2つの文字列リテラル(それ自体が配列です)へのポインターで満たされた配列が作成されます。 2番目の例であるIMOは同一である必要があります-配列を作成し、2つのchar配列へのポインターで埋めます。
2番目の例が間違っている理由を誰かに説明してもらえますか?
PSおそらく次のように書くことができます(テストしていません):
static char a[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static char b[] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static char *daytab[] = {
a,
b
};
しかし、それはあまりにも多くの作業のように見えます:)。
解決
これ:
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
単なる配列初期化子です。それ自体は配列を作成しません。最初の例では、文字列リテラルをポインターに割り当てたときに、DIDは静的ストレージにそれらの文字列を作成し(非表示)、ポインターを割り当てました。
したがって、基本的に、配列初期化子でchar *を初期化する方法はありません。実際の配列を作成し、それらに番号を割り当てる必要があります。次のようなことをする必要があります:
char a[][] = { {32, 30, 0}, {34, 32, 33, 0} }; // illegal
しかしそれは違法です。
配列を個別に構築し、最後の例のように配列に割り当てる必要があります。
他のヒント
試してください:
static char daytab[][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
これらはchar *ではありません。どちらもcharではありません。おそらく必要です:
static int daytab[][13] ...
まあ、このスレッドはすでに少し古いですが、私は同じ問題を扱っていて、配列へのポインターの配列を次のように初期化する方法を見つけました:
#include <iostream>
using namespace std;
int *a[] = {
(int[]) { 0 } ,
(int[]) { 1 , 2 } ,
(int[]) { 3 , 4 , 5 } ,
(int[]) { 6 , 7 , 8 , 9 }
};
main()
{
cout
<< a[0][0] << endl
<< a[1][0] << " " << a[1][1] << endl
<< a[2][0] << " " << a[2][1] << " " << a[2][2] << endl
<< a[3][0] << " " << a[3][1] << " " << a[3][2] << " " << a[3][3] << endl;
}
そして、gnu g ++でコンパイルして出力を取得します
0
1 2
3 4 5
6 7 8 9
およびIntel icpcでコンパイル
0
1 1
40896 32767 -961756724
0 32767 4198878 0
そのため、おそらくこのスタイルの一般的な使用法の欠如のために、インテル®コンパイラーが構文を正しくサポートしていないという点で、構文は原則として正しいようです。
---編集---
これもCバージョンです(必要に応じて):
#include <stdio.h>
int *a[] = {
(int[]) { 0 } ,
(int[]) { 1 , 2 } ,
(int[]) { 3 , 4 , 5 } ,
(int[]) { 6 , 7 , 8 , 9 }
};
int main()
{
printf( "%d\n" , a[0][0] );
printf( "%d %d\n" , a[1][0] , a[1][1] );
printf( "%d %d %d\n" , a[2][0] , a[2][1] , a[2][2] );
printf( "%d %d %d %d\n" , a[3][0] , a[3][1] , a[3][2] , a[3][3] );
}
gccとclangでテストし、正しい結果を出力します。 ところでインテル®コンパイラーの誤った出力は、コンパイラーのバグでした。
static char **daytab;
daytab=(char**)malloc(2*sizeof(char*));
daytab[0]=(char*)(char[]){0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
daytab[1]=(char*)(char[]){0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
2番目の例の構文は、多次元配列リテラルの構文です。
多次元配列は、配列へのポインタの配列ではありません。
宣言された型に応じて、あるものの構文が別のものの構文でもあるとしたら、驚くでしょう。
最初の例では、文字列リテラルは配列値ではなくポインターに評価されるため、値はポインターの配列として評価されます。配列リテラルはポインターではなく配列値として評価されるため、2番目の例は多次元配列-要素が配列値である配列であり、要素が配列値へのポインターである配列ではありません。
次のコードは、多次元配列、配列へのポインター、ポインターの配列、およびポインターへのポインターの組み合わせを示しています。これら3つのうち、ポインターの配列とポインターへのポインターのみが相互に互換性があります。
void multi_array (int x[][4], size_t len) // multidimensional array
{
for (size_t i = 0; i < len; ++i)
for (size_t j = 0; j < 4; ++j)
putchar( 'a' + x[i][j] );
putchar('\n');
}
void ptr_array (int (*x)[4], size_t len) // pointer to an array
{ ... as multi_array }
void array_ptr (int *x[], size_t len) // array of pointers
{ ... as multi_array }
void ptr_ptr (int **x, size_t len) // pointer to pointer
{ ... as multi_array }
int main() {
int a[][4] = { { 1,2,3,4 } };
int b[] = { 1,2,3,4 };
int* c[] = { b };
multi_array( a, 1 );
multi_array( (int[][4]) { { 1,2,3,4} }, 1 ); // literal syntax as value
ptr_array( &b, 1 );
array_ptr( c, 1 );
ptr_ptr( c, 1 ); // only last two are the same
return 0;
}
最初の例も機能しないことに注意してください。次のようにする必要があります:
static const char *daytab[] = {
"hello",
"world"
};
定数に注意してください。
編集:「うまくいかない」とは、悪い習慣であり、間違いを起こしやすいことを意味します。