一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

3.3 指針 (Pointers)

 奮進一生 2015-09-26

3.3 指針 (Pointers)

我們已經明白變量其實是可以由標識來存取的內存單元。但這些變量實際上是存儲在內存中具體的位置上的。對我們的程序來說,計算機內存只是一串連續(xù)的單字節(jié)單元(1byte cell),即最小數(shù)據單位,每一個單元有一個唯一地址。

計算機內存就好像城市中的街道。在一條街上,所有的房子被順序編號,每所房子有唯一編號。因此如果我們說芝麻街27號,我們很容易找到它,因為只有一所房子會是這個編號,而且我們知道它會在26號和28號之間。

同房屋按街道地址編號一樣,操作系統(tǒng)(operating system)也按照唯一順序編號來組織內存。因此,當我們說內存中的位置1776,我們知道內存中只有一個位置是這個地址,而且它在地址1775和1777之間。

 

地址操作符/去引操作符 Address/dereference operator (&)

當我們聲明一個變量的同時,它必須被存儲到內存中一個具體的單元中。通常我們并不會指定變量被存儲到哪個具體的單元中—幸虧這通常是由編譯器和操作系統(tǒng)自動完成的,但一旦操作系統(tǒng)指定了一個地址,有些時候我們可能會想知道變量被存儲在哪里了。

這可以通過在變量標識前面加與符號ampersand sign (&)來實現(xiàn),它表示"...的地址" ("address of"),因此稱為地址操作符(adress operator),又稱去引操作符(dereference operator)。例如:

ted = &andy;

將變量andy的地址賦給變量ted,因為當在變量名稱andy 前面加ampersand (&) 符號,我們指的將不再是該變量的內容,而是它在內存中的地址。

假設andy 被放在了內存中地址1776的單元中,然后我們有下列代碼:

andy = 25;
fred = andy;
ted = &andy;

其結果顯示在下面的圖片中:

我們將變量andy 的值賦給變量fred,這與以前我們看到很多例子都相同,但對于ted,我們把操作系統(tǒng)存儲andy的內存地址賦給它,我們想像該地址為1776 (它可以是任何地址,這里只是一個假設的地址),原因是當給ted 賦值的時候,我們在andy 前面加了ampersand (&) 符號。

存儲其它變量地址的變量(如上面例子中的ted ),我們稱之為指針(pointer)。在C++ 中,指針pointers 有其特定的優(yōu)點,因此經常被使用。在后面我們將會看到這種變量如何被聲明。

 

引用操作符Reference operator (*)

使用指針的時候,我們可以通過在指針標識的前面加星號asterisk (*)來存儲該指針指向的變量所存儲的數(shù)值,它可以被翻譯為“所指向的數(shù)值”("value pointed by")。因此,仍用前面例子中的數(shù)值,如果我們寫: beth = *ted; (我們可以讀作:"beth 等與ted所指向的數(shù)值") beth 將會獲得數(shù)值25,因為ted 是1776,而1776 所指向的數(shù)值為25。

你必須清楚的區(qū)分ted 存儲的是1776,但*ted (前面加asterisk * ) 指的是地址1776中存儲的數(shù)值,即25。注意加或不加星號*的不同(下面代碼中注釋顯示了如何讀這兩個不同的表達式):

beth = ted; // beth 等于 ted ( 1776 )
beth = *ted; // beth 等于 ted 所指向的數(shù)值 ( 25 )

 

地址或反引用操作符Operator of address or dereference (&)

它被用作一個變量前綴,可以被翻譯為“…的地址”("address of"),因此:&variable1 可以被讀作 variable1的地址("address of variable1" )。

引用操作符Operator of reference (*)

它表示要取的是表達式所表示的地址指向的內容。它可以被翻譯為“…指向的數(shù)值” ("value pointed by")。

* mypointer 可以被讀作 "mypointer指向的數(shù)值"。

繼續(xù)使用上面開始的例子,看下面的代碼:

andy = 25;
ted = &andy;

現(xiàn)在你應該可以清楚的看到以下等式全部成立:

andy == 25
&andy == 1776
ted == 1776
*ted == 25

第一個表達式很容易理解,因為我們有賦值語句andy=25;。第二個表達式使用了地址(或反引用)操作符(&) 來返回變量andy的地址,即 1776。第三個表達式很明顯成立,因為第二個表達式為真,而我們給ted賦值的語句為ted = &andy;。第四個表達式使用了引用操作符 (*),相當于ted指向的地址中存儲的數(shù)值,即25。

由此你也可以推斷出,只要ted 所指向的地址中存儲的數(shù)值不變,以下表達式也為真:

*ted == andy

聲明指針型變量Declaring variables of type pointer

