本問介紹數組或字符串在內存中如何表示以及如何分配內存存儲字符串。程序員
首先理出對字符串能夠執行的基本操做:數組
(1)比較兩個字符串。(2)字符串複製。(3)查找字符串長度。(4)在字符串中查找子串。編輯器
下面是幾種不一樣初始化字符串的方法:函數
char *strptr="hello"; char starrray1[]="hello"; char starrray2[6]="hello"; char starrray3[4]={'a','b','c','d'};
一般狀況下,字符數組或字符串存儲在連續內存單元中,如圖所示:spa
經過相似於數組的方式訪問串元素。數組索引和指針運算均可用來訪問數組元素:指針
示例:code
#include <stdio.h> int main() { char*str = "hello pointer"; int i = 0; for (i = 0; i < strlen(str); i++) { printf("%c", str[i]); } return 0; }
運行結果:blog
上述示例中,利用數組索引訪問字符串中的字符。函數strlen(char*)用來返回字符串的長度。返回的長度不包括全部字符串總必有的最後一個空字符。索引
下面示例使用臨時指針變量char* ptr遍歷整個字符串。生命週期
#include <stdio.h> int main() { char* str = "hello pointer"; char* ptr = str; while (*ptr != '\0') { printf("%c", *ptr); ptr++; } return 0; }
運行結果以下:
從堆區分配動態內存給字符串的方法相似於數組。程序員應該特別注意,數組中分配內存給字符序列存儲字符串時須要一個額外的空字符標記字符串結尾。
#include <stdio.h> #include <malloc.h> int main() { char* src = "hello pointer"; char* dst = NULL; dst = (char*)malloc(sizeof(char)*(strlen(src)+1)); memcpy(dst, src, strlen(src)); while (*src != '\0') { printf("%c", *src); src++; } printf("\n"); while (*dst != '\0') { printf("%c", *dst); dst++; } return 0; }
運行結果以下:
定義字符串編譯器時編譯器自動在字符串結尾增長轉義字符'\0'。某些文件中字符串常量也叫作字符串文本。最有趣的是從RO扇區分配內存給字符串常量。RO扇區是存儲字符串文本和常量的只讀數據區。存儲在該區域的數據的生命週期爲程序運行的整個生命週期。
示例:
#include <stdio.h> int* foo(void); int main() { int *m = foo(); printf("打印foo函數中的i的地址%d\n", *m); return 0; } int *foo(void) { int i = 10; return &i; }
運行結果以下:
雖然輸出結果爲10,可是着個程序是錯誤的,由於指針所指向的內存位置無效,可是這不過是由於編輯器的棧的生命週期沒有結束,一點函數調用後,相應的生命週期結束,那麼變量i的內存區域將不復存在。
可是對於字符串,其內存是從RO扇區分配的,因此在程序運行的整個生命週期都有效。
字符串文本的另外一個重要特性是,一旦字符串文本被初始化其值就不能再進行修改了,至關於const char* 變量名,其中指針可被修改,可是所指的值不能。
示例:
#include <stdio.h> int main() { char *arry = "add"; arry[0] = 'b';//修改第0個索引的值,不容許。 //程序會產生分段錯誤 arry++; return 0; }
如前所述,須要注意分撇足夠內存區域來存儲字符串中的字符。其中scanf()中的%s是將輸入字符串存儲到變量的格式。
示例:
#include <stdio.h> #include <malloc.h> int main() { char arr[6]; char* strptr; printf("輸入hello\n"); scanf("%s", arr); strptr = (char*)malloc(sizeof(char) * 10); printf("輸入hello\n"); scanf("%s", strptr); }
運行結果以下:
遍歷字符串變量的每一個索引是進行讀數據或操做數據的最基本過程。經過逐個讀索引讀取字符串,這個在動態內存存儲中已經演示過了。
字符串長度爲字符串變量所春初字符的數。結束符不計算在內。
字符串複製即將字符串變量所指向的某個內存位置的存儲數據複製到另外一個內存單元所指向的內存位置的操做。下面爲複製過程
void str_copy(char *dest_str,const char * src_str) { char* stemp=src_str; char* dtemp=dest_str; while(*stemp!='\0') { *dtemp=*stemp; stemp++; dtemp++; } *dtemp='\0'; }
字符串鏈接是將給定的字符串與其餘輸入字符串鏈接起來的操做。默認狀況下字符串鏈接都從鏈接端進行。示例代碼以下
void str_cat(char* deststr,const char* srcstr) { char *dtemp=deststr; char * stemp=srcstr; while(*dtemp!='\0') { dtemp++; } while(*srcstr!='\0') { *dtemp=*srcstr; dtemp++; srcstr++; } *dtemp='\0'; }
字符串數組的元素是指向字符數組或者字符串數組的指針。數組內每一個字符串能夠是不一樣長度。圖4-2爲字符串數組存儲示意圖。每一個索引包含一個指向字符串變量長度的指針。以下圖所示
字符串數組能夠經過三種方式來講完成。聲明包括兩個重要因素:其一爲數組大小,其二爲各元素的數據容納能力。
下面給出字符串數組聲明實例以方便讀者理解內存排列和聲明。
(1)
char str_arr[6][7];
上述的字符串數組聲明中,每行能存儲最大長度爲6個字符的串。最後一個元素用來存儲結束符。下圖表示字符串數組的內存排列。
示例:
#include <stdio.h> #include <malloc.h> int main() { char arr[6][10] = { "EGRET","IBIS","MYNA","IORA","MUNIA","BULBUL" }; int i; for (i = 0; i < 6; i++) { printf("%d-%s\n", i, arr[i]); } return 0; }
程序運行結果
(2)編譯大小未知和每一個數組元素的數據容量已知時,使用數組符號。
char * str_arr2[10]
使用示例:
#include <stdio.h> #include <string.h> #include <malloc.h> int main() { char* arr[6]; char tempstring[30]; int i; for (i = 0; i < 6; i++) { printf("插入數據\n"); scanf("%s", tempstring); arr[i] = (char*)malloc(sizeof(char)*(strlen(tempstring) + 1)); strcpy(arr[i], tempstring); } printf("數組數據"); for (i = 0; i < 6; i++) { printf("%d-%s\n", i, arr[i]); } freestring(arr, 5); return 0; } freestring(char arr[], int length) { int i; for (i = 0; i <=length; i++) { free(arr[i]); } }
程序運行結果:
freestring(char arr[],int length)函數包括字符數組的地址及其長度。上述代碼表示當遍歷時只分配(動態分配)內存空間給數組每一個元素存儲的字符指針所指向的字符串。因此在freestring()中,當每次遍歷時都須要釋放動態分配內存。
(3)編譯時數組大小與每一個數組元素的數據容量都未知時使用第三中方法對字符串數組聲明。
char** dynamic_str;
下圖表示使用二級指針聲明時字符串的內存排列。咱們能夠看到,內存中對應於每一個索引數據爲變量長度。
示例:在下面的代碼中咱們使用一種新技術來存儲字符串數組。藉助二級字符指針,首先建立指針存儲空間,接着從堆中分配空間來存儲指針,接下來再從堆中分配內存存儲指針來再從堆中分配空間來存儲新字符串的指針。
#include <stdio.h> #include <string.h> #include <malloc.h> int main() { char** arr=NULL; char tempstring[30]; int i; for (i = 0; i < 6; i++) { printf("插入數據\n"); scanf("%s", tempstring); if (arr == NULL) { arr = (char**)malloc(sizeof(char*)); } else { arr = (char**)realloc(arr,sizeof(char*)*(i+1)); } arr[i] = (char*)malloc(sizeof(char)*(strlen(tempstring) + 1)); strcpy(arr[i], tempstring); } for (i = 0; i < 6; i++) { printf("%d-%s\n", i, arr[i]); } freestring(arr, 5); return 0; } int freestring(char **arr, int length) { int i; for (i = 0; i <=length; i++) { free(arr[i]); } free(arr); }
運行結果以下:
在上述狀況下釋放內存稍有些棘手,下面爲釋放內存所需遵循的基本步驟:
(1)遍歷數組每一行。
a.訪問每行時,釋放存儲字符串的相應內存。
(2)一旦完成數組遍歷,釋放存儲全部指向字符串指針的分配內存。
下圖表示內存空間的釋放步驟。如上所述,當迭代每一個索引時,首先要在索引迭代過程當中釋放相應的數組,圖中該步驟用虛線標出。完成該步後須要釋放全部元素的內存區域。圖中這一步驟用橢圓標出。