目次
はじめに: 最初のポインターでは、ポインターの概念について学びました。ポインタはアドレスを格納するために使用される変数です。アドレスはメモリ空間の一部を一意に識別します。同時に、ポインタには型があります。ポインタの型によって、ポインタの +-整数のステップ サイズが決まり、ポインタ逆参照操作の権限。次に、さまざまな種類のポインタについて理解していただきます。
1. 文字ポインタ
文字ポインタの一般的な形式は次のとおりです。
char* 変数名
文字ポインタの一般的な使用法
int main()
{
char ch = 'w';
char* pc = &ch; //pc就是字符指针
ch='a';
*pc = 'a';
return 0;
}
変数 ch の値は直接変更することもできますし、ポインタを介して変数 ch の値を変更することもできます。
int main()
{
char arr[]="abcdef";
//创建一个数组,用字符串来初始化这个数组
const char* p="abcdef"; //常量字符串
//本质是将字符串首元素的地址赋给指针变量p
}
本質は、文字列の最初の要素のアドレスをポインタ変数 p に割り当てることです。これは定数文字列であるため変更できません。そのため、*p が文字列を変更しないように const を追加します。
文字列の最初の要素のアドレスが p に格納されていることを出力して確認します。
int main()
{
const char* p = "abcedf";
printf("%s\n", p);
printf("%c\n", *p);
}
分析例:
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
str1 と str2 は、それぞれ独自の独立した領域を持つ 2 つの作成された配列であり、2 つの配列は同じ文字列を格納しますが、アドレスが異なります。str1 と str2 は配列名で、配列名は最初の要素のアドレスを表すため、str1! =str2
2. ポインタの配列
ポインタ配列を類推して理解する
整数配列 -- 整数を格納する配列
文字配列 -- 文字を格納する数値
ポインタの配列 - ポインタの配列
2.1 ポインタ配列の一般的な形式
int* arr1[10]; // 整数ポインタ配列
char* arr2[10]; //文字ポインタの配列
2.2 2次元配列を実現するポインタ配列シミュレーション
#include <stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5 }; //arr1 - 数组名 - 首元素地址 int*
int arr2[] = { 2,3,4,5,6 }; //arr2 - 数组名 - 首元素地址 int*
int arr3[] = { 3,4,5,6,7 }; //arr3 - 数组名 - 首元素地址 int*
int* arr[] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
作成された 3 つの配列はメモリ空間内で連続していないため、実際には 2 次元配列ではありません。
3. 配列ポインタ
配列ポインタを類推して理解する
整数ポインタ - 整数変数へのポインタ。つまり、整数変数のアドレスを格納するポインタ変数です。
文字ポインタ -- 文字変数へのポインタ。つまり、文字変数のアドレスを格納するポインタ変数です。
配列ポインタ -- 配列変数へのポインタ。つまり、配列変数のアドレスを格納するポインタ変数です。
3.1 配列ポインタの一般的な形式
int (*p)[10];
説明: p は最初に * と結合され、p がポインター変数であることを示し、次に 10 個の整数の配列を指します (指す型は int [10])。したがって、 p は配列を指すポインタであり、配列ポインタと呼ばれます。
注: [] の優先順位は * の優先順位よりも高いため、p が最初に * と結合されるように () を追加する必要があります。
3.2& アレイ名 VS アレイ名
配列名は通常、最初の要素のアドレスですが、2 つの例外があります。
1. sizeof(配列名) 配列名は sizeof() 内に単独で配置されます。配列名は配列全体を表し、計算は配列全体のサイズになります。
2.&配列名 ここでも配列名は配列全体を表しており、配列全体のアドレスが取り出されます。
3.3 配列ポインタの使用
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr; //把数组arr的地址赋值给数组指针变量p
return 0;
}
void Print(int(*arr)[5], int row, int col)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
printf("%d ", *(*(arr+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
Print(arr, 3, 5);
return 0;
}
コードの解釈:
printf("%d ", *(*(arr+i)+j));
*&arr==arr 最初の要素のアドレス
*(arr+i)==arr+i は行 i の最初の要素アドレスに相当し、+j は j 要素をスキップします
2 次元配列の各行は 2 次元配列の要素として理解でき、各行は 1 次元配列です。
配列名 arr は最初の要素のアドレスを示します。2 次元配列の最初の要素は 2 次元配列の最初の行であるため、ここで渡される arr は実際には最初の行のアドレスと等価です。 1 次元配列のアドレスであり、配列ポインタで受け取ることができます。
補充:
1 次元配列パラメータの受け渡し。仮パラメータの一部は配列またはポインタにすることができます。
void test1(int arr[5], int sz)
{}
void test2(int* arr, int sz)
{}
int main()
{
int arr[5] = { 0 };
test1(arr, 5);
test2(arr, 5);
return 0;
}
2 次元配列パラメータの受け渡し、仮パラメータの部分は配列またはポインタにすることができます
void test1(int arr[3][5], int r,int c)
{}
void test2(int (*p)[5], int r,int c)
{}
int main()
{
int arr[3][5] = { 0 };
test1(arr,3, 5);
test2(arr,3, 5);
return 0;
}
注: 仮パラメータは理解しやすいように配列の形式で記述されており、本質的にはポインタです。
4、配列パラメータ、ポインタパラメータ
4.1 1次元配列パラメータの受け渡し
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
int main()
{
int arr[10] = {0};
test(arr);
}
最初のパラメータは正しく、配列パラメータは配列によって受け取られますが、仮パラメータの本質はポインタです。要素数は省略可能です。
2 番目は正しく、配列名は最初の要素のアドレスを渡すので、ポインタで受け取ることができます。
void test(int* arr[20])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
int *arr2[20] = {0};
test(arr2);
}
最初のものは正しく、ポインタの配列をポインタの配列で受け取ることができます。
2 番目は正解です。変数の型はポインターの配列であり、アドレスは配列に格納されます。各要素の型は int* で、配列の名前は最初の要素のアドレスであり、実際のパラメータはポインタのアドレスなので、第 2 レベルを使用してポインタを受け取ります。
4.2 2次元配列パラメータの受け渡し
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
最初のものは正しく、配列のパスパラメータは数値で受け取られます。
注: 2 次元配列パラメータの受け渡しの場合、関数パラメータの設計では最初の [] 番号のみを省略できます。
2 次元配列の場合、行数を知る必要はありませんが、1 行に要素がいくつあるかを知る必要があるためです。
4番目は間違いで、2次元配列の配列名は1行のアドレス、型はint(*)[]となっており、int* arrでは受け取れません。
6 番目は間違っています。整数変数のアドレスを 2 次ポインターで受け取ることができません。
4.3 レベル 1 ポインタパラメータの受け渡し
関数のパラメータ部分が第 1 レベルのポインタの場合、関数の実パラメータで渡せるパラメータ
void test(int* p)
{}
int main()
{
int n = 10;
test(&n);
int* p = &n;
test(p);
int arr[5] = { 0 };
test(arr);
return 0;
}
4.4 第 2 レベルのポインタパラメータの受け渡し
関数のパラメータ部分がセカンダリポインタの場合、関数の実パラメータで渡せるパラメータ
void test(int** p)
{}
int main()
{
int n = 10;
int* p=&n;
test(&p);
int** pp = &p;
test(pp);
int* arr[5] = { 0 };
test(arr);
return 0;
}
今回の内容はここまでです。読んで何かを感じていただければ幸いです、読者の皆様、ご支援のほどよろしくお願いいたします。記事について質問がある場合は、コメント欄にメッセージを残してください。ブロガーは慎重に修正し、今後より良い記事を書く必要があります。