由于指針可以直接引用它所指向的數(shù)值,因此有必要在聲明指針的時候指明它所指向的數(shù)據類型。指向一個整型int或浮點型float數(shù)據的指針與指向一個字符型char數(shù)據的指針并不相同。

因此,聲明指針的格式如下:

type * pointer_name;

這里,type 是指針所指向的數(shù)據的類型,而不是指針自己的類型。例如:

int * number;
char * character;
float * greatnumber;

它們是3個指針的聲明,每一個指針指向一種不同數(shù)據類型。這三個指針本身其實在內存中占用同樣大小的內存空間(指針的大小取決于不同的操作系統(tǒng)),但它們所指向的數(shù)據是不同的類型,并占用不同大小的內存空間,一個是整型int,一個是字符型char ,還有一個是浮點型float。

需要強調的一點是,在指針聲明時的星號asterisk (*) 僅表示這里聲明的是一個指針,不要把它和前面我們用過的引用操作符混淆,雖然那也是寫成一個星號 (*)。它們只是用同一符號表示的兩個不同任務。

// my first pointer
#include <iostream.h>

int main ( ) {
int value1 = 5, value2 = 15;
int * mypointer;
mypointer = &value1;
*mypointer = 10;
mypointer = &value2;
*mypointer = 20;
cout << "value1==" << value1 << "/ value2==" << value2;
return 0;
}
value1==10 / value2==20

注意變量value1 和 value2 是怎樣間接的被改變數(shù)值的。首先我們使用 ampersand sign (&) 將value1的地址賦給mypointer 。然后我們將10 賦給 mypointer所指向的數(shù)值,它其實指向value1的地址,因此,我們間接的修改了value1的數(shù)值。

為了讓你了解在同一個程序中一個指針可以被用作不同的數(shù)值,我們在這個程序中用value2 和同一個指針重復了上面的過程。

下面是一個更復雜一些的例子:

// more pointers
#include <iostream.h>

int main () {
int value1 = 5, value2 = 15;
int *p1, *p2;
p1 = &value1; // p1 = address of value1
p2 = &value2; // p2 = address of value2
*p1 = 10; // value pointed by p1 = 10
*p2 = *p1; // value pointed by p2 = value pointed by p1
p1 = p2; // p1 = p2 (value of pointer copied)
*p1 = 20; // value pointed by p1 = 20
cout << "value1==" << value1 << "/ value2==" << value2;
return 0;
}
value1==10 / value2==20

上面每一行都有注釋說明代碼的意思:ampersand (&) 為"address of",asterisk (*) 為 "value pointed by"。注意有些包含p1 和p2 的表達式不帶星號。加不加星號的含義十分不同:星號(*)后面跟指針名稱表示指針所指向的地方,而指針名稱不加星號(*)表示指針本身的數(shù)值,即它所指向的地方的地址。

另一個需要注意的地方是這一行:

int *p1, *p2;

聲明了上例用到的兩個指針,每個帶一個星號(*),因為是這一行定義的所有指針都是整型int (而不是 int*)。原因是引用操作符(*) 的優(yōu)先級順序與類型聲明的相同,因此,由于它們都是向右結合的操作,星號被優(yōu)先計算。我們在 section 1.3: Operators 中已經討論過這些。注意在聲明每一個指針的時候前面加上星號asterisk (*)。

 

指針和數(shù)組Pointers and arrays

數(shù)組的概念與指針的概念聯(lián)系非常解密。其實數(shù)組的標識相當于它的第一個元素的地址,就像一個指針相當于它所指向的第一個元素的地址,因此其實它們是同一個東西。例如,假設我們有以下聲明:

int numbers [20];
int * p;

下面的賦值為合法的:

p = numbers;

這里指針p 和numbers 是等價的,它們有相同的屬性,唯一的不同是我們可以給指針p賦其它的數(shù)值,而numbers 總是指向被定義的20個整數(shù)組中的第一個。所以,p只是一個普通的指針變量,而與之不同,numbers 是一個指針常量(constant pointer),數(shù)組名的確是一個指針常量。因此雖然前面的賦值表達式是合法的,但下面的不是:

numbers = p;

因為numbers 是一個數(shù)組(指針常量),常量標識不可以被賦其它數(shù)值。

由于變量的特性,以下例子中所有包含指針的表達式都是合法的:

// more pointers
#include <iostream.h>

int main () {
int numbers[5];
int * p;
p = numbers;
*p = 10;
p++;
*p = 20;
p = &numbers[2];
*p = 30;
p = numbers + 3;
*p = 40;
p = numbers;
*(p+4) = 50;
for (int n=0; n<5; n++)
cout << numbers[n] << ", ";
return 0;
}
10, 20, 30, 40, 50,

在數(shù)組一章中我們使用了括號[]來指明我們要引用的數(shù)組元素的索引(index)。中括號[]也叫位移(offset)操作符,它相當于在指針中的地址上加上括號中的數(shù)字。例如,下面兩個表達式互相等價:

