高度な C 言語ポインタ (1)

目次

1. 文字ポインタ

2. ポインタの配列

 ポインタ配列を類推して理解する

2.1 ポインタ配列の一般的な形式

2.2 2次元配列を実現するポインタ配列シミュレーション

3. 配列ポインタ

 配列ポインタを類推して理解する

3.1 配列ポインタの一般的な形式

3.2& アレイ名 VS アレイ名

3.3 配列ポインタの使用

4、配列パラメータ、ポインタパラメータ

4.1 1次元配列パラメータの受け渡し

4.2 2次元配列パラメータの受け渡し

4.3 レベル 1 ポインタパラメータの受け渡し 

4.4 第 2 レベルのポインタパラメータの受け渡し


はじめに: 最初のポインターでは、ポインターの概念について学びました。ポインタはアドレスを格納するために使用される変数です。アドレスはメモリ空間の一部を一意に識別します。同時に、ポインタには型があります。ポインタの型によって、ポインタの +-整数のステップ サイズが決まり、ポインタ逆参照操作の権限。次に、さまざまな種類のポインタについて理解していただきます。

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;
}

今回の内容はここまでです。読んで何かを感じていただければ幸いです、読者の皆様、ご支援のほどよろしくお願いいたします。記事について質問がある場合は、コメント欄にメッセージを残してください。ブロガーは慎重に修正し、今後より良い記事を書く必要があります。  

おすすめ

転載: blog.csdn.net/2301_76207836/article/details/131613324