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