- 程序設計基礎教程:C語言
- 常東超 劉培勝 郭來德等編著
- 3238字
- 2020-05-07 11:49:30
2.9 變量的地址和指針型變量
2.9.1 變量的地址和指針型變量的概念
(1)內存地址
在程序中,一個變量實質上代表了“內存中的某個存儲單元”,最基本的存儲單位是字節,而且每個字節都有一個編號,這個編號就稱為內存地址。就像旅館的每個房間號一樣,如果沒有房間號,旅館的工作人員就無法進行管理。同樣道理,沒有內存字節的編號,系統就無法對內存進行管理。內存的存儲空間是連續的,因此內存地址也是連續的。一般內存地址用十六進制數來表示。
(2)變量的地址
若在程序中定義了一個變量,C編譯系統就會根據所定義的變量類型,為其分配一定字節數的內存空間(在VC++中short int型數據占2字節,int型數據和float型數據占4字節,double型數據占8字節,char型數據占1字節,指針變量占4字節),此后這個變量的地址也就確定了。
例如:
short a;
float b;
char c;
這時系統為變量a分配兩個字節的連續存儲單元,為變量b分配四個字節的連續存儲單元,為變量c分配一個字節的存儲單元。如圖2.5所示,圖中的數字只是示意的字節地址。每個變量的地址就是該變量所占存儲單元的第一個字節的地址。

圖2.5 變量在內存中所占字節的地址示意圖
在這里我們稱變量a的地址為2019H,變量b的地址為1128H,變量c的地址為5050H。
一般情況下,我們在程序中只需指出變量名,無需知道每個變量在內存中的具體地址,每個變量與具體地址的聯系由C編譯系統來管理。程序中對變量進行存取操作,實際上就是對該變量所在的存儲單元進行操作,這種直接按變量的地址存取變量值的方式稱為“直接存取”方式。
(3)指針型變量
在C語言中,還可以定義一種特殊的變量,這種變量只是用來存放內存地址的。如圖2.6所示,假設有一個變量p,它也有自己的地址(假設2001H)。若將變量a的地址(假設5050H)存放到變量p中,也就是說變量p的值就是5050H,這時要訪問變量a所代表的存儲單元,可以先找到變量p的地址(2001H),從中取出變量a的地址(5050H),然后去訪問以5050H為首地址的存儲單元。這種通過變量p間接得到變量a的地址,然后存取變量a的值的方式稱為“間接存取”方式。這種用來存放地址的變量稱為“指針型變量”,上述變量p就是指針型變量。

圖2.6 存放內存地址的指針型變量示意圖
以后我們會經常提到指針指向某個變量,其含義就是指針變量的值是某個變量的地址。圖2.6中,我們可以說指針p指向了變量a。
2.9.2 指針型變量的定義和指針變量的基類型
定義指針變量的一般形式如下:
類型名 *指針變量名1,*指針變量名2,…;
例如:
int *p,*q;
float **r;
以上定義語句中,p和q都是用戶標識符。在每個變量前的星號*是一個說明符,用來說明變量p和q是指針變量,如果省略了星號*,那么變量p和q就變成了整型變量了。p和q前面的int用來說明指針變量p和q所指向的存儲單元中只能存放int型數據,這時我們稱int是指針變量p和q的基類型。或者我們可以通俗地稱p和q是兩個整型指針,而且p和q屬于一級指針(即指針變量p和q存放的是不同變量的地址)。
以上定義語句中的“float **r;”,其中r是浮點型指針變量,屬于二級指針,也就是說指針變量r所指向的是一個一級指針變量,這個一級指針變量所指向的是一個float型的變量。
再如:
void *p;
其含義是:定義了一個指針型變量p,它所指的存儲單元所存放的數據類型不定,稱為無類型指針。
定義指針變量一定要區分基類型,因為對于不同基類型的指針變量在進行指針運算時的“跨度”是不同的,在以后的章節中會體會到。另外不同基類型的指針變量不能混合使用。
2.9.3 給指針變量賦值
一個指針變量可以通過不同的方式獲得一個確定的地址值,從而指向一個具體的對象。
(1)通過求地址運算符(&)獲得地址值
單目運算符“&”用來求對象的地址,其結合性為從右到左。
int a=3,*p;
則通過以下賦值語句:
p=&a; /*給指針變量p賦值*/
也可以把上面的兩句寫成以下形式:
int a=3,*p=&a; /*給指針變量初始化*/
注意不能寫成:
int *p=&a,a;
通過上面的兩種方式就把變量
a的地址賦給了指針變量p,此時稱作指針變量p指向了變量a。如圖2.7所示。

圖2.7 指針變量p和變量a的指向關系示意圖
注意以下幾個方面:
①求地址運算符“&”的作用對象只能是變量或后面要講到的數組,而不能是常量或表達式。
例如:
int *p,a;
p=&(a+1); /*該賦值語句是錯誤的*/
②求地址運算符“&”的運算對象的類型必須與指針變量的基類型相同。
例如:
int *p,a;
float b;
p=&b; /*該賦值語句是錯誤的*/
因為指針變量p的基類型是int型,而求地址運算符“&”作用的對象b的類型是float型。
(2)通過指針變量獲得地址值
可以通過賦值運算,把一個指針變量中的地址值賦給另一個同類型的指針變量,從而使兩個指針指向同一地址。
例如:
int a=3,*p=&a,*q;
q=p;
通過賦值運算q=p,使得指針變量p和q同時指向了變量a。
注意:p和q的基類型必須一致。如圖2.8所示。

