程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 淺議C語言中數組和指針的互操作

淺議C語言中數組和指針的互操作

編輯:關於C語言

曾聽好多朋友說,C是一種懷舊的語言,因為它的歷史很久遠,然而自從各種面向對象的編程語言的相續出現讓它的影響力日減。

當然了,這是無可非議的,但是C的高效性是其他語言無妨比擬的,所以我們有必要把握其中的精華與奧妙,也就有必要知道其中的基本的數據結構的比如數組,稍微有點深度的堆棧、列表、結構體等的操作和實現。指針也是C語言中的一個很優秀靈活的結構,對它的了解也是必不可少的。

我們一般都認為數組是一維的數據存儲結構,因為二位數組或者說矩陣都可以看作是多個一維數組的組合結構,定義在其上的數據存儲訪問方式是一樣的。所以一維數組是其中最基礎的最重要的部分,只有理解了此類數據結構的本質才能觸類旁通了。

數組(array)是若干同類變量的聚合,允許通過統一的名字飲用其中的變量。所以數組也就是一個同一類型的數據的有限集合。可以通過下表來訪問數組中的某一/些數組元素。

在C語言中數組都由連續的內存區域構成(有時候,不一定是這樣),最低地址對應首元素,數組的下標是從0開始的,所以首元素也就是數組下標為0的元素,最高的地址對應最末的元素,即第N-1個元素(如果我們定義的數組為N元)。

數組的定義方式:

在C語言中允許在聲明數組的時候同時對其進行初始化,也可以把聲明和定義放在不同的位置,初始化的一般的類似於如下的表達式:

type_specifier array_name[size1]...[sizeN] = {value_list};

其中vlaue_list是由逗號(,)分隔的常量表,常量表必須和type_specifier兼容。最後由分號與下一個語句分隔。由此可見一維數組的定義方式為:type_specifier
array_name[size] = {value_list};

如下:

char hello[12] = {’H’,’e’,’l’,’l’,’o’,’,’
,’ ’,’w’,’o’,’r’,’l’,’d’,’\0’};

注意:字符數組是一"’\0’"收尾的,這是C標准的一部分。因為在操作字符數組的時候是以’\0’作為結束判斷的標志。當然了,如果你定義的是一個字符串那就不用加這個’\0’了。因為有機制幫助你自動添加。

上面的例子的串的生命方式為:string hello = "Hello, world";(當然了,具體的實現中你必須把"string.h"頭文件加入到你的文件中),或者你也可以這樣來聲明:

char *hello = "Hello, world";
或者
char hello[] = "Hello, world";

切換為字符指針數組,其效果是一樣的);數組初始化的時候還可 以不標明最大小,即char hello[] = {’H’,’e’,’l’,’l’,’o’
,’,’,’ ’,’w’,’o’,’r’,’l’,’d’,’\0’};,

這時候編譯器會根據後邊的賦值情況為數組分配合適的內存空間,這個你不用擔憂,除非機器正處於內存缺狀態。

數組元素的訪問:

可以利用循環結構來挨個訪問數組的元素,比如:

[...]
   int i;
   char hello[12] = {’H’,’e’,’l’,’l’,’o’,’
   ,’,’ ’,’w’,’o’,’r’,’l’,’d’,’\0’};
   [...]
   for(i = 0; i < 12; i++){
  
   printf("%c",hello[i]);
   }
   printf("\n");
   [...]

其中有一點必須注意了,那就是i的值不能取到12,因為我們的下標識從0開始的,即hello[0]是第一個元素,數組的下界,而hello[12]是第一個空元素,數組的上界。

其實,數組元素的個數等於定義時的下標,也等於數組的上界(12)減去下界(0)得到的數值,還等於上界地址減去下界地址模sizeof(tyep_specifier)的值(假設數組空間是連續分布的,如果不是這樣那麼這種方法也就不成立了)。

在數組初始化的時候還允許在數組的最後加一個逗號(,),比如:

int month_lengths[] = {’31’,’29’,’31’,’30’,’31’,’30’,’31’,’31’,’30’,’31’,’30’,’31’,};

因為在分行的情況下,編譯程序是通過逗號作為標志的,所以這是合法的。

以上結構可以寫為如下的形式:

int month_lengths[] = {’31’,’29’,’31’,’30’,’31’,’30’,’31’,’31’,’30’,’31’,’30’,’31’,};

還有一點必須注意了,字符和字符串的表達式不一樣的。單引號用於字符表達式,而雙引號則用於字符串表達式的。這是好多人,包括C的老手,最愛犯的錯誤。寫一個換行語句是會

寫成 :

printf(’\n’); ,而在字符判斷操作的時候卻寫成了:
int c;
[...]
if((c = getchar()) != "\n"){
do something here
}
[...]

這種低級的錯誤是很容易避免的,但是一不小心就出錯了,而且還不少呢,在教科書、參考書、以及一些文獻以及網絡文字裡經常出現,只要你細心一點就可以發現。這看似問題不大,但是

如果在嵌入式系統,尤其是實時系統有可能造成系統崩潰的,所以寫程序時不要報以任何的僥幸心理,必須審慎每一個細節。程序設計不是一門技術,而是一門藝術。

下面看看指針吧。

指針是(pointer)是存放內存地址的變量。 從這個定義中我們可以實力一個這樣的概念: 與指針操作相關的最直接的要素是地址,內存地址。這裡的地址指內存中另一個變量的位置。

