C . 1パート1のポインター


導入


何も言うよりも👋,
ケースでは、バナーでは、この男は、アルゴリズムの概念のKhwarizmiの人である疑問に思います.
今後はもっと高度なシリーズをスタートさせてください.

ポインタ


ポインタ入門


Cプログラミング言語では、メモリを1 D配列として視覚化します.

私が電話するとき、私は実際に細胞の中にいます.
今どのように多くのセルが消費されますか?それは実装に依存します.多くの人はCharが常に1バイトでintが2バイトで、長さが4バイトだと思っていますが、そのように固定されていません.
どのプラットフォームが実行されているかによって、すべての変数が何らかのサイズになります.しかし、サイズが決められるとき、それは固定されます.しかし、Charがすべてのプラットホームで同じであることを修正できません.
ほとんどのシステムはcharを1バイトに実装しています.このプラットフォームでintが2バイトかかると仮定しましょう.

もう一つの方法は、アドレスを使ってアクセスすることです.つまり、あるメモリを与えられたすべてのエンティティがアドレスを持っている場合があります.名前があっても、アドレスを使ってアクセスするのが速いでしょう.あなたが名前でそれを呼び出すたびに、それは物理アドレスには、論理アドレスに変換されるので、任意の方法は、要素にアクセスするアドレスを指定する必要があります.したがって、時々名前よりむしろアドレスを使うのは便利です.我々が名前を使うならば、それは我々(それは人間として)を読むためにより速いです、しかし、機械アドレスのために、より簡単に実行できます.
そして、ここでポインタは一般的にアドレスを扱います、そして、あなたがどんなプログラムかどんなポインタでも実体を書くとき、ポインタはポインタを持たないプログラムより速く走るはずです.これは最初の利点です、そして、他の利点はダイナミックにつくられたデータ構造があなたが彼らをつくるとき、あなたが彼らを変えることができないということです.それで、ポインタはCプログラミング言語で重要な役割を演じそうです.
それで、ポインタについて話しましょう.ポインターとはポインタはいくつかの他の変数または他の実体のアドレスを含むと思われる変数です.すべてのシステムはメモリに対応するビットを持ち、ポインタはそのビット数を保持しなければならない.それで、再びポインタのサイズはプラットホームに依存するでしょう、いくつかのプラットホームは32ビット他の16ビットを持ちます.
char a;
int b;
int *p; //can be read as *p is a variable of type int .
p = &b; 
私たちは2演算子が1であり、他のです-と我々は第1部演算子&最高の社長を参照してくださいとして、この演算子は、Bのアドレスを取得するために使用され、変数のアドレスまたは他のデータ構造は、メモリ内で宣言されている他のデータ構造のみ.たとえば、式や定数のアドレスを取得できません.
このプラットフォームのアドレスが8ビット= 1バイトであると言うので、ポインタは1バイトだけかかります.

Bの開始アドレスが100であるとしましょう、Bが2バイトであるとしても、Pはまだアドレスを指しているので、1バイトであることができます、したがって、& Bは100のとき、Pの値は100です.サイズがintまたはcharまたは他の構造体であっても、ポインタのサイズは常に同じです.
私たちには、異なるサイズの家があります.
このように、データ構造がアドレスが常に同じであるということを覚えておいてください.要素のサイズでポインタのサイズをチェインしてはいけません.
しかし、サイズが重要でないならば、あなたは待つことができると思うことができます.それは本当です、しかし、アドレス算術が後でそれについて話すという問題があります.
私がPを使うならば、私が100で立っているように、そして、私がアドレスの中でアクセスする必要があるならば、私は* Pを使用する必要があります、そして、ここで、それはDifferenceまたはIndirection操作と呼ばれています.
& : address
* : dereferencing and indirection

私がP(またはB)だけを使っているならば、私がそれを見ているとき、私はバイオレット人であるつもりです.
だから今* pとbは同じです.


#include <stdio.h>

int main()
{
    int  x =  5;
    int *y = &x; // assume address of x is 0x7ffe5b67bf54

    printf("%d\n" ,  x); // output : 5
    printf("%p\n" , &x); // output : 0x7ffe5b67bf54
    printf("%p\n" ,  y); // output : 0x7ffe5b67bf54
    printf("%d\n" , *y); // output : 5
    printf("%p\n" , &y); // output : 0x7ffe5b67bf54

    return 0;
}
もう一つの例
void f(int *p , int *q)
{
    p  = q;
    *p = 2;
}
int i = 0 , j = 1;

int main() {
    f(&i , &j);
    printf("%d %d\n",i,j);
    return 0;
}

これは関数の開始前に可視化されます.P & IとQを指す

実行の後、両方は& Jを指しています、そして、プログラムは0

ポインタと関数


void swap(int x , int y) {
    int temp;
    temp = x;
    x    = y;
    y    = temp;
}

int main() {
    int a,b;
    a = 3;
    b = 4;
    swap(a,b);

    return 0;
}
我々はAとBを持っている主な機能では、彼らはメインのスタックになります

関数を値に渡したとき、関数の新しいスタックが作成されます

