深入理解C指標之五:指標和字串

來源:互聯網
上載者:User

標籤:

原文:深入理解C指標之五:指標和字串

  基礎概念

  字串可以分配到記憶體的不同地區,通常使用指標來支援字串操作。字串是以ASCII字元NUL結尾的字元序列。ASCII字元NUL表示為\0。字串通常儲存在數組或者從堆上分配的記憶體中。不過,並非所有的字元數組都是字串。例如,字元數組可能沒有NUL字元。

  C中有兩種類型的字串。

  * 單位元組字串。由char資料類型組成的序列。

  * 寬字元串。由wchar_t資料類型組成的序列。

  wchar_t資料類型用來表示寬字元串,可能是16位或32位寬。這兩種字串都以NUL結尾。寬字元主要用來支援非拉丁字元集,對於支援外語的應用程式很有用。

  字串的長度是除了NUL字元之外的字元數。為字串分配記憶體時,要記得為NUL字元預留空間。注意NUL是一個字元,定義為\0,與NULL(void*(0))不一樣。NULL用來表示一種特殊的指標。

  字元常量是單引號引起來的字元序列。字元常量通常由一個字元組成,也可以包含多個字元,如逸出字元。在C中,它們的類型是int。char的長度是1位元組,而一個字元字面量的長度是 sizeof(int) 個位元組。

printf("%d\n", sizeof(‘a‘));//4

   聲明字串的方式有3種:字面量字元數組字元指標。字串字面量是用雙引號引起來的字元序列,常用來進行初始化,它們位於字串字面量池中。如果聲明一個擁有32個字元的數組,那麼只能放31個字串文本,因為字串要以NUL結尾。字串在記憶體中的位置取決於聲明的位置。

  定義字面量時通常會將其分配在字面量池中,多次用到同一個字面量時,池中通常只有一份副本。通常認為字面量是不可變的。你可以關閉字面量池來產生多個副本。在哪裡使用字串字面量不重要,它沒有範圍的概念。

  有些編譯器允許修改字串,因此把不希望被修改的字串聲明為常量是個不錯的選擇。

  字串初始化方式取決於變數被聲明為數組還是指標,字串所用的記憶體要麼是數組要麼是指標指向的一塊記憶體。我們可以從字串字面量或其它地方(比如標準輸入)得到字元。

char head[] = "hello man";printf("size of head is : %d\n",sizeof(head));//size of head is : 10

   可以看到 "hello man"有9個字元,但是長度是10,因為有NUL字元。還可以strcpy函數初始化數組。

char head1[10];strcpy(head1,"hello man");

   注意不要用數組的名字作為左值。

  動態記憶體分配可以提供更多靈活性,也可能讓記憶體存在更久。通常使用malloc和strcpy來初始化字串。

char* head2 = (char*) malloc (strlen("hello man")+1);strcpy(head2,"hello man");

    注意在使用malloc決定所需記憶體長度時,要為NUL預留空間,並且使用strlen而不是sizeof。sizeof會返回數組和指標的長度,而不是字串的長度。如果用字串字面量來初始化指標會導致指標指向字串字面量池。注意不要把字元字面量賦給指標,因為它是int類型。可以把字元字面量賦給解引後的指標。

*(head2 + 7) = ‘e‘;printf("head2 is %s\n", head2);//head2 is hello men

   總之字串可能位於全域或靜態記憶體(global or static array),也可能位於字串字面量池({……}),可能位於堆上(malloc),可能位於函數的棧幀裡(char array[])。字串的位置決定它能存在多久以及哪些程式可以訪問它。全域記憶體的字串會一直存在,可以被多個函數訪問;靜態字串也一直存在,但只有定義它的函數能訪問;堆上的字串可以被多個函數訪問,直到被釋放前都存在。

  標準字串操作

   比較字串的標準方法是strcmp函數。其原型如下。

int strcmp(const char* s1, const char* s2);

   如果兩個字串相等,返回0。s1大於s2,返回正數。s1小於s2,返回負數。

char command[16];printf("enter a command :");scanf("%s",command);if(strcmp(command,"quit")==0)
{ printf("you typed quit!\n");}else{ printf("i don‘t know what you typed!\n");}

   注意如果此處使用if(command == "quit")的話,被比較的實際是command的地址和字串字面量的地址。

  複製字串通常使用strcpy函數實現,其原型如下:

char* strcpy(char* s1, const char* s2);

   有一類應用程式會讀入一系列字串,挨個存入占最少記憶體的數組。先建立一個足夠長的字元串數組,其長度足以容納使用者允許輸入的最長字串,然後把字串讀入這個數組。有了讀取的字串,我們就能根據字串的長度分配合適的記憶體。

char mynames[32];char* myname[30];size_t count = 0;printf("enter a name please:");scanf("%s",mynames);myname[count] = (char*) malloc (strlen(mynames)+1);strcpy(myname[count], mynames);count++;

     可以在一個迴圈裡重複這個操作。

  兩個指標可以引用同一個字串。兩個指標引用同一個地址稱為別名。把一個指標賦值給另一個指標只是複製了字串的地址而已。

  字串拼接涉及兩個字串的合并。通常用strcat來執行這種操作。這個函數的原型為:

