【Cアドバンス】※ポインタ(1)

コンテンツ

1.文字ポインタ

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;
    printf("%c\n", ch);  // w
    printf("%c\n", *pc); // w
    //所以*pc的内容 = ch的内容 = 'w';
    return 0;
}
  • もう一つの例:
int main()
{
	char arr[] = "abcdef";
	char* pc = arr;
	printf("%s\n", arr);  //abcdef
	printf("%s\n", pc);   //abcdef
	return 0;
}
  • 別の使用方法は次のとおりです。
     
#include<stdio.h>
int main()
{
	const char* pstr = "hello bit";  //这里是把一个字符串放到pstr指针变量里了吗?
	printf("%c\n", *pstr);  // h
	printf("%s\n", pstr);   // hello bit
    char* p = "abcdef";
    *p = 'w';  // err 编译器报错,最好在开始加上const修饰,这样若再修改*p编译器会直接报警,像上述的const char* pstr = "hello bit";一样
	return 0;
}

code const char * pstr ="hellobit。";

文字列helloビットが文字ポインタpstrに入れられていると誰もが考えるのは特に簡単ですが、/本質的に、文字列helloビットの最初の文字のアドレスはpstrに入れられます。

上記のコードは、定数文字列の最初の文字hのアドレスをポインタ変数pstrに格納することを意味します。

  • 次に、次のような面接の質問があります。
#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");
	}
	return 0;
  • 結果は次のとおりです。

  • ハハの理由:

ここでは、abcdefを格納するために2つの配列arr1とarr2を作成しました。次に、この文字列はメモリ内に2つのスペースを持っている必要があり、arr1とarr2の2つの異なる配列がabcdefを作成しました。もちろん、2つの異なる配列の配列名は異なる最初の要素、2つのアドレスが異なるスペースにある場合、2つの値は自然に異なるので、ハハ。

  • heheの理由:

2つの文字列abcdefはまったく同じであり、どちらも定数文字列であり、変更できません。2つのコピーをメモリに格納する必要はありません。メモリの使用を容易にするために、スペースを節約できます。 p1とp2の両方が同じスペースを指しています。開始位置のアドレス、つまり最初の文字のアドレス。つまり、p1 == p2なので、hehe。

2.ポインタの配列

  • 概念:ポインターの配列は、ポインターの配列です。
#include<stdio.h>
int main()
{
	int arr[10] = { 0 }; // 整形数组
	char ch[5] = { 0 };  // 字符数组
	int* arr1[5];  // 存放整形指针的数组 - 指针数组
	char* arr2[5]; // 存放一级字符指针的数组 - 指针数组
    char** arr3[5];// 存放二级字符指针的数组 - 指针数组
	return 0;
}
  • 使用する:
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int i = 0;
	int *arr[4] = { &a,&b,&c,&d };
	for (i = 0; i < 4; i++)
	{
		printf("%d ", *(arr[i]));  // 10 20 30 40
	}
	return 0;
}

上記のコードは理解を容易にするために書かれたものですが、少し低いため、実際の状況では同様のコードはほとんどありません。

正しい使用法は次のとおりです。

  • 文字ポインタの配列:
#include<stdio.h>
int main()
{
	char* arr[] = { "abcdef","qwer","zhangsan" };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}
  • 描画の説明char*arr [] = {"abcdef"、 "qwer"、 "zhangsan"};

  •  整数ポインタの配列
#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 };
	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");
	}
	return 0;
}
  • 描画の説明int*parr [] = {arr1、arr2、arr3};

3.配列ポインタ

   3.1、配列ポインタの定義

  • 配列ポインタはポインタですか?または配列?

答えは次のとおりです。ポインタ

私たちはすでに精通しています:

整数ポインター: int*pint;整数データを指すことができるポインター

浮動小数点ポインター: float*pf;浮動小数点データを指すことができるポインター

