天天看點

c指針

1.變量與位址

指針就是位址,變量名是抽象出來的

2.指針與指針變量

指針(位址值,常量)

指針變量:變量的值存的是位址

3.直接通路與間接通路

4.空指針與野指針

5.空類型指針

6.定義與初始化的書寫規則

7.指針運算

8.指針與數組關系

指針與一維數組

指針與二維數組

指針與字元數組

9.const與指針

10.指針數組和數組指針

11.多級指針

指針

指針與變量的關系

int i 位址:0x1000 值:1

int p 位址:0x2000 值:0x1000

int q 位址: 0x3000 值:0x2000

i=1

&i=0x1000

p=0x1000

&p=0x2000

*p=*(0x1000)=1

指針占用大小一緻

指針變量要和它所指向變量類型要一緻(指針運算)

int * p=&i

#include <stdio.h>
#include <stdlib.h>


int main(){
    int i=1;
    int *p;
    p=&i;
    float *q;
    double *d;
    char *c;

    printf("sizeof(i)=%d\n",sizeof(i));//sizeof(i)=4
    printf("sizeof(p)=%d\n",sizeof(p));//sizeof(p)=8
    printf("sizeof(q)=%d\n",sizeof(q));//sizeof(q)=8
    printf("sizeof(d)=%d\n",sizeof(d));//sizeof(d)=8
    printf("sizeof(c)=%d\n",sizeof(c));//sizeof(c)=8
    printf("i=%d\n",i);//i=1
    printf("&i=%p\n",&i);//&i=0x7ffeb1349594
    printf("p=%p\n",p);//p=0x7ffeb1349594
    printf("&p=%p\n",&p);//&p=0x7ffeb1349588
    printf("*p=%d\n",*p);//*p=1

}      
#include <stdio.h>
#include <stdlib.h>


int main(){
    int i=1;
    int *p;
    p=&i;
    //int (*q)= &p; 
    int **q= &p;

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

}
------------
i=1
&i=0x7ffedb72ec5c
p=0x7ffedb72ec5c,&p=0x7ffedb72ec50,*p=1
q=0x7ffedb72ec50,&q=0x7ffedb72ec48,*q=0x7ffedb72ec5c
**q=1      

空指針 int *p = NULL; 0号位址不分給任何程式,當不确定時最好指向空

野指針 目前指針的指向是不确定的或者根本沒有指向

int main(){

int *p ;

*p=1;

printf("%p--->%d\n",p,*p );

}

會執行時

段錯誤 (core dumped)

空類型的指針

當不知道要存放什麼類型的指針時

能和任意類型指針做交換

void *p=NULL;

指針運算

*,& ,關系運算(位址值的高低),++,--

#include <stdio.h>
#include <stdlib.h>


int main(){
    int a[3]={1,2,3};
    //a是常量,p是變量
    int *p=a;
    //或者int *p=(int[3]){1,2,3};
    int i;
    for(i=0;i<sizeof(a)/sizeof(*a);i++){
        printf("%p=%d\n",a+i,*(a+i) );
        printf("%p=%d\n",p+i,*p+i );
    }
}
--------------------------------
0x7ffce7c84760=1
0x7ffce7c84760=1
0x7ffce7c84764=2
0x7ffce7c84764=2
0x7ffce7c84768=3
0x7ffce7c84768=3

a[i]=*(a+i)=*(p+i)=p[i];
&a[i]=a+i=p+i=&p[i];
----------------------------------------
int main(){
    int a[4]={4,1,3,7};
    int y;
    int *p=&a[1];

    y=(*--p)++;
    printf("y=%d\n",y );//y=4
    printf("a[0]=%d\n",a[0]);//a[0]=5
}      

 指針與二維數組