XとYはAとBとは異なります.なぜなら、それらはAとBの値だけを取りますが、それらのアドレスではなくメモリ内の異なるアドレスで占有されるからです.XまたはYへのどんな変化でも、それはAとBに影響を及ぼしません.
今このバージョンを見ましょう
void swap(int *x , int *y) {
    int temp;
    temp = *x;     // temp = 3
    *x   = *y;    // *x=a=4
    *y   = temp; // *y=b=temp=3
}

int main() {
    int a,b;
    a = 3;
    b = 4;
    swap(&a,&b); // assume &a=100 and &b=200

    return 0;
}

現在、Xはアドレス' A 'の値を持ちます、そして、Yはアドレス' B 'の値を持っています、したがって、* xは' a 'で、* yは' b 'です.XとYはまだAとBより異なったアドレスを持ちますが、XとYの値は' A 'と' B 'のアドレスです.
それで、Xのために私が作るどんな変化でも、それがyのために同じアドレスをここで修正している時から、' a 'に影響を及ぼします.
これは、関数からAとBにアクセスする方法です.

ポインタと配列


配列とポインタは意味が強く関係しています.配列や接尾辞を使えば、ポインタでそれを行うことができます.
私たちが5の要素を持っていることを意味しますので、5つのブロックの隣接するメモリ空間を割り当てることになります.つまり、100から100までのブロックの開始と、システムの整数が2バイトとなり、システムがバイトアドレス可能であるということを意味します.それで、単語が2バイトであるなら、それは2バイトごとにユニークなアドレスがあることを意味します.しかし、私たちは1バイトを仮定します.
それで、私たちの配列は10バイト(2 * 5)を必要とします

配列名は変数ではなく、アドレスを表現する別の方法です.したがって、メモリには名前が割り当てられません.しかし、[ 0 ] A [ 1 ] ...彼らは割り当てられるが、それ自身ではない.Aは100のセルの外側に立っているようです.* a a a = a [ 0 ]のように、セルの中に行きます.
だから今、私たちは*(a + 2)= a [ 1 ]に立っていると思いますが、それは間違っています.ここでは、アドレス算術と呼ばれる何かを持っています.アドレス算術はこのように働きます、それで、ポインタのどちらかの配列名に整数を加えるように試みるときはいつでも、追加はスケール追加です.
したがって、Aがここで100を表すように、+ 1が102の次の要素を表すので、A + Iが一般的にA + Iを書くとき、それは内部的に+(i * sizeof(int))に変換されます.したがって、私は、(a + 3)= a [ 3 ]のように、私はタイプAをタイプします.また、[ A [ 3 ] ] =& A [ 3 ]は、[ A [ 3 ] ]のようにそれを置く必要はありません.
ポインタ*を宣言した場合
int *p;
p = &a[0];  // p will point to address 100
p = p+1;    // p will point to address 102
p = p+1;    // p will point to address 104
どのように、コンパイラはそれを知っていますか?私たちは、すべてのポインタは何を指しているタイプのたびに固定サイズを持っていると言う前に覚えていますか?そういうわけで、私はポインタの前にintをタイプするとき、コンパイラが正しくそれを増やします.
pがアドレス100を持つので、私がp = p + 3をタイプするとき、それは106
*(p+3)=a[3]=p[3]
And
p+3=a+3=&a[3]
配列名があれば
a=a+1; // X
a++; // X
a=b; //X
Aが値でないので、それらのすべては許されません.しかし、他にはそれらが有効です
p=p+1;
p++;
p=variable;
pが変数であるので、これは配列とポインタの違いです
したがって、私は配列の名前を関数に渡すときはいつでも、私は開始アドレスを渡しているので、これは私たちが呼び出している関数のポインタを取り、そのポインタを使用することで、配列上で実行できる同じ操作を行うことができます.

ポインタ演算あるいはアドレス演算


有効なポインタ操作:
a )同じ型のポインタの割り当て:
2つのポインタが同じ型を持っていれば、EGを割り当てることができます.
int *p,*q; // same type
p=q; // legal
q=p; // legal
int *p , a[];
p=a;// allowed
a=p;// Now allowed hince a is not an variable
異なるタイプの2つのポインターを割り当てる必要があるならば、私たちはThread型キャスティングに行く必要があります.すべてのポインタが同じサイズのメモリを持っているということです.私たちはそれらを割り当てることができますが、それらに算術演算を行う必要があるときにコンパイラは混乱します.
char *c;
int *p;
p = (int*)c;
あなたは常にそれをキャストしなければなりません、そして、それは野生のポインタでなければなりません(他に何もNULLに初期化されません)、さもなければ許されません.
ポインタをintで追加または減算する

あなたがバウンスから出る場合を除き、追加と減算の問題がないはずです.エラーが発生します.
c )同じ配列の2つのポインタを差し引くか、比較する

2つのポインタが同じ配列を指しているなら、それらを比較することができます.また、それらを減算することができます、それらをシフトすることはできませんまたはそれらを追加します.PとQを含むPとQの間の要素数を取得する必要があるとしましょうので、すべてのセルが2バイトP - Qを2で割ったときには、PとQの間にQ - P + 1を書きます.
dをゼロに割り当てるか、比較する
ポインターのみをゼロに割り当てることができますが、意味がありません.唯一のケースは、ポインタが有効であるかどうかを確認する必要があるかどうかを調べます.