1. 程式人生 > >C語言---從指標到二重指標(上)

C語言---從指標到二重指標(上)

江湖傳言:得指標者得C語言。不知道這句話是從什麼時候開始流傳的,不過這也不重要,重要的是我們隱約能從中感受到指標在C語言中的重要性。對於初學C語言的人來說,指標可能是大多數人心中的痛。筆者當年學C語言的時候在指標這塊還算順利,自以為已經熟練掌握了指標,等到後來學習資料結構的時候,才發現自己其實一直都理解偏了,那叫一個尷尬,所以至今我都不敢說自己學會了指標,就像大多數人都不敢說自己精通了C語言一樣。本文試圖用較為淺顯易懂的方式來重新認識指標。

先來看一個簡單的例子:

#include <stdio.h>
#include <stdlib.h>
void swap(int a, int b)
{
	int temp;
	printf("@@2  a = %d, b = %d\n", a, b);
	temp = a;
	a = b;
	b = temp;
	printf("@@3  a = %d, b = %d\n", a, b);
}
int main(void) {
	int a = 1;
	int b = 3;
	printf("@1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("@4  a = %d, b = %d\n", a, b);
	system("pause");
}

執行結果如下:

從程式的執行結果可以看到,在swap函式中明明完成了對a,b兩個變數的交換,可回到主函式後a,b還是原來那個a,b,主函式對swap函式的所作所為竟毫無反應,莫非電腦進了水?其實不然,來幾條列印語句看看發生了什麼。

#include <stdio.h>
#include <stdlib.h>
void swap(int a, int b)
{
	int temp;
	printf("\n @@2  the address of a is: %p\n", &a);
	printf(" @@2  the address of b is: %p\n", &b);
	printf(" @@2  a = %d, b = %d\n", a, b);
	temp = a;
	a = b;
	b = temp;
	printf("\n @@3  the address of a is: %p\n", &a);
	printf(" @@3  the address of b is: %p\n", &b);
	printf(" @@3  a = %d, b = %d\n", a, b);
}
int main(void) {
	int a = 1;
	int b = 3;
	printf(" @1  the address of a is: %p\n", &a);
	printf(" @1  the address of b is: %p\n", &b);
	printf(" @1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("\n @4  the address of a is: %p\n", &a);
	printf(" @4  the address of b is: %p\n", &b);
	printf(" @4  a = %d, b = %d\n", a, b);
	system("pause");
}

注:printf("\[email protected]@2  the address of a is: %p\n",&a);語句中的%p(轉換說明符)和&(取地址運算子)可以列印變數在計算機中的儲存地址,“&”即為取地址運算子,這個後面會講到。

 執行結果如下:

上面這段程式碼增添了不少列印語句,顯得十分臃腫,但結論是十分清晰的:主函式main中的a,b的地址和swap函式中a,b的地址不同,原來他們不是同一個a,b。既然不是同一個,為了避免出現六耳獼猴的鬧劇,我們用c,d來代替swap中的a,b。程式碼如下:

#include <stdio.h>
#include <stdlib.h>
void swap(int c, int d)
{
	int temp;
	printf("\n @@2  c = %d, d = %d\n", c, d);
	temp = c;
	c = d;
	d = temp;
	printf(" @@3  d = %d, d = %d\n", c, d);
}
int main(void)
{
	int a = 1;
	int b = 3;
	printf(" @1  a = %d, b = %d\n", a, b);
	swap(a, b);
	printf("\n @4  a = %d, b = %d\n", a, b);
	system("pause");
}

執行結果如下:

 這一切該作何解釋呢?我曾對此困惑了一段時間,直到看到了下面這句話:“C語言是以傳值的方式將引數值傳遞給被調函式--C語言之父”,所謂傳值,即為賦值,在這個程式中呼叫swap函式具體過程如下:

c = a, d =b;          (此時:a = 1, b = 3,c = 1, d = 3)

temp = c;

c = d;              (此時:a = 1,b = 3, c = 3, d = 4)

d = temp;       (此時:a = 1, b = 3,c = 3, d = 1)

從中可以看出:呼叫swap函式時,a和b對c和d賦完值後就沒它們什麼事了,交換數值只發生在c,d之間,所以swap函式結束後,a,b並沒有實現資料交換。

      那麼我們就無法實現資料交換這麼簡單的功能嗎?路總是有的。一種方法是直接在主函式中對a,b進行資料交換,這樣不會涉及值傳遞,但不符合C語言模組化的設計思路,當然也可以在swap()函式中使用return語句,但只能返回一個值,所以並不可行。另一種方法就是運用指標。

      所謂指標者,即為儲存變數地址的變數(在計算機中,資料是按地址在記憶體中存放)。有兩個關於指標的運算子,分別是:取地址運算子“&”(用於獲取變數的記憶體地址), 間接定址運算子“*”(用於訪問指標變數的內容)。可以這麼理解:有一個int型別的變數a,記作int a ,對變數a進行取地址運算即可得到一個指標變數b,b = &a(即變數b為變數a的地址,由於a是int型別的變數,所以b是一個int型別的指標變數,記作int* b(int*為變數b的資料型別)。在對b進行間接定址運算獲得b所代表的地址所存放的值(即為變數a的值),a = *b(即a = *(&a)).由於存在這種關係,所以我們可以理解為指標變數b指向了另一個變數a。具體可以參考以下程式碼:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int a = 1;
	int* b = &a;
	printf(" a = %d\n", a);
	printf(" the address of a is: %p\n", &a);
	printf(" b = %p\n", b);
	int c = *b;
	printf(" c = %d\n", c);
	system("pause");
}

執行結果如下:

好了,現在我們回到前面的問題:用指標實現數值交換,程式碼如下:

#include <stdio.h>
#include <stdlib.h>
void swap(int* c, int* d)		//
{
	int tmp;
	int* temp = &tmp;
	*temp = *c;			//
	*c = *d;			//
	*d = *temp;			//
}
int main(void)
{
	int a = 1;
	int b = 2;
	printf(" @1 a = %d\n", a);
	printf(" @1 b = %d\n", b);
	swap(&a, &b);
	printf("\n @2 a = %d\n", a);
	printf(" @2 b = %d\n", b);
	system("pause");
}

{
	int tmp;
	int* temp = &tmp;
	*temp = *c;			//
	*c = *d;			//
	*d = *temp;			//
}
int main(void)
{
	int a = 1;
	int b = 2;
	printf(" @1 a = %d\n", a);
	printf(" @1 b = %d\n", b);
	swap(&a, &b);
	printf("\n @2 a = %d\n", a);
	printf(" @2 b = %d\n", b);
	system("pause");
}

執行結果如下:

 

好了,我們再來看看這中間發生了什麼,為了更清晰,這次使用圖說話:

從圖中可以看出,在swap函式中我們只是交換了指標變數c和d所指的變數(即1和2),由於他們是由&a和&b賦值而得,但c和d本身並沒有任何改變,所以swap函式結束後,c就是&a, d就是&b,如下圖所示:

至此我們完成了兩個數的交換。

好了,說了這麼多,指標究竟有什麼用呢?這裡只說幾點:

一:指標可以使我們直接操作記憶體,這是很多其他高階語言所不具備的(比如Java),直接對記憶體進行操作可以使得程式的執行效率更高。比如現在你需要對一個函式傳入一個結構體引數,這個結構體如下:

struct para {
	int data1;
	int data2;
	char data3;

//此處略去一萬字.....

double dataN; };

這麼一大塊資料,直接它傳入函式,,這是一件十分費力不討好的事,如果傳入這個結構體的指標,那就簡單高效多了,這就相當於你想把自己一大屋子的零食送人,你可以直接送到對方的屋子裡,比如叫輛卡車來拉,你也可以告訴對方零食在哪個房間,叫他自己來取,傳指標明顯屬於後者。

二:由於C語言函式只能返回一個值(使用return語句),當程式需要返回多個值時,return已經滿足不了人民日益增長的物質文化需要,這時我們可以考慮使用指標來實現,具體可參考上面用指標實現兩數交換的程式。

三:指標和結構體進行組合可以使我們更好地來組織資料,比如實現資料的動態儲存,­­­­­­­­­­­­­­­­對資料快速的進行查詢和排序。限於篇幅,在這裡就先不展開來談了。(未完待續)。

相關推薦

C語言實現粒子群算法PSO

計算 default img 第一個元素 1.4 best 實驗 atl 說過 上一回說了基本粒子群算法的實現,並且給出了C語言代碼。這一篇主要講解影響粒子群算法的一個重要參數---w。我們已經說過粒子群算法的核心的兩個公式為: Vid(k+1)=w*Vid(k)+c1*r

C語言求一元次方根包括虛根

#include <stdio.h> #include <math.h> float a,b,c,result; void underZero(){      float part1 = (-1)*b/(2*a);      float part2

資料結構之---C語言實現平衡叉樹AVL樹

//AVL(自動平衡二叉樹) #include <stdio.h> #include <stdlib.h> typedef int ElemType; //每個結點的平均值 typedef enum {      EH = 0,      LH =

嗨翻C語言--這裏沒有蠢問題

環境變量 文本編輯 進制 括號 大寫 指針變量 位數 literal 意義 問:card_name[0]是什麽意思?答:它是用戶輸入的第一個字符。如果用戶輸入了10,那麽card_name[0]就將是1。問:總是得用/*和*/寫註釋嗎?答:如果你的編譯器支持C99標準,就可

C語言指針的使用例子1指針地址的輸出

clu 用例 int clas 指針 light 使用 指針的使用 div #include <stdio.h> int main(void) { int a=10; int *p = &a; *p = 89; printf("變量

【藍橋杯】第六屆國賽C語言B組 2.完美正方形dfs

spa else img IT bool break main LG fill 如果一些邊長互不相同的正方形,可以恰好拼出一個更大的正方形,則稱其為完美正方形。 歷史上,人們花了很久才找到了若幹完美正方形。比如:如下邊長的22個正方形2 3 4 6 7 8 12 13 14

關於c語言的scanf\n的問題scanf_s

今天遇到一道題要求輸入scanf("%c %c %c\n",&a,&b,&c),結果不停地出現問題,經過許久嘗試,終於發現是\n出現了問題,scanf遇到\n看到另有玄機。這種情況下會要去多輸入一行,才有用,但是實際讀入的卻還是第一次輸入

C語言程式的模組化——繼承2

在C語言程式的模組化——封裝中,介紹瞭如何使用C語言的結構體來實現一個類的封裝,並通過掩碼結構體的方式實 現了類成員的保護,使公有屬性和私有屬性共存。 現在再談談面向物件的另一個基本特性——繼承。 繼承表示類與類之間的層次關係,這種關係使得某類物件可以繼承另外一類物件的

C語言資料型別表示式相關知識Linux

#include<stdio.h> int main(int argc,char**argv) { printf(“Hello Word\n"); printf(“argv[%d],%s\n”,i,argv[i]); return 0; } 計算

C語言變數定義與資料溢位初學者

1、變數定義的一般形式為:型別說明符、變數名識別符號等;例:int a,b,c;(abc為整型變數) 在書寫變數定義時應注意以下幾點: (1)允許在一個型別說明符後,定義多個相同型別的變數。各變數之間用“,”間隔。型別說明符與變數名之間用一個空格間隔。 (2)最後一個變數之後必須以“;”結尾。 (3)

C語言的奇技淫巧積累

列印引數巨集 #define INPUT_CHECK(x) \ if ((x)) \ {

嵌入式C語言的位元組對齊理解圖文

 1.位元組對齊?            對齊跟資料在記憶體中的位置有關。如果一個變數的記憶體地址正好位於系統長度的整數倍,他就被稱做自然對齊。比如在32位cpu下,假設一個整型變數的地址為0x00000004,那它就是自然對齊的。  2. 計算機為什麼要對齊?   需

C語言再學習 -- ASCII碼錶

ASCII碼錶第一部分:ASCII非列印控制字元表ASCII表上的數字0–31分配給了控制字元,用於控制像印表機等一些外圍裝置。例如,12代表換頁/新頁功能。此命令指示印表機跳到下一頁的開頭。(參詳ASCII碼錶中0-31)第二部分:ASCII列印字元數字 32–126 分配給了能在鍵盤上找到的字元,當您檢視

在linux下,如何在C語言中使用正則表示式整理

  一個正則表示式的教程可以參看(裡面有個測試正則表示式的工具)      正則表達是用來匹配字串的好東東。       如果使用者熟悉Linux下的sed、awk、grep或vi,那麼對正則表示式這一概念肯定不會陌生。由於它可以極大地簡化處理字串時的複雜度,因此現 在已

C語言隨機讀寫資料檔案

void rewind(FILE * fp):將檔案位置標記重新指向檔案開頭,該函式沒有返回值; 【例子】計算機D盤根目錄有一個存放學生資訊的stud.dat檔案,請先講檔案資訊顯示在控制檯,然後在將資料複製到F盤stud.dat檔案中。 #include <stdio.h> #

c語言基礎語法六——結構體完結

1;關於c語言結構體的引入; 在前面已經介紹了整形(int,long,….),浮點型(flaot,double),字元型(char),還介紹了陣列(儲存一組具有相同型別的資料),字串。但是在實際問題中只有這些資料型別是不夠的,有時候我們需要其中的幾種一起來修飾

C語言廣度優先搜尋之迷宮佇列

變數 head 和 tail 是隊頭和隊尾指標, head 總是指向隊頭, tail 總是指向隊尾的下一個元素。每個點的 predecessor 成員也是一個指標,指向它的前趨在 queue 陣列中的位置。如下圖所示: 廣度優先是一種步步為營的策略,每次都從各個方向探索一

R語言基礎入門到提高matrices矩陣

# Box office Star Wars (in millions!) new_hope <- c(460.998, 314.4) empire_strikes <- c(290.475, 247.900) return_jedi <- c(309.306, 165.8) # Con

C語言資料結構之稀疏矩陣

最近開始學習C語言的稀疏矩陣的一些知識,現在簡單的整理梳理一下知識脈絡,僅供自己總結學習,歡迎技術指正,拒絕盲噴。 1.首先先介紹一下關於稀疏矩陣的一些基礎知識,關於稀疏矩陣,一直都沒有過很清楚詳細的定義。簡單的說,在M*N的一個矩陣中,假設有t個元素不為0,那麼有計算公

C++資料結構:叉樹——先序建立叉樹

一、二叉樹 (Binary Tree) 定義: 二叉樹是n個節點的有限集合,該集合或者為空集( 稱為空二叉樹 ),或者由一個根節點和兩棵互不相交的的二叉樹組成,這兩棵二叉樹分別稱為根節點的左子樹和右