静的な2次元配列へのポインターが必要です。これはどのように行われますか?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
次のようなさまざまなエラーが発生します。
- 警告:互換性のないポインタ型からの割り当て
- 下付きの値は配列でもポインタでもありません
- エラー:フレキシブル配列メンバーの無効な使用
静的な2次元配列へのポインターが必要です。これはどのように行われますか?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
次のようなさまざまなエラーが発生します。
回答:
ここで、配列の最初の要素へのポインタを作成します
uint8_t (*matrix_ptr)[20] = l_matrix;
typedefを使用すると、これはよりきれいに見えます
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
その後、あなたは人生を再び楽しむことができます:)
matrix_ptr[0][1] = ...;
Cのポインター/配列の世界に注意してください。これについては多くの混乱があります。
コメントフィールドが短すぎてそこで行うことができないため、ここで他のいくつかの回答を確認します。複数の代替案が提案されましたが、それらがどのように動作するかは示されていませんでした。ここに彼らがする方法があります
uint8_t (*matrix_ptr)[][20] = l_matrix;
エラーを修正&
し、次のスニペットのようにアドレス演算子を追加した場合
uint8_t (*matrix_ptr)[][20] = &l_matrix;
次に、20 uint8_tの配列型の要素の不完全な配列型へのポインターを作成します。ポインタは配列の配列を指すため、次のようにしてアクセスする必要があります
(*matrix_ptr)[0][1] = ...;
そして、それは不完全な配列へのポインタなので、ショートカットとして行うことはできません
matrix_ptr[0][0][1] = ...;
インデックスを作成するには、要素タイプのサイズがわかっている必要があるためです(インデックス付けは、ポインタに整数を追加することを意味するため、不完全なタイプでは機能しません)。とは互換性のあるタイプC
であるためT[]
、これはでのみ機能することに注意してくださいT[N]
。C ++には互換性のある型の概念がないためT[]
、とT[10]
は型が異なるため、そのコードは拒否されます。
次の代替案はまったく機能しません。これは、配列の要素タイプを1次元配列として表示すると、それがではない uint8_t
ためですが、uint8_t[20]
uint8_t *matrix_ptr = l_matrix; // fail
以下は良い選択肢です
uint8_t (*matrix_ptr)[10][20] = &l_matrix;
あなたはそれにアクセスします
(*matrix_ptr)[0][1] = ...;
matrix_ptr[0][0][1] = ...; // also possible now
外形寸法を保持するという利点があります。だからあなたはそれにsizeofを適用することができます
sizeof (*matrix_ptr) == sizeof(uint8_t) * 10 * 20
配列内のアイテムが連続して格納されているという事実を利用するもう1つの答えがあります
uint8_t *matrix_ptr = l_matrix[0];
さて、これは正式には2次元配列の最初の要素の要素にのみアクセスできるようにします。つまり、次の条件が成立します
matrix_ptr[0] = ...; // valid
matrix_ptr[19] = ...; // valid
matrix_ptr[20] = ...; // undefined behavior
matrix_ptr[10*20-1] = ...; // undefined behavior
おそらくまで動作することに気づくでしょう10*20-1
が、エイリアス分析やその他の積極的な最適化を行うと、一部のコンパイラはそのコードを壊す可能性があると仮定する可能性があります。とは言っても、失敗するコンパイラーに遭遇したことはありません(しかし、実際のコードではその手法を使用していません)。CFAQにもその手法が含まれています(そのUBについての警告付き) )、そして配列タイプを変更できない場合、これはあなたを救う最後のオプションです:)
uint8_t *d[20]
uint8_tへの3つのポインターの配列を作成すると混同している可能性がありますが、この場合は機能しません。
完全にこのことを理解し、あなたがしなければなりませんは、次の概念ます。
まず第一に(そして十分に説教されています)、配列はポインタではありません。代わりに、ほとんどの用途では、それらは最初の要素へのアドレスに「減衰」します。これは、ポインターに割り当てることができます。
int a[] = {1, 2, 3};
int *p = a; // p now points to a[0]
私はそれがこのように機能するので、配列の内容にすべてをコピーせずにアクセスできると思います これは単に配列型の動作であり、それらが同じものであることを意味するものではありません。
多次元配列は、コンパイラ/マシンが理解して操作できるようにメモリを「パーティション化」する方法にすぎません。
例えば、 int a[4][3][5]
= 4 * 3 * 5(60)の整数サイズのメモリの「チャンク」を含む配列。
int a[4][3][5]
vsプレーンを使用することの利点int b[60]
は、それらが「パーティション化」され(必要に応じて、「チャンク」での作業がより簡単になる)、プログラムが境界チェックを実行できるようになることです。
実際にint a[4][3][5]
は、メモリとまったく同じようint b[60]
に格納されます。唯一の違いは、プログラムが特定のサイズの個別のエンティティ(具体的には、5つのグループの3つのグループ)であるかのようにプログラムを管理することです。
覚えておいてください:int a[4][3][5]
とint b[60]
は両方ともメモリ内で同じであり、唯一の違いは、それらがアプリケーション/コンパイラによってどのように処理されるかです。
{
{1, 2, 3, 4, 5}
{6, 7, 8, 9, 10}
{11, 12, 13, 14, 15}
}
{
{16, 17, 18, 19, 20}
{21, 22, 23, 24, 25}
{26, 27, 28, 29, 30}
}
{
{31, 32, 33, 34, 35}
{36, 37, 38, 39, 40}
{41, 42, 43, 44, 45}
}
{
{46, 47, 48, 49, 50}
{51, 52, 53, 54, 55}
{56, 57, 58, 59, 60}
}
これから、各「パーティション」はプログラムが追跡している単なる配列であることがはっきりとわかります。
現在、配列はポインタと構文的に異なります。具体的には、これはコンパイラ/マシンがそれらを異なる方法で処理することを意味します。これは簡単なように思えるかもしれませんが、これを見てください。
int a[3][3];
printf("%p %p", a, a[0]);
上記の例では、次のように同じメモリアドレスを2回出力します。
0x7eb5a3b4 0x7eb5a3b4
ただし、ポインタに割り当てることができるのは1つだけなので、直接:
int *p1 = a[0]; // RIGHT !
int *p2 = a; // WRONG !
なぜ a
ポインターに割り当てる ことができないのa[0]
ですか?
これは単に、多次元配列の結果であり、その理由を説明します。
「a
」のレベルでも、楽しみにできる別の「次元」があることがわかります。a[0]
ただし、' ' のレベルでは、すでに最上位の次元にいるので、プログラムに関する限り、通常の配列を調べているだけです。
あなたは尋ねるかもしれません:
配列へのポインターの作成に関して、配列が多次元であることがなぜ重要なのですか?
このように考えるのが最善です:
多次元配列からの「減衰」は、単なるアドレスではなく、パーティションデータを含むアドレスです(これは、基礎となるデータが他の配列で構成されていることも認識しています)。これは、最初の次元を超える配列によって設定された境界で構成されます。
この「パーティション」ロジックは、指定しない限り、ポインター内に存在できません。
int a[4][5][95][8];
int (*p)[5][95][8];
p = a; // p = *a[0] // p = a+0
そうしないと、配列のソートプロパティの意味が失われます。
また、周りの括弧の使用を注意してください*p
:int (*p)[5][95][8]
-我々はこれらの限界は、これらの境界を持つポインタのない配列とポインタを作っていることを指定すること:int *p[5][95][8]
確認してみましょう:
簡単に言うと、多次元配列は、その内容を理解する能力を持つアドレスに減衰します。
int *p1 = &(a[0]); // RIGHT !
、実際には次と同じですint *p1 = a;
G'day、
宣言
static uint8_t l_matrix[10][20];
unit8_tの20の場所、つまり200 uint8_tサイズの場所の10行のストレージを確保しました。各要素は、20 x行+列を計算することで見つかります。
そうではない
uint8_t (*matrix_ptr)[20] = l_matrix;
必要なものを与えて、配列の最初の行の列0の要素をポイントしますか?
編集:これについてもう少し考えてみると、配列名は定義上、ポインタではありませんか?つまり、配列の名前は最初の要素の場所の同義語です。すなわち、l_matrix [0] [0]?
Edit2:他の人が述べたように、コメントスペースはこれ以上議論するには少なすぎます。とにかく:
typedef uint8_t array_of_20_uint8_t[20];
array_of_20_uint8_t *matrix_ptr = l_matrix;
問題のアレイにストレージの割り当てを提供しません。
上記のように、そして標準で定義されているように、ステートメントは:
static uint8_t l_matrix[10][20];
タイプuint8_tの200の連続した場所を確保しました。
次の形式のステートメントを使用してl_matrixを参照します。
(*l_matrix + (20 * rowno) + colno)
行rownoにあるcolno番目の要素の内容を提供します。
すべてのポインター操作は、ポイントされたオブジェクトのサイズを自動的に考慮します。-K&Rセクション5.4、p.103
これは、パディングまたはバイトアラインメントのシフトが手元のオブジェクトのストレージに関係している場合にも当てはまります。コンパイラはこれらを自動的に調整します。C ANSI規格の定義による。
HTH
乾杯、
C99(clangとgccでサポート)には、多次元配列を参照によって関数に渡すためのあいまいな構文があります。
int l_matrix[10][20];
void test(int matrix_ptr[static 10][20]) {
}
int main(void) {
test(l_matrix);
}
単純なポインタとは異なり、これは配列サイズについてのヒントであり、理論的には、コンパイラが小さすぎる配列を渡すことについて警告し、境界外のアクセスであることを明らかにすることができます。
悲しいことに、それは修正されずsizeof()
、コンパイラーはまだその情報を使用していないようです。そのため、それは好奇心のままです。
static 10
は、少なくとも 10要素が存在することを保証するものです。これも、サイズが固定されていないことを意味します。
static
がないと、配列が参照渡しされないことを示唆しているようですが、これは正しくありません。配列はとにかく参照渡しされます。元の質問は別のユースケースについて尋ねました-同じ関数/名前空間内の補助ポインターを使用して2D配列の要素にアクセスします。
配列を線形として宣言し、(row、col)から配列のインデックス計算を自分で行うことで、コンパイラをいじるのをいつでも回避できます。
static uint8_t l_matrix[200];
void test(int row, int col, uint8_t val)
{
uint8_t* matrix_ptr = l_matrix;
matrix_ptr [col+y*row] = val; // to assign a value
}
これは、コンパイラがとにかくやったことです。
多次元配列を指すポインターを初期化する基本的な構文は次のとおりです。
type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name
それを呼び出すための基本的な構文は
(*pointer_name)[1st index][2nd index][...]
次に例を示します。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// The multidimentional array...
char balance[5][100] = {
"Subham",
"Messi"
};
char (*p)[5][100] = &balance; // Pointer initialization...
printf("%s\n",(*p)[0]); // Calling...
printf("%s\n",(*p)[1]); // Calling...
return 0;
}
出力は次のとおりです。
Subham
Messi
出来た...
あなたはこのようにそれを行うことができます:
uint8_t (*matrix_ptr)[10][20] = &l_matrix;