char* strcat(char* s1, const char* s2);

   下面是如何使用緩衝區拼接字串。

char* error = "ERROR: ";char* errormsg = "not enough memory!";char* _buffer = (char*) malloc (strlen(error) + strlen(errormsg) +1);strcpy(_buffer, error);strcpy(_buffer, errormsg);printf("%s\n", _buffer);printf("%s\n", error);printf("%s\n", errormsg);

  如果直接使用strcpy(error, errormsg)的話,可能會覆蓋error字串字面量地址後面某些未知的內容,因為我們沒有為新的字串分配獨立記憶體。拼接字串常見的錯誤就是沒有為新字串額外分配空間。此外注意不要使用字元字面量代替字串字面量作為該函數的參數。

  傳遞和返回字串   

   先定義一個函數。

size_t strLength(char* string){size_t length = 0;while(*(string++)){length++;}return length;}char simpleArray[] = "simple string";char* simplePtr = (char*) malloc (strlen("simple string")+1);strcpy(simplePtr, "simple string");printf("%d\n", strLength(simplePtr));//13

   對指標調用這個函數,只需要傳入指標的名字。對數組使用該函數也可以這麼做。在這裡數組的名字被解釋成了地址。

printf("%d\n", strLength(simpleArray)); 

   你也可以對數組的0下標使用取地址操作符,但是那樣太繁瑣了:strLength(&simpleArray[0])。

  把參數聲明為指向字元常量的指標,可以防止字串被修改。假如要讓函數返回一個由該函數初始化的字串,必須決定是否由函數調用者負責釋放分配的記憶體。如果在函數內部動態分配記憶體並返回指向該記憶體的指標,那麼調用者必須負責最終釋放該記憶體,這要求調用者必須清楚函數的使用方法。

  main函數通常是應用程式第一個執行的函數。對基於命令列的程式來說,通過為其傳遞某些資訊來開啟某些功能的開關很常見。比如linux下的ls命令會通過接受 -la 等參數來執行不同行為。C通過argcargv參數支援命令列參數。第一個參數argc是一個整數,用來指定傳遞參數的數量。系統至少會傳遞一個參數,這個參數是可執行檔的名字。第二個參數argv,通常被看做字串指標的一維數組,每個指標引用一個命令列參數。

int main(int argc, char** argv){int i =0;while(i<argc){printf("argv[%d]is %s\n",i,argv[i]);//argv[0]is ./mysenderi++;}}

  可以看到,不附加任何參數,預設內建的一個參數就是 ./mysender,是我的編譯後檔案的名字。試試用以下方式執行:./mysender -f jack -la limit = 100,此時輸出為:

argv[0]is ./mysenderargv[1]is -fargv[2]is jackargv[3]is -laargv[4]is limitargv[5]is =argv[6]is 100

   由於我把 "=" 符號用空格分開了,結果 "=" 被當做一個參數了。實際上應該用空格把每個參數分開,參數本身不應包含空格了。

  函數返回字串時,返回的實際是字串的地址。這可能是字串字面量的地址,可能是動態記憶體的地址,可能是本地字串變數的地址。

  先看第一種情況。對於靜態指向字串字面量的指標,應該注意在不同地方重複使用會覆蓋上一次的結果。字串並非總是被看做常量,可以通過命令關閉字串常量池,把字串聲明為常量可以防止字串被修改。如果返回的是動態分配的記憶體,那麼一定要注意防止記憶體泄露。返回局部變數字串的地址可能有問題,因為函數執行完畢後該處記憶體可能被別的棧幀覆寫。比如你在函數裡聲明一個字串數組並初始化了,然後返回數組的地址,這個數組所佔用的記憶體是不安全的,面臨被其它函數的棧幀覆寫的風險。

  函數指標和字串  

   通過函數指標控製程序執行是一種非常靈活的方法。

#include <stdio.h>#include <stdlib.h>#include <string.h>char* stringToLower(const char* string){char* tmp = (char*) malloc (strlen(string) + 1);char* start = tmp;while(*string != 0){*(tmp++) = tolower(*(string++));}*tmp = 0;return start;}main(){typedef int (fptroperation)(const char*, const char*);int compare(const char* s1, const char* s2){return strcmp(s1,s2);}int compareIgnoreCase(const char* s1, const char* s2){char* t1 =stringToLower(s1);char* t2 =stringToLower(s2);int result = strcmp(t1,t2);free(t1);free(t2);return result;}void sort(char* array[], int size, fptroperation operation){int swap = 1;while(swap){swap = 0;int l = 0;while(l<size-1){if(operation(array[l],array[l+1])>0){swap = 1;char* tmp = array[l];array[l] = array[l+1];array[l+1] = tmp;}l++;}}}void display(char* names[], int size){int i = 0;while(i<size){printf("%s ",names[i]);i++;}printf("\n");}char* names[] = {"jack","rose","Titanic","hello","World"};char* newnames[] = {"jack","rose","Titanic","hello","World"};sort(names, 5, compare);display(names,5);//Titanic World hello jack rose sort(newnames, 5, compareIgnoreCase);display(newnames, 5);//hello jack rose Titanic World}

    這個樣本通過使用函數指標來實現不同規則下的字串比較工作。

深入理解C指標之五:指標和字串

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.