C言語学習ノート---上級ポインタ01

C言語ポインタ上級編1

はじめに:
ポインタとは何ですか?
ポインタは、アドレスを使用して、その値がコンピュータのメモリ内の別の場所に格納されている値を直接指すプログラミング言語のオブジェクトです。必要な変数ユニットはアドレスを介して見つけることができる
ため、アドレスはこの変数ユニットを指していると言えますしたがって、可視化されたアドレスを「ポインタ」と呼び、それをアドレスとしてメモリユニットを見つけることができます。また、通常の話し言葉で言うポインタは、通常ポインタ変数を指します。はメモリアドレスを格納するために使用される変数です。

/知識ポイントのまとめ/

1. ポインタ変数とは、メモリ空間を一意に識別するアドレスを格納する変数です
2. ポインタのサイズは 4/8 バイト(32 ビット/64 ビット)で固定です 3.
ポインタには型がありますポインタには型があり、型によってポインタ±整数のステップサイズとポインタ逆参照の許可が決まります
4. ポインタ演算(ポインタプラスマイナス整数:オフセット、ポインタマイナスポインタ:ポインタとポインタ間の要素数)ポインタ; ポインタの関係 操作: ポインタのサイズ比較)
詳細については、「ポインタの基本」を参照してください。

1. 文字ポインタ

ポインタはアドレスです。音声言語でのポインタはポインタ変数を指します
。整数ポインタ: 整数データへのポインタ。
文字ポインタ: 文字データへのポインタ変数。

#include <stdio.h>
int main()
{
    
    
	char ch = 'w';
	char* pc = &ch;

	char* p = "abcdef";//这里的字符串等价于表达式,然后返回的结果是第一个字符的地址(可理解为数组)
	//注意的是,这里的“abcdef”属于常量表达式,不可改变
	printf("%c\n","abcdef"[3]);//d
	//类似于数组名 --- “abcdef” ,所以可理解为数组形式

	const* pd = "abccdef";

	printf("%s\n", pd);//abcdef
	printf("%c\n", *pd);//a

	return 0;
}

(1)、ここでの文字列は式と同等であり、返される結果は
最初の文字のアドレスです (配列として理解できます)。 (2)、ここ
での「abcdef」は定数式であり、次のようにすることはできません。変化

質問例:

#include <stdio.h>
int main()
{
    
    
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char str3 = "hello bit.";
	const char str4 = "hello bit.";

	if (str1 == str2)
	{
    
    
		printf("1 to 2 same\n");
	}
	else
	{
    
    
		printf("1 to 2 not same\n");
	}

	if (str3 == str4)
	{
    
    
		printf("3 to 4 same\n");
	}
	else
	{
    
    
		printf("3 to 4 not same\n");
	}

	if (&str3 == &str4)
	{
    
    
		printf("&3 to &4 same\n");
	}
	else
	{
    
    
		printf("&3 to &4 not same\n");
	}
	return 0;
}

まとめ
(1)、str1とstr2は異なる空間アドレスなので等しくない
(2)、str3とstr4は同じ定数式を格納しているため、指すアドレスは同じで、取得される値も同じは同じですが、str3 と str4 のアドレス自体は異なります。

2. ポインタ配列

文字配列 – 文字を格納する配列
整数配列 – 整数を格納する
配列 ポインタ配列 – ポインタを格納する配列 配列に格納される要素はポインタ型です。

例:
int* arr[5];
char* ch[6];

2.1. ポインタ配列ルーチン 1 – 2 次元配列をシミュレートする

#include <stdio.h>
int main()
{
    
    
	int arr1[] = {
    
     1,2,3,4,5 };
	int arr2[] = {
    
     2,3,4,5,6 };
	int arr3[] = {
    
     3,4,5,6,7 };
	//int* int* 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;
}

2.2. ポインタ配列ルーチン2

#include <stdio.h>
int main()
{
    
    
	//指针数组
	char* arr[5] = {
    
    "hello bit","hehe","abcf","cojag","waofrdf"};
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%s\n",arr[i]);
	}
	return 0;
}

3. 配列ポインタ

ポインタ配列 - 配列、ポインタを格納する配列です 配列
ポインタ - ポインタ
文字ポインタ - 文字へのポインタ 整数ポインタ - 整数へのポインタ
浮動
小数点ポインタ - 浮動小数点数へのポインタ。

3.1. 配列名を確認しますか?

配列名は配列の最初の要素のアドレスですが、sizeof (配列名)、配列名という 2 つの例外があり、2 つの例外
の配列名は配列全体のアドレスを表します。