その配列ポインタは次のようになります。配列を指すことができるポインタ

  • 次のコードのどれが配列ポインタですか?
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
  • 説明:
int *p1[10]; // 指针数组
int (*p2)[10];// 数组指针

//解释p1
//p1先和[]结合,说明p1是个数组,数组有10个元素且每个元素类型是int*,所以p1是指针数组

//解释:p2
//p2先和*结合,说明p2是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p2是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p2先和*结合。以此确保p2是数组指针

   3.2、&アレイ名VSアレイ名

  • 次のアレイの場合:
int arr[10];

arrと&arrとは何ですか?

arrは配列の名前であり、配列の名前は配列の最初の要素のアドレスを表すことがわかっています。

では、&arr配列名は正確には何ですか?

  • コードの一部を見てみましょう:
#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    printf("   arr=   %p\n", arr);
    printf("&(arr[0])=%p\n", &(arr[0]));
    printf("  &arr=   %p\n", &arr);
    return 0;
}
  • 結果は次のとおりです。

配列名と配列名で出力されるアドレスが同じであることがわかります 

2つは同じですか?

  • 別のコードを見てみましょう。
#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    printf("        arr=  %p\n", arr);
    printf("      arr+1=  %p\n", arr + 1);
    printf("\n");
    printf("  &(arr[0])=  %p\n", &(arr[0]));
    printf("&(arr[0])+1=  %p\n", &(arr[0]) + 1);
    printf("\n");
    printf("       &arr=  %p\n", &arr);
    printf("   &arr + 1=  %p\n", &arr + 1);
    return 0;
}
  • 次のように実行します。

上記のコードによると、配列名arrが最初の要素のアドレスであることがわかっているため、arrと&arr [0]は同じであり、+1によって取得される後続のアドレスはそれに応じて等しくなります。実際、&arrとarrは、値は同じですが、意味は異なるはずです。

実際:&arrは配列のアドレスを表し、配列の最初の要素のアドレスではありません。(慎重に体験してください)

この例の&arrの型は次のとおりです。int(*)[10]、これは配列ポインタ型です

配列+1のアドレス。配列のサイズ全体をスキップするため、&arr + 1と&arrの差は40です。 

  • 別のコードセット:
int arr[10];
int* p = arr;
int(*p2)[10] = &arr;  //取出的是数组的地址,既然是数组的地址,就应该放到数组指针变量中
//p2就是一个数组指针

pは最初の要素のアドレスを格納し、p +1は1intをスキップし、*pはintデータの4バイトにアクセスします

p2はarr配列のアドレスを格納し、p2+1は配列全体をスキップします

* p2はarr配列にアクセスし、* p2はarrと同等であり、* p2は配列名であり、配列名は最初の要素のアドレスと同等であるため、*p2は基本的にarrの最初の要素のアドレスです。配列。

p <---> * p2 <----> arr

   p + 1 <---> * p2 + 1

   3.3、配列ポインタの使用

  • 配列ポインタは配列を指しているため、配列ポインタは配列のアドレスを格納する必要があります。コードを見てください:
#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
    //但是我们一般很少这样写代码,我们先用数组指针打印出10个元素看看
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *((*p) + i));  // 1 2 3 4 5 6 7 8 9 10
	}
    return 0;
}
char* arr[5];
char* (*pa)[5] = &arr;
//pa是指针变量的名字,(*pa)中*说明pa是指针,[5]说明pa指向的数组是5个元素,char*说明pa指向的数组的元素类型是char*
  • 配列ポインターを使用して1次元配列を印刷する上記の方法は、やや不要です。明らかに、forループを使用して印刷できますが、配列ポインターが使用されます。これは間違いなくおかしいです。(犬の頭は命を救う)
  • 配列ポインタは、次のように、少なくとも2次元配列を使用する方が便利です。
