目次
1. 文字ポインタ
1.1 文字ポインタの理解
文字ポインタの理解: 実際、これは非常に簡単です。他の型と比較するだけです。以下のコードを参照してください。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10; //定义一个整型变量
printf("%d\n", a);
int* p = &a; //将变量a的地址存放的整型指针变量p中
*p = 15; //对p解引用改变a地址的内容,即把15换成10
printf("%d\n", a);
printf("-------------\n");
char c = 'A'; //定义一个字符变量
printf("%c\n", c); //将变量c的地址存放的字符指针变量pc中
char* pc = &c; 对pc解引用改变a地址的内容,即把'A'换成'C'
*pc = 'C';
printf("%c\n", c);
return 0;
}
整数ポインタ型の使用法と同じで、ポインタは変数のアドレスを格納するために使用され、逆参照によりアドレスの内容が変更されますが、型が異なります。整数ポインタは整数型変数のアドレスを格納し、文字ポインタには変数型の文字アドレスが格納されます。
1.2 文字ポインタは文字列を格納します
文字ポインタが文字変数のアドレスを格納できることはわかっていますが、それが格納された文字列の場合はどうなるでしょうか?
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
char arr[] = "hello classmate!";
printf("%s\n", arr);
printf("%c\n",*arr);
printf("----------------\n");
const char* pstr = "hello classmate!";
printf("%s\n", pstr);
printf("%c\n", *pstr);
return 0;
}
上記のコードを分析すると、文字ポインタが文字列変数を格納する方法が、最初の要素のアドレスを示す配列の配列名と似ていることに驚くでしょう。上記のコードによると、具体的には次のように分割されます。上部と下部。
最初の部分: arr 配列名の文字配列が定義されます。配列名は配列の最初の要素のアドレスです。つまり、配列名 arr は文字 'h' のアドレスです。文字列を出力するときは、通常、文字列を印刷するには配列名を入力するだけで済みます。これは、最初の要素のアドレスが指定されている限り文字列の印刷が可能であることも示しています。arr は最初の要素のアドレスであるため、arr の逆参照は最初の要素になります。
2 番目の部分: 最初の部分と非常に似ていますが、文字列のアドレスを格納する pstr 文字ポインタを定義します。結果が出力され、pstr が「hello同級生」文字列全体のアドレスを格納しているのではなく、最初の部分のアドレスを格納していることがわかります。文字 'h' のアドレス。
文字列の出力と pstr 逆参照は上記の配列と同じなので、詳細は説明しません。なぜ const が付加されているかというと、pstr には定数文字列が格納されており、定数なので改ざんされないように const を付加しているためです。
1.3 文字ポインタの使用
まずコードを見て、自分で少し調べてください。
#include <stdio.h>
int main()
{
char str1[] = "hello friend.";
char str2[] = "hello friend.";
const char *str3 = "hello friend.";
const char *str4 = "hello friend.";
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 が配列の最初の要素のアドレスであることは誰もが知っています。その後、両方とも最初の文字 'h' のアドレスであることがわかり、str1 == str2 と考えるかもしれません。そんな幻想に惑わされないでください。str1 と str2 は 2 つの配列であり、メモリ内に 2 つの異なる空間を開きます。内容が同じであっても、アドレスが同じになることはできません。2 つの最初の要素のアドレスは当然異なるため、str1 は等しくありませんstr2に。
文字ポインタ str3 と str4 を見ると、文字 'h' のアドレスが格納されていますが、str3 と str4 はポインタであるためです。同じ文字列を指す場合、実際には同じメモリ ブロック (実際には同じブロック アドレス) を指します。簡単に言うと、str3 と str4 は同じブロック アドレスを使用します。
2. ポインタの配列
2.1 ポインタ配列の理解
ポインタの配列とは何ですか? 配列ですか? ポインタですか? 今後はこのように判断して、誰が後ろに配置されてもポインタ配列、配列が後ろに配置されるのでポインタ配列は配列、ポインタを格納するために使用される配列です。
#include<stdio.h>
int main()
{
int arr1[10]; //arr1存放了10个元素,类型是整型
char arr2[10];//arr2存放了10个元素,类型是字符型
int* arr3[10];//arr3存放了10个元素,类型是整型指针
char* arr4[10];//arr4存放了10个元素,类型是字符指针
return 0;
}
ポインタ配列の内容は非常に浅いため、この内容は理解できるはずであり、配列ポインタに焦点を当てます。
3. 配列ポインタ
3.1 配列ポインタの理解
配列は誰が後ろに置いたかの原則に従ってポインタであるため、配列ポインタはポインタです。それは何のためですか?私たちは知っています
int main()
{
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
return 0;
}
[ ] の優先順位は * の優先順位よりも高いため、p1 は最初に [10] と結合され、型は int* であることを覚えておく必要があります。これは、p1 がポインターの配列であることを意味します。
p2 が最初に * と結合されるように、p2が最初に追加されます () 。p は最初に * と結合され、p2 がポインター変数であることを示し、次に 10 個の整数の配列を指します。したがって、p2 は配列を指すポインタであり、配列ポインタと呼ばれます。
3.2 配列名と&配列名の違い
配列ポインタを理解する前に、配列名と & 配列名を区別できる必要があるためです。
int arr[10];
#include <stdio.h>
int main()
{
int arr[10] = {0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
配列名と&配列名で出力されるアドレスが同じであることがわかります。二つは同じですか?
もう一度次のコードを見てみましょう。
#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);
return 0;
}
操作の結果は次のようになります。
まず第一に、配列名を理解する必要があります。
配列名は配列の最初の要素のアドレスです。
例外が 2 つあります:
1. sizeof(配列名)。ここでの配列名は最初の要素のアドレスではありません。配列の要素、配列名は配列全体を表し、sizeof(配列名) は配列全体のサイズをバイト単位で計算します。
2. &Array name。ここで、配列名は配列全体を表し、&array name は配列全体のアドレスを取得します。
また、すべての場所の配列名は、配列の最初の要素のアドレスです。
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
return 0;
}
一般に、配列ポインタは 2 次元配列でパラメータを渡す場合によく使用されます。
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int(*arr)[5], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
print_arr1(arr, 3, 5);
printf("-----------------\n");
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 3, 5);
return 0;
}
実行中のコードは次のとおりです。
2 次元配列がパラメータとして渡される場合、関数は 2 次元配列と配列ポインタの両方を受け取ることができます。2次元配列の配列名は最初の要素のアドレスであり、最初の要素は変数ではなく1次元配列なので、配列ポインタを使用して受け取ることができます。
コードのようなもの:
#include <stdio.h>
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); 0を返します。}
3.4 配列パラメータ、ポインタパラメータ
3.5 1次元配列パラメータの受け渡し
配列と関数を定義し、関数呼び出し時に配列名を入れ、受信時に配列とポインタで関数を受け取ることができます。配列はメモリ上に連続的に格納されるため、配列名を渡すと最初の要素のアドレスが分かり、ポインタを使って最初の要素のアドレスを受け取ると配列全体を知ることができます。配列を含む配列名は基本的にポインタを受け取ります。
簡単な例を使用します。
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[10] = { 0 };
test(arr);
test2(arr2);
}
順番に答えてください:
main 関数は test 関数を呼び出し、arr 配列の名前を渡します。
void test(int arr[])//ok? arr は配列であり、配列で受け取ることができます。わかりました
{}
void test(int arr[10])//ok? 同様に、arr は配列であり、配列で受け取ることができます。わかりました
{}
void test(int* arr)//ok? 最初の要素のアドレスをポインタで受け取ることができます。わかりました{}
main 関数は test2 関数を呼び出し、arr2 配列名を渡します。
void test2(int* arr[20])//ok? arr2 はポインターの配列であり、ポインターの配列で受け取ることができます。わかりました
{}
void test2(int** arr)//ok? arr2 の最初の要素は第 1 レベルのポインターであり、第 2 レベルのポインターで受け取る必要があります。わかりました
{}
3.6 2次元配列パラメータの受け渡し
パラメータを 2 次元配列で渡す場合、実際には最初の要素のアドレスが渡され、最初の要素は 1 次元配列になります。配列ポインタを使用して 1 次元配列を受け取るか、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);
}
void test ( int arr [ 3 ][ 5 ]) // 過去に渡した1次元配列のアドレスを2次元配列でも受け取ることができます。わかりました{}void test ( int arr [][]) // 2次元配列は行を省略できますが、列を省略できません。よくない{}void test ( int arr [][ 5 ]) // 上記と同じ、OK{}void test ( int * arr ) // 渡された 1 次元配列アドレスなので、受け取るには配列ポインタを使用しますが、ok ではありません{}void test ( int* arr [ 5 ]) // 混乱しないでください、兄弟たち、これはポインタの配列であり、配列ポインタではありません。問題ありません。{}void test ( int ( * arr )[ 5 ]) // 配列ポインターであることを確認します。{}void test ( int ** arr ) // 第 2 レベルのポインタが受け取ることができるのは第 1 レベルのポインタであり、アドレスを直接受け取ることはできません / not ok{}
3.7 レベル 1 ポインタパラメータの受け渡し
関数のパラメータが第 1 レベルのポインタの場合、呼び出し時にどのような種類のパラメータを渡すことができますか?
1. 単一変数 address の場合、第 1 レベルのポインターのアドレスを受け取ることに問題はありません。
void my_printf(char* str) { printf("%c\n", *str); } int main() { char pc = 'K'; my_printf(&pc); return 0; }
2. 配列名。配列名は最初の要素のアドレスであるため、
void my_printf(char* str) { printf("%s\n", str); } int main() { char arr[] = "CSDN"; my_printf(arr); return 0; }
3. レベル 1 ポインタ。配列名のアドレスを受け取るには変数を使用します。このとき、変数は第 1 レベルのポインタなので、第 1 レベルのポインタはパラメータを渡し、第 1 レベルのポインタは受け取ります。
void my_printf(char* str) { printf("%c\n", *str); } int main() { char cp = 'J'; char* cpp = &cp; my_printf(cpp); return 0; }
3.8 第 2 レベルのポインタパラメータの受け渡し
1. 第 1 レベルのポインタのアドレス。
void my_printf(char** str) { printf("%c\n", **str); } int main() { char pc = 'A'; char* pcc = &pc; my_printf(&pcc); return 0; }
2. 二次ポインタ。
void my_printf(char** str) { printf("%c\n", **str); } int main() { char pc = 'B'; char* pcc = &pc; char** pccc = &pcc; my_printf(pccc); return 0; }
3. ポインタの配列。
void my_printf(char** str) { printf("%s\n", *str); } int main() { char* arr[] = {"abcdef"}; my_printf(arr); return 0; }
それでは、今日の内容はここまでです。もしジャッジを助けていただけるなら、ジャッジがあなたを3回連続でサポートしてくれることを願っています!!!、