配列ポインタ - &arr は
最初の要素のアドレス値と同じですが、ポインタ演算を実行すると、配列全体のサイズがバイト単位で加算または減算されることがわかります。同時に、ポインタの型によってサイズが決まります
。バイト (ステップ サイズ) ポインタ + 1 +

#include <stdio.h>
int main()
{
    
    
	int arr[10];
	printf("%p\n",arr);
	printf("%p\n",arr+1);

	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);

	printf("%p\n", arr[0]);
	printf("%p\n", arr[0] + 1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);
	return 0;
}

3.2. 配列ポインタの定義と初期化(フォーマット)

#include <stdio.h>
int main()
{
    
    
	int arr[10] = {
    
     0 };
	//int* p = &arr;
	int(*p)[10] = &arr;//p是用来存放数组的地址的,p就是数组指针
	
	char* arr2[5];
	char* (*pc)[5] = &arr2;
	//(*pc)说明pc是一个指针类型,(*pc)[5],表示数组指针,char* 表示该数组类型属于字符指针类型
	
	//int arr3[] = { 1,2,3 };
	//int(*p3)[] = &arr3;
	//数组指针的[]操作符的参数必须指明,否则报错
	return 0;
}

3.3. 配列ポインタの役割 — 一般的に 2 次元配列で使用されます

#include <stdio.h>
int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	//数组指针
	int(*p)[10] = &arr;
	int i = 0;
	for(i=0;i<10;i++)
	{
    
    
		printf("%d ",(*p)[i]);
	}
	//指针变量
	int* p = &arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", p[i]);
	}
	return 0;
}

3.4. 配列ポインタ - 2次元配列の応用

2 次元配列の配列名は最初の要素のアドレスです。つまり、配列ポインタの最初の要素のアドレスは最初の行のアドレスです。

#include <stdio.h>
//二维数组传参也是二维数组的形式
//void print(int arr[3][5], int row, int col)//形参是数组形式
//{
    
    
//	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");
//	}
//}
void print(int (*p)[5], int row, int col)//形参是指针形式,指向第一行
{
    
    
	int i = 0;
	for (i = 0; i < 3; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < 5; j++)
		{
    
    
			printf("%d ",p[i][j]);
		}
		printf("\n");
	}
}
int main()
{
    
    
	int arr[3][5] = {
    
     {
    
    1,2,3,4,5},{
    
    2,3,4,5,6},{
    
    6,7,8,9,10} };
	print(arr,3,5);//二维数组传参,行,列
	return 0;
}

4. 配列パラメータの探索 - パラメータとしてのポインタパラメータ

4.1. 1次元配列パラメータの転送

#include <stdio.h>

void test(int arr[])//ok
{
    
    }
void test(int arr[10])//ok
{
    
    }
void test(int *arr)//ok
{
    
    }
void test2(int* arr[20])//ok
{
    
    }
void test2(int* *arr)//ok
{
    
    }
int main()
{
    
    
	int arr[10] = {
    
     0 };
	int* arr2[20] = {
    
     0 };

	test(arr);
	test2(arr2);
	return 0;
}

4.2. 2次元配列パラメータの転送

#include <stdio.h>

void test(int arr[3][5])//ok
{
    
    }
void test(int arr[][])//No,列不可以省
{
    
    }
void test(int arr[][5])//ok
{
    
    }
void test(int *arr)//No,不能以一维数组接收二维数组,因为二维数组的首元素地址是第一行的地址,而不是第一个元素的地址
{
    
    }
void test(int* arr[5])//No,类型不同,int* 与int
{
    
    }
void test(int (*arr)[5])//ok,数组指针,指向第一行
{
    
    }
void test(int **arr)//No,二级指针用于指向一级指针的地址
{
    
    }

int main()
{
    
    
	int arr[3][5];
	test(arr);
	return 0;
}

4.3. ポインタパラメータの受け渡し

第 1 レベルのポインタ パラメータの受け渡し:
第 1 レベルのポインタ パラメータを渡すとき、仮パラメータは第 1 レベルのポインタの形式で記述されます。

#include <stdio.h>
void print(int* p, int sz)//一级指针传参时,形参写成一级指针形式就可以了
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d\n",*(p+i));
	}
}
int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p传给函数,作形参
	print(p,sz);
	return 0;
}

思考の拡張 1 :
仮引数が第 1 レベルのポインタである関数に遭遇した場合、関数の実引数として何を渡すことができるかを考えてみましょう。
回答: 渡すことができる対応する型のアドレス/第 1 レベルのポインタ変数である限り、配列名と同様に問題ありません。