int main(){
    int a[2][3]={1,2,3,4,5,6};
    int i,j;
    int *p;
    //p=a    //警告:從不相容的指針類型指派 p在列上移動,a不是 a+1 =p+3
    p=*a;  //*(a+0)
    printf("p=  %p\n",p );


    printf("a=  %p\n*a=%p\na[0]=%p\na+1=%p \na[1]=%p\n",a,*a,a[0],a+1,a[1]);
    printf("---------------------\n");
    for(i=0;i<2;i++){
        for(j=0;j<3;j++){
            printf("%p->%d\n",*(a+i)+j,*(*(a+i)+j));
        }
    }

    printf("---------------------------\n");
    for(i=0;i<6;i++){
        printf("%p=%d\n",&p[i],p[i] );
    }
}
--------------------------------------------------
p=  0x7fffaaf11c10
a=  0x7fffaaf11c10
*a=0x7fffaaf11c10
a[0]=0x7fffaaf11c10
a+1=0x7fffaaf11c1c 
a[1]=0x7fffaaf11c1c
---------------------
0x7fffaaf11c10->1
0x7fffaaf11c14->2
0x7fffaaf11c18->3
0x7fffaaf11c1c->4
0x7fffaaf11c20->5
0x7fffaaf11c24->6
---------------------------
0x7fffaaf11c10=1
0x7fffaaf11c14=2
0x7fffaaf11c18=3
0x7fffaaf11c1c=4
0x7fffaaf11c20=5
0x7fffaaf11c24=6      

數組指針

int (*p)[3]

p+1 移動3個

#include <stdio.h>
#include <stdlib.h>


int main(){
    int a[2][3]={1,2,3,4,5,6};
    int i,j;
    int *p=*a;
    int (*q)[3]=a;

    printf("q=%p\n",q);
    printf("a=%p\n",a);
    printf("q+1=%p\n",q+1);
    printf("a+1=%p\n",a+1);
}
---------------------------------
q=0x7ffc8d5f2520
a=0x7ffc8d5f2520
q+1=0x7ffc8d5f252c
a+1=0x7ffc8d5f252c      

一二維數組的數組名特點

int main(){
    int a[]={1,2,3,4,5,6};
    int b[2][3]={1,2,3,4,5,6};

    printf("a\t=%p\n",a );
    printf("*a\t=%p\n",*a);
    printf("&a\t=%p\n",&a);
    printf("a+1\t=%p\t\n",a+1 );
     printf("&a+1\t=%p\n",(&a)+1 );

    printf("&a[0]\t=%p\n",&a[0] );
    printf("-----------------------\n");

    printf("b\t=%p\n",b );
    printf("*b\t=%p\n",*b );
    printf("&b\t=%p\n",&b );
    printf("b[0]\t=%p\n", b[0]);
}

---------------------------------
a    =0x7ffdd4a12cc0
*a    =0x1
&a    =0x7ffdd4a12cc0
a+1    =0x7ffdd4a12cc4    
&a+1    =0x7ffdd4a12cd8
&a[0]    =0x7ffdd4a12cc0
-----------------------
b    =0x7ffdd4a12ca0
*b    =0x7ffdd4a12ca0
&b    =0x7ffdd4a12ca0
b[0]    =0x7ffdd4a12ca0      

 a,&a指向同一塊位址

但他們+1後的效果不同,a+1是一個元素的記憶體大小(增加4),而&a+1增加的是整個數組的記憶體(這裡是int 大小*數組元素個數24)

&a取的是整個數組的位址,既數組名取位址等價于對數組取位址

其實a和 &a結果都是數組的首位址,但他們的類型是不一樣

a表示&a[0],也即對數組首元素取位址,a+1表示首位址+sizeof(元素類型)

&a雖然值為數組首元素位址,但類型為:類型 (*)[數組元素個數],是以&a+1大小為:首位址+sizeof(a)

數組名僅僅是“相當”于指針,而并非真的是指針,數組名是隻是個常量(一個值為數組首元素位址的常量),

是以不能進行++或者--運算,而常量更是無法取位址的,而之是以有&a,其實這裡的a的意義早已經不是當初那個數組

名了,它此時代表了整個數組

#include <stdio.h>
#include <stdlib.h>

int main(){
    char str[]="hello";
    char *p=str+1;
    puts(str);//hello
    puts(p);//ello

}
-------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(){
    char str[]="hello";
    char *str1="hello";

    printf("%d,%d\n",sizeof(str),strlen(str) );
    printf("%d,%d\n",sizeof(str1),strlen(str1) );

    strcpy(str,"world");//str隻指針常量,不可以str=world
    printf("str=%s\n",str);

    str1="world";
    //strcpy(str1,"world");  //段錯誤 (core dumped)
    printf("str1=%s\n",str1);
}