a[5] = 0; // a [offset of 5] = 0
*(a+5) = 0; // pointed by (a+5) = 0

不管a 是一個指針還是一個數(shù)組名, 這兩個表達式都是合法的。

 

指針初始化Pointer initialization

當聲明一個指針的時候我們可能需要同時指定它們指向哪個變量,

int number;
int *tommy = &number;

這相當于:

int number;
int *tommy;
tommy = &number;

當給一個指針賦值的時候,我們總是賦給它一個地址值,而不是它所指向數(shù)據的值。你必須考慮到在聲明一個指針的時候,星號 (*) 只是用來指明它是指針,而從不表示引用操作符reference operator (*)。記住,它們是兩種不同操作,雖然它們寫成同樣的符號。因此,我們要注意不要將以上的代碼與下面的代碼混淆:

int number;
int *tommy;
*tommy = &number;

這些代碼也沒有什么實際意義。

在定義數(shù)組指針的時候,編譯器允許我們在聲明變量指針的同時對數(shù)組進行初始化,初始化的內容需要是常量,例如:

char * terry = "hello";

在這個例子中,內存中預留了存儲"hello" 的空間,并且terry被賦予了向這個內存塊的第一個字符(對應’h’)的指針。假設"hello"存儲在地址1702,下圖顯示了上面的定義在內存中狀態(tài):

這里需要強調,terry 存儲的是數(shù)值1702 ,而不是'h' 或 "hello",雖然1702 指向這些字符。

指針terry 指向一個字符串,可以被當作數(shù)組一樣使用(數(shù)組只是一個常量指針)。例如,如果我們的心情變了,而想把terry指向的內容中的字符'o' 變?yōu)榉?!' ,我們可以用以下兩種方式的任何一種來實現(xiàn):

terry[4] = '!';
*(terry+4) = '!';

記住寫 terry[4] 與*(terry+4)是一樣的,雖然第一種表達方式更常用一些。以上兩個表達式都會實現(xiàn)以下改變:

指針的數(shù)學運算Arithmetic of pointers

對指針進行數(shù)學運算與其他整型數(shù)據類型進行數(shù)學運算稍有不同。首先,對指針只有加法和減法運算,其它運算在指針世界里沒有意義。但是指針的加法和減法的具體運算根據它所指向的數(shù)據的類型的大小的不同而有所不同。

我們知道不同的數(shù)據類型在內存中占用的存儲空間是不一樣的。例如,對于整型數(shù)據,字符char 占用1 的字節(jié)(1 byte),短整型short 占用2 個字節(jié),長整型long 占用4個字節(jié)。

假設我們有3個指針:

char *mychar;
short *myshort;
long *mylong;

而且我們知道他們分別指向內存地址1000, 2000 和3000。

因此如果我們有以下代碼:

mychar++;
myshort++;
mylong++;

就像你可能想到的,mychar的值將會變?yōu)?001。而myshort 的值將會變?yōu)?002,mylong的值將會變?yōu)?004。原因是當我們給指針加1時,我們實際是讓該指針指向下一個與它被定義的數(shù)據類型的相同的元素。因此,它所指向的數(shù)據類型的長度字節(jié)數(shù)將會被加到指針的數(shù)值上。以上過程可以由下圖表示:

這一點對指針的加法和減法運算都適用。如果我們寫以下代碼,它們與上面例子的作用一抹一樣: mychar = mychar + 1;

myshort = myshort + 1;
mylong = mylong + 1;

這里需要提醒你的是,遞增 (++) 和遞減 (--) 操作符比引用操作符reference operator (*)有更高的優(yōu)先級,因此,以下的表達式有可能引起歧義:

*p++;
*p++ = *q++;

第一個表達式等同于*(p++) ,它所作的是增加p (它所指向的地址,而不是它存儲的數(shù)值)。

在第二個表達式中,因為兩個遞增操作(++) 都是在整個表達式被計算之后進行而不是在之前,所以*q 的值首先被賦予*p ,然后q 和p 都增加1。它相當于:

*p = *q;
p++;
q++;

像通常一樣,我們建議使用括號()以避免意想不到的結果。

 

指針的指針Pointers to pointers

C++ 允許使用指向指針的指針。要做到這一點,我們只需要在每一層引用之前加星號(*)即可:

char a;
char * b;
char ** c;
a = 'z';
b = &a;
c = &b;

假設隨機選擇內存地址為7230, 8092 和10502,以上例子可以用下圖表示:

(方框內為變量的內容;方框下面為內存地址)

這個例子中新的元素是變量c,關于它我們可以從3個方面來討論,每一個方面對應了不同的數(shù)值:

c 是一個(char **)類型的變量,它的值是8092

