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的每个值是个数组名常量,指向一个字符数组
指针数组和数组指针
数组指针:[存储类型] 数据类型 (*指针名) [下标] =值
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]); } }

浙公网安备 33010602011771号