C言語コレクション| 02-C言語の魂(ポインター)

ドキュメントのバージョン 更新時間 コンテンツを更新する
v1.0 2020-09-13 最初のドラフトが完了しました

1.ポインターの重要性

  • いくつかの複雑なデータ構造を表す
  • データをすばやく転送し、メモリ消費量を削減(アドレスを直接送信)
  • 関数が複数の値を返すようにする
  • ハードウェアに直接アクセスできる
  • 弦を簡単に扱える

第二に、ポインターの定義

1.アドレスとは

アドレスはメモリユニットの番号であり、ゼロから始まる非負の整数であり、CPUのアドレスバスの幅がアドレス指定範囲を決定します。

2.ポインターとは

ポインタはアドレスであり、アドレスはポインタです。

3.どのポインタ変数

アドレス/ポインタを格納する変数。

通常、ポインター変数は、使用時に略してポインターと呼ばれますが、ポインター変数とポインターは同じものではありません。

4.ポインタ変数が占めるバイト数

ポインタ変数は、変数が指すバイト数に関係なく、ポインタ変数自体が占めるバイト数は、CPUアドレスバスの幅です

  • 32ビットCPUの場合:ポインター変数は4バイトを占有します。
  • 64ビットCPUの場合:ポインター変数は8バイトを占有します。
/**
 * CPU:64bit
 * OS:Windows10
 * IDE:Clion
 * Compiler:MinGW-64
*/

#include <stdio.h>

int main() {
    
    

    printf("sizeof(void *):%d\r\n", sizeof(void *));

    printf("sizeof(char *):%d\r\n", sizeof(char *));

    printf("sizeof(int *):%d\r\n", sizeof(int *));

    printf("sizeof(long *):%d\r\n", sizeof(long *));

    printf("sizeof(float *):%d\r\n", sizeof(float *));

    printf("sizeof(double *):%d\r\n", sizeof(double *));

    return 0;
}

実行結果は次のとおりです。

sizeof(void *):8
sizeof(char *):8
sizeof(int *):8
sizeof(long *):8
sizeof(float *):8
sizeof(double *):8

第三に、ポインターの分類

1.基本型ポインター

①ポインターを定義します。

int *p = NULL;

変数pが定義されています。pの型は、int型の変数のアドレスint *格納する型です

ポインターはハードウェアにアクセスできるため、危険を防ぐために、ポインターのデフォルト値には通常NULLが割り当てられます。

NULL0の番号が付けられたアドレススペースを示します。このアドレスでは書き込みも読み取りも許可されていません。

②ポインターの割り当て:

int i = 99;
p = &i;

呼び出されるポインター変数pにint型変数iのアドレスを格納します。ポインター変数pは変数iを指します

③ポインタを使用:

int j;
j = *p;	//执行之后j的值为99

* pは、アドレスがpの内容である変数を意味します。

2.ポインターと配列

2.1。ポインターと1次元配列

①1次元配列名:1次元配列の最初の要素のアドレスを格納するポインタ定数です。

②下付き文字とポインタの関係:p [i] = *(p + i)

③関数が配列を処理する場合、少なくとも2つのパラメーターを受け取る必要があります。配列の最初の要素のアドレスと配列の長さです。

2.2。ポインタ変数の操作

①ポインター変数を追加、乗算、または分割できない

②2つのポインタ変数が同じ連続メモリ(配列)を指す場合のみ、それらを減算して、2つのアドレスの差を示すことができます。

2.3。ポインタ配列と配列ポインタ

ポインタ配列:ポインタ変数の配列で、各要素はポインタ変数です。

int *a[10];

配列ポインター:配列の名前である配列を指すポインター変数を表します。

int a[10];
int *p = a;

3.ポインターと構造

4.ポインターと関数

4.1。関数ポインタとは

関数を定義する場合、関数名はアドレスであるため、最初にポインター変数を定義して関数のアドレスを保存すると、このポインター変数が関数ポインターになります

4.2。関数ポインタの役割

  • コールバック関数として使用:ユーザーは関数をコールバック関数として実装できます。
  • 抽象レイヤードデザイン

4.3。関数ポインタの使用

①定義方法:

int (*add)(int a, int b);

②使い方:

int my_add(int a, int b)
{
    
    
	return a+b;
}
int main()
{
    
    
	add = my_add;
	printf("myadd result is:%d\r\n", my_add(1,1));
	printf("add result is:%d\r\n", add(1,1));
	
	return 0;
}

実行結果は次のとおりです。

myadd result is:2
add result is:2

5.マルチレベルポインター

int i = 10;

①レベル1ポインター:

int *p = &i;

評価済み:pは、変数型のアドレスint *を格納するポインター変数pのint型です。

②二次ポインタ:

int **q = &p;

感謝:qはint **ポインター変数のタイプですqはint *変数タイプのアドレスを格納するために使用されます。

③3レベルポインター:

int ***r = &q;

感謝:rはint ***ポインター変数の型ですrはint **変数型のアドレスを格納するために使用されます。

④使用:

printf("i = %d\r\n", ***r);

サンプルプログラムは以下のとおりです。

/**
 * CPU:64bit
 * OS:Windows10
 * IDE:Clion
 * Compiler:MinGW-64
*/

#include <stdio.h>

int main() {
    
    

    int i = 10;

    int *p = &i;

    int **q = &p;

    int ***r = &q;

    printf("i = %d, &i = %p\r\n", i, &i);

    printf("p = %p, &p = %p, *p = %d\r\n", p, &p, *p);

    printf("q = %p, &q = %p, *q = %p, **q = %d\r\n", q, &q, *q, **q);

    printf("r = %p, &r = %p, *r = %p, **r = %p, ***r = %d\r\n", r, &r, *r, **r, ***r);
    
    return 0;
}

実行結果は次のとおりです。

i = 10, &i = 000000000061FE1C
p = 000000000061FE1C, &p = 000000000061FE10, *p = 10
q = 000000000061FE10, &q = 000000000061FE08, *q = 000000000061FE1C, **q = 10
r = 000000000061FE08, &r = 000000000061FE00, *r = 000000000061FE10, **r = 000000000061FE1C, ***r = 10

4番目に、void *ポインターの魔法の効果

①ボイドポインタを表す型なしポインタをので、空隙型ポインタは、ポインタの任意の型に変換することができます。

たとえば、一般的なAPI関数を設計する場合、パラメーターをvoid *に設定できるため、ユーザーがそれを呼び出すと、int型のポインターまたは構造体型のポインターを渡すことができますが、これにはユーザーがそれを呼び出す必要もあります。自分のポインタのタイプを指定します。

②void *ポインタには型がないため、*演算子を使用して操作したり、逆参照したりすることはできません

5、動的メモリの割り当てと解放

1.スタティックメモリとダイナミックメモリ

  • 静的メモリはスタックに割り当てられ、システムによって割り当てられ、システムによって解放されます。
  • 動的メモリはヒープに割り当てられ、手動で適用され、手動で解放されます。

2.動的メモリ使用量

例として、動的配列の構築を取り上げます。

#define NUM	10
int *ptr = (int *)malloc(NUM * sizeof(int));

malloc関数は、システムから動的メモリを適用するために使用されます。パラメータは、要求されたメモリのサイズ(単位:バイト)です。アプリケーションが成功すると、メモリアドレスが返され、アプリケーションが失敗すると、NULLが返されます。

それを使用した後、解放するためにfree関数を使用します:

free(ptr);
ptr = NULL;

3.静的配列と動的配列の比較

静的配列(従来の配列)の欠点は次のとおりです。

  • サイズは固定されており、定数ではなく、整数のみを使用できます。
  • プログラムの実行中、プログラムの終了後、メモリは常に占有されて解放されます。
  • 配列の長さが定義されると、再度変更することはできません。
  • 関数で定義された配列は、関数が戻った後に破棄され、他の関数は再び使用できません。

動的メモリを使用する動的配列は、上記の4つの欠点を解決します。

  • 任意のサイズで、プログラムの実行中に動的に適用され、動的に変更できます。
  • 要求されたメモリが解放されない(無料)限り、すべての機能を使用できます。

おすすめ

転載: blog.csdn.net/Mculover666/article/details/108559915