高度なポインタの説明(1)

1.ポインタのレビュー
1.ポインタはアドレスを格納するために使用される変数であり、アドレスはメモリ空間の一部を一意に識別します
。2。ポインタのサイズは固定4/8バイト(32ビットプラットフォーム/ 64ビット)です。プラットホーム)

#include<stdio.h>
void test(int a[])
{
    
    
	printf("%d",sizeof(a)/sizeof(a[0]);
int main()
{
    
    
	int a[10]={
    
    0};
	test(a);
}

//若为32位平台,运行结果为1;64位则为2

3.ポインターにはタイプがあり、ポインターのタイプによって、ポインターの±整数のステップサイズ、および
ポインターが逆参照されるときの権限が決まり
ます。4 ポインターの操作。2。ポインターが進められ
ます。1 。文字ポインタ

3つの表現方法

#include<stdio.h>
int main()
{
    
    
	char ch='w';
	char* pc=&ch;
	return 0;
}
#include<stdio.h>
int main()
{
    
    
	char arr[]="abcdef";
	char* pc=arr;
	printf("%s\n",arr);
	printf("%s\n",pc);
}
#include<stdio.h>
int main()
{
    
    
	const char* p="abcdef"; //"abcdef"为常量字符串,注意不能修改
	printf("%c\n",*p);
	printf("%s\n:,p);	
}

次に質問を見てください

#include<stdio.h>
int main()
{
    
    
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	const char* p1 = "abcdef";
	const char* p2 = "abcdef";
	if (arr1 == arr2)
	{
    
    
		printf("hehe\n");
	}
	else
	{
    
    
		printf("haha\n");
	}
	if (p1 == p2)
	{
    
    
		printf("hehe\n");
	}
	else
	{
    
    
		printf("haha\n");
	}
	// 打印结果 haha
	//		   hehe	

ポインタは、文字列がメモリの定数領域に格納されているため(その内容は変更できません)、アドレスが同じであることを意味します。したがって、p1とp2の両方が文字列定数領域のアドレス
と配列表現を格納します定数領域に格納されます文字列をスタック領域の配列にコピーします(スタック領域のデータは変更できます)ので、2つの配列のアドレスは同じではありません。
一部の学生はの4つの領域を知らない可能性がありますメモリ。この記事を読むことができます。4ゾーンメモリモデル(スタック、ヒープ、コード、グローバル)をよりよく理解する

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* parr[] = {
    
     arr1,arr2,arr3 };
	// []的优先级要高于 *号,parr先与[]结合,说明parr是个数组,里面存的元素类型是int *类型
	int i = 0;
	for (i = 0; i < 3; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < 5; j++)
		{
    
    
			printf("%d ", *(parr[i] + j));
		}
	}
	printf("\n");
}
//打印结果  1 2 3 4 5
//         2 3 4 5 6
//         3 4 5 6 7

parr [i]は、arr1、arr2、arr3の最初の要素のアドレスを格納する3つのポインター変数に対応します。Parr
[i] + jは、配列内の各要素のアドレスを表し、内容は逆参照後に取得されます。

3.配列ポインター
配列ポインターはポインター、配列へのポインターです

int arr[10]={
    
    0};
int(*p)[10]=&arr;

説明:pは*と組み合わされ、pがポインター変数であることを示し、10個の整数の配列を指します。したがって、pは、配列ポインターと呼ばれる配列を指すポインター
です。ここで、優先順位に注意してください。 of []これは*記号よりも大きいため、pが最初に*と結合されるように括弧を追加する必要があります。

次に、紛らわしい問題を見てみましょう:
&array name vs array name

次の配列の場合

int arr[10];

arrと&arrとは何ですか?
arrは配列名であり、配列名は配列の最初の要素のアドレスを表します。
&arrとは何ですか?

次のコードを見てみましょう

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

結果から、配列名と&配列名で出力されるアドレスが同じであることがわかります
。2つは同じですか?

コードの一部を見てみましょう

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

上記のコードによると、&arrとarrの値は同じですが、意味は同じではないことがわかります。
実際、&arrは配列のアドレスを表し、配列の最初の要素のアドレスではありません(ここで注意深く理解してください)
配列のアドレスプラス1。配列全体のサイズをスキップして、&arr + 1と&arrの差が40になるようにします。

4.配列ポインタの使用

このコードを見てください

#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]); // *p==arr
	}
	printf("\n");
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(*p + i));
	}
	printf("\n");
}
// 1 2 3 4 5 6 7 8 9 10
// 1 2 3 4 5 6 7 8 9 10

ここで重要なのは、* p == arrを理解することです。配列のアドレスを逆参照することは、配列名を取得することと同じです。

#include<stdio.h>
void print2(int(*p)[5], int x, int y)
{
    
    
	int i = 0;
	for (i = 0; i < x; i++)
	{
    
    
		int j = 0;
		for (j = 0; j < y; j++)
		{
    
    
			printf("%d ", p[i][j]);
			printf("%d ", *(p[i] + j));
			printf("%d ", *(*(p + i) + j));
			printf("%d ", (*(p + 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} };
	print2(arr, 3, 5);
}

配列名は配列の最初の要素のアドレスであることがわかっているので、2次元配列名を渡すときにどのタイプのポインターを使用する必要がありますか?

実際、この2次元配列は1次元配列、各行は要素と考えることができます。私が書いた配列には3つの要素があり、各要素は1次元配列であり、最初の要素のアドレスはタイプint [5]の1次元配列のアドレス。したがって、パラメータを渡すときに配列ポインタを使用して配列のアドレスを受け取ります。

上記の4つの印刷方法は同等であり、最初の方法は理解しやすいので、次の3つの方法を見てみましょう。

*(p[i]+j)

pは最初の行の1次元配列のアドレス、p [i]は(i + 1)番目の行の配列アドレスの逆参照です。前述のように、配列アドレスの逆参照は配列を取得することと同じです。 nameであり、配列名は配列の各行の最初の要素のアドレスであるため、p [i] + jは各要素のアドレスであり、コンテンツを取得するために再度逆参照します。

*(*(p+i)+j)

pは、最初の行の1次元配列のアドレスです。前述のように、配列アドレスは1ずつ増加し、配列サイズ全体をスキップします。(p + i)は、それぞれの1次元配列のアドレスを表します。逆参照後の行*(p + i)1次元配列の各行の配列名、つまり1次元配列の各行の最初の要素のアドレス*(p + i)+ j各要素のアドレスを表し、内容は参照解除後に取得されます

上記の2つの比較から、見つけるのは難しくありません
。p[i]は*(p + i)と同等です。

(*(p + i))[j]

*(p + i)は、1次元配列の各行の最初の要素のアドレスを表し、2次元配列の各要素もこの方法で取得できます。

おすすめ

転載: blog.csdn.net/DR5200/article/details/112299601