圖2.8 指針變量p和q與變量a的關系示意圖
(3)給指針變量賦“空”值
不允許給一個指針變量直接賦一個整數值。
例如:
int *p;
p=2009; /*該賦值語句是錯誤的*/
但是可以給一個指針變量賦空值。
例如:
int *p;
p=NULL; /*該賦值語句是合法的*/
NULL是在stdio.h頭文件中的預定義符,它的代碼值為0,因此在使用NULL時,應在程序的前面出現預定義行:#include"stdio.h"或#include<stdio.h>。當執行了上述的賦值語句p=NULL后,稱p為空指針。以上賦值語句等價于:
p='\0'; 或 p=0;
空指針的含義是:指針p并不是指向地址為0的存儲單元,而是不指向任何存儲單元。企圖通過一個空指針去訪問一個存儲單元時,將會得到一個出錯信息。
2.9.4 對指針變量的操作
通過上面的學習,我們已經了解關于指針變量的含義了,那么對于任何的存儲單元就有兩種形式來存取單元的數據了,一種是“直接存取”,另一種是“間接存取”。
所謂“直接存取”就是按變量的地址存取變量值的方式。通俗地說就是直接使用變量名來對該變量所對應的存儲單元進行存取操作。
所謂“間接存取”就是通過指針變量p間接得到變量a的地址,然后存取變量a的值的方式。
C語言提供了一個稱作“間接訪問運算符”的單目運算符:“*”。“*”出現在程序中的不同位置,其含義是不同的。
例如:
int a=3,*p,b; /*這里的“*”是個說明符,用來說明變量p是個指針型變量*/
p=&a; /*通過取地址運算符&使指針變量p指向變量a,即先賦值*/
b=*p; /*這里的“*”是代表“取數據”,即把p所指向的存儲單元中的數據(3)讀出來賦給變量b,等價于b=a*/
*p=5; /*這里的“*”是代表存數據,即把一個整數5存到指針變量 p所指向的單元(也就是變量a),等價于a=5,此時a的值變為5*/
使用指針變量應注意以下幾個方面:
①對指針變量的使用必須是先使指針變量有固定的指向然后才可以使用,即先賦值后使用。
例如:
int a,*p;
*p=5; /*這種寫法是錯誤的,因為此時指針變量p還沒有固定指向,這樣使用容易造成重要數據的破壞*/
②運算符“&”和“*”的優先級相同,結合性是從右到左。
例如:
int a=3,*p,**q;
p=&a;
q=&p;
a.&*p的含義是什么?由于&和*的優先級相同,按從右到左結合,等價于&(*p),*先和p結合,*p就是變量a,再執行&運算,相當于&a,即取變量a的地址。因此&*p等價于&a。
b.*&a的含義是什么?由于&和*的優先級相同,按從右到左結合,等價于*(&a),&先和a結合,即&a,取變量a的地址,再進行*運算,相當于取變量a的值。因此*&a等價于a。
c.q=&p;可以用圖2.9來形象表示:

圖2.9 變量q、p和a的關系
d.*、++、--的優先級是相同的,結合性為從右到左。
例如:
(*p)++ /*等價于a++*/
*p++ /*等價于*(p++)*/
++*p /*等價于++a*/
*++p /*等價于*(++p)*/
【例2.15】 指針變量使用舉例。
#include<stdio.h>
void main()
{ int a=9,*p=&a,**q=&p;
printf("%d\n",a); /*對變量的直接存取*/
printf("%d\n",*p); /*對變量的間接存取*/
printf("%d\n",**q); /*對變量的間接存取*/
}
程序運行結果:
9
9
9
【例2.16】 指針變量使用舉例。
#include<stdio.h>
void main()
{ int a=9,*p;
p=&a;
*p=*p+1; /*等價于a=a+1*/
printf("%d ",a); /*對變量的直接存取*/
printf("%d\n",*p); /*對變量的間接存取*/
printf("%d ",++*p); /*對變量的間接存取*/
printf("%d\n",(*p)++); /*對變量的間接存取*/
}
程序運行結果:
10 10
11 11
- Functional Python Programming
- Vue.js前端開發基礎與項目實戰
- AngularJS Web Application Development Blueprints
- Building Mobile Applications Using Kendo UI Mobile and ASP.NET Web API
- Mastering Scientific Computing with R
- Julia Cookbook
- Active Directory with PowerShell
- 軟件測試實用教程
- 響應式Web設計:HTML5和CSS3實戰(第2版)
- Building Dynamics CRM 2015 Dashboards with Power BI
- Python程序設計教程
- Offer來了:Java面試核心知識點精講(框架篇)
- Java EE互聯網輕量級框架整合開發:SSM+Redis+Spring微服務(上下冊)
- Django 3 Web Development Cookbook
- 秒懂算法:用常識解讀數據結構與算法