*c 是一個(char*)類型的變量,它的值是7230

**c 是一個(char)類型的變量,它的值是'z'

 

空指針void pointers

指針void 是一種特殊類型的指針。void 指針可以指向任意類型的數(shù)據,可以是整數(shù),浮點數(shù)甚至字符串。唯一個限制是被指向的數(shù)值不可以被直接引用(不可以對它們使用引用星號*),因為它的長度是不定的,因此,必須使用類型轉換操作或賦值操作來把void 指針指向一個具體的數(shù)據類型。

它的應用之一是被用來給函數(shù)傳遞通用參數(shù):

// integer increaser
#include <iostream.h>

void increase (void* data, int type) {
switch (type) {
case sizeof(char) : (*((char*)data))++; break;
case sizeof(short): (*((short*)data))++; break;
case sizeof(long) : (*((long*)data))++; break;
}
}

int main () {
char a = 5;
short b = 9;
long c = 12;
increase (&a,sizeof(a));
increase (&b,sizeof(b));
increase (&c,sizeof(c));
cout << (int) a << ", " << b << ", " << c;
return 0;
}
6, 10, 13

sizeof 是C++的一個操作符,用來返回其參數(shù)的長度字節(jié)數(shù)常量。例如,sizeof(char) 返回 1,因為 char 類型是1字節(jié)長數(shù)據類型。

 

函數(shù)指針Pointers to functions

C++ 允許對指向函數(shù)的指針進行操作。它最大的作用是把一個函數(shù)作為參數(shù)傳遞給另外一個函數(shù)。聲明一個函數(shù)指針像聲明一個函數(shù)原型一樣,除了函數(shù)的名字需要被括在括號內并在前面加星號asterisk (*)。例如:

// pointer to functions
#include <iostream.h>

int addition (int a, int b) {
    return (a+b);
}

int subtraction (int a, int b) {
    return (a-b);
}

int (*minus)(int,int) = subtraction;
int operation (int x, int y, int (*functocall)(int,int)) {
    int g;
    g = (*functocall)(x,y);
    return (g);
}

int main () {
    int m,n;
    m = operation (7, 5, addition);
    n = operation (20, m, minus);
    cout <<n;
    return 0;
}
8

在這個例子里, minus 是一個全局指針,指向一個有兩個整型參數(shù)的函數(shù),它被賦值指向函數(shù)subtraction,所有這些由一行代碼實現(xiàn):

int (* minus)(int,int) = subtraction;

這里似乎解釋的不太清楚,有問題問為什么(int int)只有類型,沒有參數(shù),就再多說兩句。

這里 int (*minus)(int int)實際是在定義一個指針變量,這個指針的名字叫做minus,這個指針的類型是指向一個函數(shù),函數(shù)的類型是有兩個整型參數(shù)并返回一個整型值。

整句話“int (*minus)(int,int) = subtraction;”是定義了這樣一個指針并把函數(shù)subtraction的值賦給它,也就是說有了這個定義后minus就代表了函數(shù)subtraction。因此括號中的兩個int int實際只是一種變量類型的聲明,也就是說是一種形式參數(shù)而不是實際參數(shù)。


    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    中国少妇精品偷拍视频| 99久久精品午夜一区二区| 欧美人妻少妇精品久久性色| 一本色道久久综合狠狠躁| 国产小青蛙全集免费看| 国产精品不卡一区二区三区四区 | 初尝人妻少妇中文字幕在线| 日本人妻中出在线观看| 国内真实露脸偷拍视频| 亚洲午夜精品视频观看| 色哟哟国产精品免费视频| 日韩欧美高清国内精品| 亚洲欧洲精品一区二区三区| 国产精品内射婷婷一级二级| 亚洲国产精品久久琪琪| 欧美视频在线观看一区| 国产欧美亚洲精品自拍| 极品少妇一区二区三区精品视频| 亚洲欧美日韩在线中文字幕| 欧美日韩综合在线第一页| 亚洲中文字幕熟女丝袜久久| 亚洲中文字幕综合网在线| 成年女人午夜在线视频| 久久久精品日韩欧美丰满| 久久精品中文扫妇内射| 国产传媒精品视频一区| 一区二区日本一区二区欧美| 亚洲熟女乱色一区二区三区| 蜜臀人妻一区二区三区| 婷婷激情四射在线观看视频| 欧美精品二区中文乱码字幕高清| 久久少妇诱惑免费视频| 午夜久久精品福利视频| 日韩国产亚洲一区二区三区| 亚洲欧美日本成人在线| 91亚洲人人在字幕国产| 亚洲a码一区二区三区| 国产av熟女一区二区三区蜜桃| 欧美日韩亚洲国产av| 99在线视频精品免费播放| 日韩人妻精品免费一区二区三区|