#include<stdio.h>
// 参数是数组的形式
void print1(int arr[3][5], int x, int y)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < x; i++)
	{
		for (j = 0; j < y; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
// 参数是指针的形式
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} };
	print1(arr, 3, 5); // arr - 数组名 - 数组名就是首元素地址
    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
	print2(arr, 3, 5);
	return 0;
}

  •  知らせ:
  • print2関数の仮パラメーターにポインターを使用するプロセス:

まず、print2関数の実際のパラメーターの配列名は、最初の要素の最初の行のアドレスであり、最初の行は1次元配列であるため、正式なパラメーターに渡されるアドレスは、 1次元配列なので、配列ポインタに配置する必要があります。ポインタは、それぞれがintである5つの要素を持つ配列を指します。したがって、void print2(int(* p)[5]、int x、int y)

  • print2関数の出力を印刷する4つの異なる方法は同等です。

配列の内容を出力する場合は、配列をトラバースし、最初に変数iを作成して、forループを使用する必要があります。pは行を指し、p + iは行iをスキップすることを意味します。このとき、添え字iのある行を意味し、*(p + i)を逆参照してこの行を検索します。これは、配列名を取得することと同じです。この行の、次に変数jを作成します。これは、同じforループです。この行を見つけました。この行の要素を見つけてから、jを追加し、添え字j *(p + i)で要素アドレスを見つける必要があります。 + j、次に括弧を再度囲み、*逆参照して行iと列jの要素を検索します。*(*(p + i)+ j)であり、次の3つの印刷方法は、次のコードと同様に、前の方法と同等です。

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
		printf("%d ", *(arr + i));
		printf("%d ", arr[i]);  
		printf("%d ", p[i]);
		// arr[i] == *(arr + i) == *(p + i) == p[i] 等价
	}
  • ポインター配列と配列ポインターを学習した後、次のコードの意味を確認して見てみましょう。
int arr[5]; // arr是一个5个元素的整形数组
int* parr1[10];// parr1是一个数组,数组有10个元素,每个元素的类型都是int*,parr1是指针数组
int(*parr2)[10];//parr2是一个指针,该指针指向了一个数组,数组有10个元素,每个元素的类型都是int,parr2是数组指针
int(*parr3[10])[5];//parr3是一个数组,该数组的有10个元素,每个元素是一个数组指针,该数组指针指向的数组有5个元素,每个元素是int

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

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

  • ポインタなし:
#include<stdio.h>
void test(int arr[3][5])//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); // 二维数组传参
	return 0;
}
  • ポインタ付き:
#include<stdio.h>
void test(int* arr)//ok?   不可 arr是个二维数组,二维数组的数组名表示第一行的地址,第一行是个一维数组,而int* arr是存放整形地址的
{}
void test(int** arr)//ok?  不可 数组名是首元素的地址,是第一行的地址,是数组的地址,数组的地址不能存到二级指针里,二级指针是存放一级指针变量的地址
{}
void test(int* arr[5])//ok? 不可
{}
void test(int(*arr)[5])//ok? 可 第一行的地址是5个整形的指针
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr); // 二维数组传参
	return 0;
}

   4.3.第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 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p,传给函数
	print(p, sz);
	return 0;
}
  • 関数のパラメーター部分が第1レベルのポインターである場合、関数はどのパラメーターを受け取ることができますか?
#include<stdio.h>
void test1(int* p)
{}

void test2(char* p)
{}
int main()
{
	int a = 10;
	int* p1 = &a;
	test1(&a); //可
	test1(p1); //可

	char ch = 'w';
	char* pc = &ch;
	test2(&ch);//可
	test2(pc); //可
	return 0;
}

   4.4.第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次ポインターの場合、どのパラメーターを受け取ることができますか?
void test(char** p)
{}
int main()
{
	char c = 'b';
	char* pc = &c;
	char** ppc = &pc;
	char* arr[10];
	test(&pc); // 可
	test(ppc); // 可
	test(arr);//Ok? 可
	return 0;
}

おすすめ

転載: blog.csdn.net/bit_zyx/article/details/122483665