字符串: C語言中最有用、最重要的數據類型之一。
字符串:是以\0字符結尾的char類型數組。所以可以把數組和指針知識應用於字符串。
如何在程序定義字符串:
1、 字符串字面量
用雙引號括起來的內容稱為字符串字面量,也叫作字符串常量。雙引號中的字符和編譯器自動加入末尾的\0字符,都作為字符串儲存在內存中。
如果要在字符串內部使用引號,必須要在雙引號前面加上一個反斜杠(\)。
字符串字面量被視為const,就不能更改了。
字符串常量屬於靜態存儲類別。這說明如果在函數中使用字符串常量,該字符串只會被儲存一次,在整個程序的生命期內存在,即使函數被調用多次。用雙引號括起來的內容被視為指向該字符串儲存位置的指針。
2、 字符串數組和初始化
定義字符串數組時,必須讓編譯器知道需要多少空間,一種方法是足夠空間的數組儲存字符串。
指定數組大小的時候,必須確保數組的元素個數至少比字符串長度多1;
省略數組初始化聲明中的大小,編譯器會自動計算數組的大小;
數組表示法_創建字符串:char words[MAXLENGTH]=“I am a string in an array.”
指針表示法_創建字符串:char * pt1 = “Something is pointing at me.”
3、數組和指針
數組表示法_創建字符串:char words[MAXLENGTH]=“I am a string in an array.”
這種表示法,字符串字面量被存儲在靜態存儲區。數組只有在運行時才會被分配內存。此時,才將字符串拷貝到數組中。此時字符串有兩個副本,一個是在靜態內存中的字符串字母量,另一個是儲存在words數組中的字符串。
指針表示法_創建字符串:char * pt1 = “Something is pointing at me.”
編譯器為字符串在靜態存儲區預留了29個元素的空間。一旦開始運行程序,它就會為指針變量pt1留出一個儲存位置,並把字符串的地址儲存在指針變量中。
4、數組和指針的區別
char heart[] = “I love Tillie!”; ->數組名heart是常量;
const char *head = “I love Millie!”; ->指針名是head變量;
head =heart ->可以;
heart =head –>不可以,非法構造,賦值運算符左邊必須是變量;
5、字符串數組
const char *mytalents[LIM]={
"Adding number swiftly",
"Multiplying accurately",
"Stashing data",
"Following instructions to the letter",
"Understanding the C language"
};
char yourtalents[LIM][SLEN]={
"Walking in a straight line",
"Sleeping","Watching television",
"Mailing letters","Reading email"
};
字符串數組分配內存的使用率低。因為數組中儲存着字符串字面量的副本。每個字符串都被儲存了兩次。
如果要使用數組表示待顯示的字符串,建議使用指針數組。指針數組也有缺陷,就是字符串字面量不能更改。如果要改變或者為字符串輸入預留空間,不要使用指向字符串字面量的指針。
++++++++++++++++++++++++++++++++++++++++++++++++++++++
字符串的輸入:
想把字符串讀入程序,首先必須預留儲存該字符串的空間。然后用輸入函數獲取該字符串。
如何分配空間:
要做的第1件事就是分配空間。以儲存稍后讀入的字符串。意味着必須為字符串分配足夠的空間。不能指望計算機在讀取字符串時順便計算它的長度。
char *name;
scanf(“%s”,name);
name是未初始化的指針,name可能指向任何地方。所以可能會擦寫掉程序中的數據或代碼,從而導致程序異常中止。
最簡單的辦法:在聲明時顯式指明數組的大小。
char name[81];
現在name是一個已分配塊(81字節)的地址。還有一種辦法就是使用C庫函數來分配內存。
空字符:
用於標記C字符串末尾的字符;對應的字符ASCII編碼是0;
空指針:
有一個值,該值不會與任何有效的地址對應。
本質上講空字符是整數類型,占1個字節;
空指針是指針類型,是一個地址,占4個字節。
丟棄輸入行中余下的字符,是因為,輸入行中多出來的字符會被留在緩沖區中。成為下一次讀取語句的輸入。
1、gets() 函數 這個函數不安全,被摒棄了
scanf()和轉換說明%s只能讀取一個單詞,可是在程序中經常要讀取一整行輸入,而不僅僅是一個單詞。
很久前gets()就用於處理這種情況。gets()函數簡單易用,它讀取整行輸入,直至遇到換行符。
gets()函數有個問題,無法檢查數組是否裝得下行。數組名會被轉換成數組首元素的地址。get()函數只知道數組的開始處,並不知道數組中有多少個元素。
如果輸入字符串過長,會導致緩沖區溢出(buffer overflow)。多余的字符超出了指定的目標空間。如果這些多余的字符只是占用了尚未使用的內存,就不會立即出現問題。但是一旦擦寫掉程序中的其他數據,就會導致程序異常中止。
C99標准建議不要使用get()函數,甚至被C11標准摒棄了。
2、fgets()函數
函數原型:char *fgets(char *buf, int bufsize, FILE *stream); ---->從指定的文件讀取一個字符串
buf 字符型指針,指向存儲讀入數據的緩沖區的地址;---->存到哪
n 從流中讀入n-1個字符;--->讀多少個
stream 指向讀取的流; --->從哪讀
會在讀入的最后一個字符后加上串結束標志'\0'
返回值:讀取失敗或讀到文件結尾返回NULL;輸入成功時,返回 char 型指針,指向讀入的字符串內容,含換行鍵;
為什么會有換行符問題:因為輸入時會敲回車嘛,就產生換行符。函數會把換行符一並存儲起來。(讀一行字符串的范疇)
fgets()會存儲換行符,有好處也有壞處。好處就是:檢查末尾有沒有換行符就知道是否讀取了一整行。壞處就是:你可能並不想把換行符儲存在字符串中,這樣的換行符會帶來一些麻煩。
如何處理掉換行符:在已經儲存的字符串中查找換行符,將其替換成空字符
while (words[i] != ‘\n’)
i++;
words[i]=’\0’;
3、gets_s()函數
函數原型: gets_s(words,STLEN);
只從標准輸入中讀取數據,不需要第3個參數;
如果讀到換行符,會丟棄它,而不是儲存它;(不存換行符)
如果讀到最大STLEN字符數的話,.....很復雜;
沒有fgets()函數靈活,易用;不建議用;
4、s_gets()函數
函數實現如下:
1 char * s_gets(char * st, int n) 2 { 3 char * ret_val; 4 int i=0; 5 6 ret_val = fgets(st, n, stdin); //讀取成功,返回一個指針,指向輸入字符串的首字符; 7 if(ret_val) 8 { 9 while(st[i]!='\n' && st[i]!='\0') 10 i++; 11 if(st[i] =='\n') //fgets會把換行符也吃進來了,fgets會在末尾自動加上\0; 12 st[i]='\0'; 13 else //其實是'\0' 14 while(getchar() != '\n') //會把緩沖區后續的字符都清空 15 continue; 16 } 17 return ret_val; 18 }
特性:1、會吃進換行符,但是會將其替換成\0空字符;2、會把\0后續的緩沖區內容都清空,為的是不干擾下次輸入;
5、scanf()函數
函數原型:int scanf(const char * restrict format,...);
返回一個整數值:該值等於sanf()成功讀取的項數或EOF(文件結尾)。文件結尾不存在於文件中,而僅僅是一種標志,流的狀態的標志。
scanf("%d %d",&a,&b);
|
++++++++++++++++++++++++++++++++++++++++++++++++++++++
字符串的輸出:
1、puts()函數
自動在字符串末尾加上換行符,\n
函數只用來輸出字符串,沒有格式控制,里面的參數可以直接是字符串或者是存放字符串的字符數組名。
2、fputs()函數
int fputs(const char *str, FILE *stream); ---->向指定的文件寫入一個字符串;
str 這是一個數組;
stream 指向FILE對象的指針,該FILE對象標識了要被寫入字符串的流。
3、printf()函數
函數的輸出格式很多,可以根據不同格式加轉義字符,達到格式化輸出。