如果一個變量含有一個變量的地址,則稱一個變量指向第二個變量。

准備存放指針的變量必須在使用前聲明好。 指針的聲明由一個基類(base type)、一個星號(*)和變量名組成。 一般的形式為:

type * name;

其中,type是指針的基類類型,可以是任何有效的數據類型,name是指針變量的名稱。當然了 type *name 和 type * name是等效的,一般的編譯器都會在編譯程序的時候忽略中間的空格。

指針的基類定義了指針可以指向的變量的類型。 技術上,任何指針類型都可以指向內存的任何位置。然而,指針的操作是基於指針的類型的,也就是說指針變量和其指向的地址的變量的類型必須兼容。即如下的操作時錯誤的:

int *p;
char ch = ’A’;
p = &ch;

因為類型不匹配,所以這是錯誤的。其實,這句話不完全對。這只是一個標准罷了,具體還需要看你的編譯器的實現了。上面這種方式的操作在好多編譯器上是可以通過的,比如,Win-TC、LCC等(在Win-TC下直接通過,而在LCC下也只有一個警告罷了)。 不過采用這種方式設計的程序的可移植性一定是很糟糕的。還有如果在操作中如果你想把指針地址存給一個數組元素時,如果類型不匹配,那麼你只有強制轉換指針地址的數據類型了,直接賦值是不可能的。所以我們提倡還是采用標准的規定來進行操作。還有下面這一種操作也是錯誤的,雖然類型是匹配的:

int *p;
int i;
p = &i;

因為i僅被用戶聲明了,但是沒有分配內存,所以這是錯誤的。一個變量只有在被初始化的時候,才有可能分配到內存的(大多數情況是初始化之後,就分配到內存,但是如果在內存不足的情況就不可能滿足這個要求了)。 在指針聲明中還存在一個誤區,那就是錯誤的認為在如是的表達式 int *p,m,n; 中聲明了3個指針變量,其實只有第一個(p)是指針變量,而其他兩個(m,n)只是int型變量罷了。只有這樣才能同時聲明幾個指針變量的: int *p, *m, *n, ...;

指針也可以像一般變量一樣進行初始化的,但是你不能給一個一個指針直接的賦值哦。 比如:

int *p;
p = 10; 只是錯誤的。不過你可以把指針賦值為空,即
int *p = 0; 或者
int *p;
p = NULL;

因為在許多的C語言的頭文件,如<stdio.h>,定義了宏NULL,它是一個空指針常量,所以我們的表達式是合法的,效果和上面的一個相同。

我們可以把一個數組變量賦予一個指針,因為它們都是一種地址的映射罷了。這才是我們的主題。比如:

char str[100], *pointer;
pointer = str;

這裡,pointer指向數組str的首元素,與str[0]或者訪問變量str的實質是一樣的,操作的都是數組的第1個元素,0位置上的元素。如果要訪問str數組的第10個元素,那麼操作如下:

str[9]

*(pointer + 9) 這是等效的。在指針中也有++,--這樣的操作,尤其在指針和數組互操作時,用到的機率最大,因為操作很靈活。比如:

#include <stdio.h>
int main(){
char hello[] = "Hello, world !";
char *p;
p = hello;
do{
printf("%c", *p);
p++;
} while(*p);
printf("\n");
return ;
}

這是一個典型的例子,即用了指針的“自增”操作,還是數組合指針互操作的好例子。你的操作還可以是:

p = &hello[3];
*p = hello[3];
*&hello[3] = *p;
printf("%d\n",*&hello[3]);
hello[3] = *p;
printf("%d\n",&hello[3]);
hello[3] = *p;
printf("%d\n",hello[3]);

等等,其中 *&hello[3] 和 &hello[3]的值是一樣的,都是讀取hello數組中3號元素(第四個元素)的地址的。而語句 p = &hello[3];是把3號元素的地址賦給指針變量,而hello[3] = *p;

是把指針p的值賦給hello[3]的。這也是互操作的一種方式。

注意了: *++P , ++*p 和 *p++ 以及 *--p , --*p 和 *p-- 表示的結果是不同的。因為++、--的優先級高於*的優先級,所以前邊的表達式相當於 *(++p) , *(++p) 和 *(p++) 以及 *(--p) , *(--p) 和 *(p--)。建議牢牢的記住操作符的優先級,因為在這些細節上稍不注意就會產生問題,我們意想不到的。

數組和指針都可以當作參數來處理,但是才用指針的概率要高一些,因為指針較數組更加靈活。例如,你可以如下傳遞一個數組或指針變量:

#include <stdio.h>
int main(){
  char hello[] = "Hello, world !\n";
  char *p;
  p = hello;
  printf(hello);
  printf(p);
  printf("%s", p);
  printf("%s", hello);
  do{
   printf("%c", *p);
   p++;
  } while(*p);
  printf("\n");
  return ;
}

在LCC中是完全通過的,其輸出結果是:

Hello, world !
Hello, world !
Hello, world !
Hello, world !
Hello, world !

但是在有些編譯器上,語句:

printf(hello);
printf(p);

可能只會輸出字符串的第1個元素,因為有些編譯器采取的是對字符串進行“截取”的方式來處理,所以其結果有可能是:

HHHello, world !
Hello, world !
Hello, world !

可能這些東西並不一定有用,但是知道這些知識還是有必要的。如果我的辛勞能對你的學習或者開發有點幫助,那我也就滿足了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved