初識指針
一、什么是指針?
在了解指針之前先要先弄清楚地址的概念。
如果在程序中定義了一個變量,在對程序進行編譯時,系統就會給這個變量分配內存單元。編譯系統根據程序中定義的變量類型,分配一定長度的空間。
例如:整型變量分配4個字節,字符型分配1個字節,單精度分配4個字節等。內存區的每一個字節有一個編號,這就是“地址編號”,它就相當於旅館中的房間號,每一個房間都可以看作一塊內存區域,都可以用來存放東西,我們給每個房間都編一個房間門牌號,用於更好的區分每一個房間,內存中也是一樣的,整個內存由很多個字節組成,每個字節都有其對應的“房間號”,這就是“地址”了。通過這個“房間號”就可以找到其對應的“房間”,然后就可以從房間里取東西,或者把東西放進房間里了。
理解了地址的概念之后,那所謂的指針,就是內存地址,也就是地址的編號,可以把“指針指向地址”理解成“用小本本把房間號記下來”,那這個小本本就相當於一個用於記房間號的指針了,一個變量的地址稱為此變量的“指針”。
二、指針常量與指針變量
1、指針常量
之前有了解過不同數據類型的變量所占內存字節數的這個概念,那么系統在編譯時給一個變量分配的內存地址就稱為此變量的“指針”,這個指針的指向是無法改變的,所以又稱為指針常量,數組的地址也是指針常量(也稱為地址常量)。
int a = 10;
//&a = 10000; //&a取變量a的地址,是一個地址常量,不能夠改變它的位置
scanf("%d",&a); //輸入222
printf("a=%d\t &a=%p/n",a,&a); //輸出地址
2、指針變量
(1)指針變量的概念
如果有一個變量專門用來存放另一個變量的地址,則稱這個變量為“指針變量”,也就是說C語言中有一類變量是專門用來存儲(指向)地址的,我們將它稱為“指針變量”,指針變量的中存儲的地址可以被改變,也就是可以改變指針變量的指向,就好比一張紙或一個小本本,寫着一個房間的房間號,那把這個房間的房間號擦掉,寫上另一個房間的房間號也是可以的,這就是指針變量和指針常量最大的區別所在了,可以改變指針變量的指向。
//定義指針變量格式:數據類型 *變量名;
int* p;
(2)指針變量的定義
定義指針變量的一般格式:
類型名* 指針變量名;
int a= 10;
int* p; //定義了一個整型指針變量p,可以用於指向整型數據的地址(里面保存的是整型變量的地址)
p = &a; //將整型變量a的地址賦值給整型指針變量p,整型指針p指向了整型變量a的地址。
int*p,*q;
char* p1,*q1;
double *p2,*q2;
/* 注意:左端的int、char等是在定義指針變量時必須指定的“基類型”。指針變量的基類型用來規定此指針變量可以指向的變量的類型。如:上面定義的*p和*q只能用於指向int整型變量的地址,p2和q2只能用於指向double雙精度類型變量的地址。 */
char *q;
int *p;
printf("%d %d\n",sizeof(p),sizeof(q)); //都輸出為4,指針變量所占內存大小一般是占4個字節
(3) 指針變量的引用:
int* p; //定義了一個整型指針變量p,這里的'*'是指針變量定義符
*p; //*p解引用,這里'*'是解引用符號
int a=10;
int *p;
p = &a;
printf("*&a=%d"); //對a的地址進行解引用,輸出為10,。
printf("&p=%p "); //輸出為指針變量p的地址(p自己的地址,不是存儲其他變量地址的地址)
與指針和地址相關運算符:’*’(指針運算符)和’&’(取地址運算符) //區別對待位運算符&
//例如:int a,*p; p=&a; *p=10;
//在引用指針變量時,有以下幾種情況:
//1、給指針變量賦值
如:int a = 10,b=20;
int *p=&a;
//定義一個整型指針變量p,初始化p的值為a的地址,也就是p指向a地址
/*解引用:*/
*p=30; //通過指針變量p引用a變量,改變a的值為30
//這里的’*’為解引用符號,*p引用指針變量p所指向地址中對應的值
scanf(“%d”,p); //scanf通過指針變量p給變量a賦值
printf(“%d\n”,*p); //通過指針變量p輸出變量a的值
*p=b; //將b的值放入指針變量p所指向的內存地址中(a的地址單元中)
p=&b; //改變指針p的指向,指針p不再指向a的地址了,而是指向b的地址
printf(“%d\n”,*p); //輸出變量b的值
//輸出內存地址編號
printf(“%p\n”,p); //以十六進制的格式輸出指針變量p所指向地址的內存地址編號
printf(“%d\n”,&a); //以十進制的格式輸出變量a所在的內存地址編號
printf(“%o\n”,&b); //以八進制的格式輸出變量b所在的內存地址編號
printf(“%p\n”,&p); //以十六進制的格式輸出指針變量p所在的內存地址編號
三、指針變量作為函數參數
函數的參數不僅可以是整型、浮點型、字符型的數據,還可以是指針類型。它的作用是將一個變量的地址傳遞到另一個函數中。
1、函數參數為指針類型的函數:
//1、函數參數為指針類型的函數:
void fun1(int x,int y)
{//這里定義了一個普通函數fun1
printf(“x=%d\ty=%d\n”,x++,y++);
}
void fun2(int *x,int *y)
{//這里定義了一個形參為整型指針類型函數fun2,其形參為指針類型的變量
printf(“x=%d\ty=%d\n”,(*x)++,(*y)++); //注意*和++的優先級,++的優先級高
//注意:和printf(“x=%d\ty=%d\n”,x++,y++);的區別,也就是沒有*和有*的區別
//沒有*就是指針的偏移,指針本身移動
}
int main()
{
int a=10,b=20,*p,*q;
fun2(&a,&b); //這里調用函數fun時,所傳遞的實參必須是地址
printf(“a=%d\tb=%d\n”,a,b); //輸出的a和b的值為11,21
p=&a; //使用整型指針變量p指向整型變量a的地址
q=&b; //使用整型指針變量q指向整型變量b的地址
fun2(p,q); //這里使用指針變量p和q作為實參傳遞
printf(“*p=%d\t*q=%d\n”,*p,*q);
return 0;
}
2、指針函數
//返回值為指針類型的函數稱為“指針函數”:
int *fun3(int *x) //這是一個指針函數,返回值類型為整型int指針類型
{//int* x = &a;
++*x;
return x; //返回指針變量x所指向的內存地址
}
//調用
int a=10;
int* p;
p=fun3(&a);
printf("a=%d\t"); //輸出11;
int *fun4()
{
int a=10; //局部變量:出了作用域,內存揮被釋放
//如果加static,出了作用域,其內存不會被釋放,但是只能在作用域范圍內使用
return &a; //返回a內存地址
//注意:不允許返回局部變量的地址
}
int* p;
p=fun4(); //p=&a;使用了非法空間
四、通過指針引用數組
1、數組元素的指針
數組元素的地址表示:
如:int a[10] = {0,1,2,3,4,5,6,7,8,9},*p;
&a[0]; //引用數組元素a[0]地址的表示方法
2、指針指向數組元素
p=&a[1]; //指針變量p指向數組元素a[1]的地址
3、指針指向的移動(指針的偏移)
int a[10] = {0,1,2,3,4,5,6,7,8,9},*p;
p=&a[0];
++*p; //指針指向地址中的數值加1
printf(“%#p\n”,p); //打印指針變量p所指向的地址編號
p++; //指針移動到數組元素a[1]的位置
printf(“%#X\n”,p); //打印移動后指針變量p所指向的地址編號
//指針變量++(或--)移動一次是移動其基類型大小的內存區域
for(int i=0;i<10;i++)
{
printf(“%d”,*(p+i)); //通過指針移動引用數組元素,輸出數組
//指針本身位置不改變
}
for(int i=0;i<10;i++)
{
printf(“%d”,p[i])); //通過指針帶下標的形式引用數組元素
//[]相當於*解引用
}
for(int i=0;i<10;i++)
{
printf(“%d”,*(a+i)); //通過數組首地址帶下標的形式引用數組元素
//這里不能使用*a++,數組的首地址是一個地址常量,其指向不能被改變
}
for(int i=0;i<10;i++)
{
printf(“%d”,*p++); //指針本身的偏移(指針指向的改變)
//如果使用指針本身偏移后,再使用*(p++)就指向數組外面未知區域去了
}
4、指針指向字符串
char *p = “abcdefg”; //通過字符指針直接引用字符串
//字符串是一個字符數組,其首地址是地址常量
while (*p)
{
printf("%c\t", *(p++));
}
int* p;
char* str = "abcdefg1234567890";
p = (int*)str; //將str強轉成int*類型
for(int i=0;i<strlen(str);i++)
{
printf(“%d”,*(p+i));
}
//輸出ae26 +亂碼,因為p指針一次移動4個字節,不是一個1字節