6,5
8,5
str=world
str1=world      
void main(int argc,char *argv[]){
    int i;
    char *str="abc";//char* str = {"abc"};表示先定義個字元串常量,并将其位址賦給str
    char str1[]="abc";//str1[] = "abc";會有一個額外的拷貝過程,即把常量區的 "abc"拷貝到棧記憶體去
    
    printf("str=%s\n",str);
    //printf("*str=%s\n",*str); //段錯誤
    printf("str1=%s\n",str1);
    //printf("*str1=%s\n",*str1);////段錯誤

}
/*
本質上來說,char *str定義了一個char型的指針,它隻知道所指向的記憶體單元,并不知道這個記憶體單元有多大,
是以:
當char *str = "abc";後,不能使用s[0]='a';語句進行指派,這是将提示記憶體不能為"written"
當用char str[]="abc";後,完全可以使用s[0]='a';進行指派,這是正常的數組操作

字元串中的所有字元在記憶體中是連續排列的,str 指向的是字元串的第 0個字元;
我們通常将第 0個字元的位址稱為字元串的首位址。
字元串中每個字元的類型都是         char                ,是以 str 的類型也必須是         char *                
*/      

 char str[]與char *str

最根本的差別是在記憶體中的存儲區域不一樣,字元數組存儲在全局資料區或棧區,第二種形式的字元串存儲在常量區。

全局資料區和棧區的字元串(也包括其他資料)有讀取和寫入的權限,而常量區的字元串(也包括其他資料)隻有讀取權限,沒有寫入權限 

記憶體權限的不同導緻的一個明顯結果就是,字元數組在定義後可以讀取和修改每個字元,而對于第二種形式的字元串,

一旦被定義後就隻能讀取不能修改,任何對它的指派都是錯誤的

我們将第二種形式的字元串稱為字元串常量,意思很明顯,常量隻能讀取不能寫入

#include <stdio.h>
int main(){
    char *str = "Hello World!";
    str = "I love C!";  //正确
    str[3] = 'P';  //錯誤

    return 0;
}      

這段代碼能夠正常編譯和連結,但在運作時會出現段錯誤(Segment Fault)或者寫入位置錯誤

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LEN 5
int main(){

    char *lan[LEN]={"php","js","java","node","python"};

    printf("lan=%p,&lan=%p\n",lan,&lan );             
    printf("lan[0]=%p,&lan[0]=%p\n",lan[0],&lan[0]);
    printf("lan[1]=%p,&lan[1]=%p\n",lan[1],&lan[1]);
    printf("lan[0]=%s\n",lan[0]);
    printf("lan[0][0]=%c\n",lan[0][0] );
    printf("sizeof(lan)=%d\n",sizeof(lan));
    printf("strlen(lan[0])=%d,sizeof(lan[0])=%d\n",strlen(lan[0]),sizeof(lan[0]));

    printf("lan[5]=%s\n",lan[5]);

}
-----------------------------------
lan=0x7ffd1897b4e0,&lan=0x7ffd1897b4e0
lan[0]=0x400728,&lan[0]=0x7ffd1897b4e0
lan[1]=0x40072c,&lan[1]=0x7ffd1897b4e8
lan[0]=php
lan[0][0]=p
sizeof(lan)=40
strlen(lan[0])=3,sizeof(lan[0])=8
lan[5]=(null) //null作為結束符,對比\n作為字元數組的結束符      

lan的每個值是個數組名常量,指向一個字元數組

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LEN 5
int main(){
    int i,j,k;
    char *tmp;

    char *name[LEN]={"php","js","java","node","python"};

    for(i=0;i<LEN-1;i++){
        k=i;
        for(j=i+1;j<LEN;j++){
            if(strcmp(name[k],name[j])>0){
                k=j;
            }
        }

        if(k != i ){
            tmp=name[i];
            name[i]=name[k];
            name[k]=tmp;
        }
    }

    for(i=0;i<LEN;i++){
        puts(name[i]);
    }
}      
c IT
上一篇: C指針
下一篇: c指針