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的每个值是个数组名常量,指向一个字符数组

const与指针

指针数组和数组指针
数组指针:[存储类型] 数据类型 (*指针名) [下标] =值
int (*p)[3];
指针数组:[存储类型] 数据类型 * 数组名 [长度]
int * arr[3];

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

 

posted @ 2018-01-03 22:26  H&K  阅读(176)  评论(0)    收藏  举报