第 2 レベルのポインタ パラメータの転送:
第 2 レベルのポインタ パラメータが渡されるとき、仮パラメータは第 2 レベルのポインタの形式で書き込まれます。

#include <stdio.h>

void test(int** ptr)
{
    
    
	printf("num = %d\n",**ptr);
}

int main()
{
    
    
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	//test(&p);等价
	return 0;
}

思考の拡張 2 :
仮引数が第 1 レベルのポインタである関数に遭遇した場合、関数の実引数として何を渡すことができるかを考えてみましょう。
回答: 渡せるのは、対応する型の第 1 レベルのポインタ アドレス/第 2 ポインタ変数であれば問題ありません。また、第 1 レベルのポインタ型の配列名も OK です。

5. 関数ポインタ

配列ポインタ – 配列へのポインタ – 配列のアドレスを格納します – 配列名は配列全体のアドレスです
関数ポインタ – 関数へのポインタ – 関数のアドレスを格納します

関数のアドレスを取得するにはどうすればよいですか? それも&関数名経由でしょうか?
答え: & 演算子と関数名を追加するだけです。

5.1. 関数ポインタの初期化(フォーマット)

#include <stdio.h>

int Add(int x, int y)
{
    
    
	return (x + y);
}

int main()
{
    
    
	//&函数名就是函数的地址
	//函数名也是函数的地址
	printf("%p\n", &Add);
	printf("%p\n", Add);
	//均表示函数的地址

	//函数指针
	//int (*pf1)(int , int) = Add;//pf1就是函数指针变量
	int (*pf2)(int, int) = &Add;//pf2同样是函数指针变量

	int ret = (*pf2)(2, 3);
	//因为Add与&Add等价
	//常规写法:
	//int ret = Add(2,3);
	//所以*号就可以省略
	//int ret = pf2(2,3)
	printf("%d\n",ret);
}

6. 知識ポイントを統合する

6.1. 次のコードの説明 1

int arr[5];//arr是一个存放5个整型数据的数组
int* parr1[10];//parr1是一个数组,数组的10个元素类型是int*
int(*parr2)[10];//parr2是一个数组指针,该指针指向的数组,指向数组的10个元素类型是int
int(*parr3[10])[5];//parr3是一个数组,是存放数组指针的10元素的数组,存放的这个数组指针,指针指向的5个元素是int类型

6.2. 次のコードの説明 2

#include <stdio.h>
int main()
{
    
    
	(*(void(*)())0)();
	//整体是一个函数调用
	//(void(*)()函数指针类型
	//(void(*)()) 强制类型转换为函数指针类型
	//(*(类型)0)(); --- 所以这段代码表示:
	//调用0地址处的函数,这个函数没有参数,且返回值类型为void省略不写
	
	void (*signal(int, void(*)(int)))(int);
	//整体是一个函数声明
	//void(*)(int)函数指针作为参数
	//signal(int, void(*)(int)) --- 函数,一个参数是int类型,一个参数是函数指针类型,返回值类型也是作为函数指针类型
	//(*signal(int, void(*)(int))) --- 函数指针
	//(*signal(int, void(*)(int)))(int) --- 函数指针参数是一个int类型参数
	//void (*signal(int, void(*)(int)))(int) -- 一个返回值类型为空,参数是一个整型的函数指针的声明
	//所以这段代码表示:
	//是一个函数声明,声明的是signal函数,signal函数的参数有两个,一个是int类型,一个是函数指针类型,该类型是void(*)(int),该函数指针指向的函数,参数类型是int类型,返回值类型为void
	//signal函数的返回值类型也是函数指针类型,该类型是void (*)(int),该函数指针指向的函数,参数类型是int类型,返回值类型为void
	
	//利用typedef 对这段代码简化:
	typedef void(*pfun_t)(int);
	//typedef 对函数指针类型重命名时,被重名的变量名,放在*面,即pfun_t的位置
	pfun_t signal(int, pfun_t);
	return 0;
}

7. 結論

これで、C 言語ポインタの進歩の前半部分は終了です。
つづく...
半エーカーの角砂糖を開けるとすぐに、天窓と雲の影が一緒に残ります。
運河がどこでこれほど透明になることができるのでしょうか? それは、生きた水の水源があるからです。-朱熹(本を読んだ感想)

おすすめ

転載: blog.csdn.net/m0_69455439/article/details/132501166