學校里開始上數據結構了,一開始是從C語言一些相關的基礎開始講起。第一次作業主要是字符串相關的基礎知識以及編程題目。先做了一部分,整理了一下一些字符串隱含的知識和一些易誤易混的概念,算是給自己的一個復盤和歸納。
strcpy函數相關
首先來看一下這段代碼
char s[7]="abcdef",a[4]="ABC";
strcpy(s,a);
printf("%s,s);
?一開始的時候我以為會輸出ABCdef,但是只輸出了ABC。相信應該也會有人和我一樣犯這種錯誤,本質上還是對strcpy本質不了解。本質上其實是把a的首地址復制到了s上,所以打印的時候自然不會把s的內容也給輸出出來。
那我們不妨把a的長度變為9,變成ASDFGHJK(注意還有個\0),再進行復制后,輸出的s竟然還是ASDFGHJK,這就說明了一個問題:復制地址的時候,目標字符串(前者s)的長度和原字符串(后者a)的長度并不會影響復制,即使前者的長度比后者短,依舊可完整的輸出原字符串(a)。這體現了地址傳遞與值傳遞的差異所在。
同時,通過閱讀《C Primer Plus》,我還了解到了strcpy函數另外的幾個特點:
1.strcpy的返回值是char*類型,具體來說是其中第一個參數的地址,比如:strcpy(s+2,a);返回的就是s+2這個指針。
2.第一個參數不用指向目標字符串的首地址,就像上面的例子,可以使s+2,這樣我們可以進行在數組的中部進行插入,值得注意的是,在中部插入之后,原來字符串的后半部分也是不會再有的了,全部都是新復制進來的字符串。
3.strcpy函數如果要進行復制的話,其目標字符串指針必須指明地址,否則將指向一個不定的位置造成錯誤。
4.聲明一個數組(char s[2])會自動為你分配內存,但是僅聲明一個指針(char*s)不會給存儲數據用的空間,僅僅會給一個存儲地址的空間。
5.假如你是給字符串數組復制了一個常量字符串,那么后面你就不能在對他進行修改了。比如以下代碼就會報錯:
char sr[5];
strcpy(sr,"qwer");
sr="qq";
關于字符串,字符數組的賦值問題
來看下面的代碼
char a[3];
char b[]="china";
a=b;
printf("%s",a);
乍一看上去好像沒有什么問題 ,但是編譯會報錯。
?
所以我們來總結一下有關字符串,字符指針的賦值問題。
參考文章:c語言中不能將字符串賦值給字符數組,c - "error:對具有數組類型的表達式的賦值 error"
1.這個問題翻譯過來是對具有數組類型的表達式的賦值?,也就是說,作為一個左值,數組類型是不可以被進行賦值的,這個是c11的規定,就按照規定來就好了。
2.char a[10]="hello";這個語句是正確的,從中我的理解是:如果初始化和數組聲明在同時進行,是不會出問題的,此時雖然左邊是數組類型,但是右邊的字符串不再是常量,而是被理解為變量;但是如果先聲明數組再進行初始化就會有問題,這時我們進行的是賦值操作,此時右邊的字符串就是一個常量了。
3.如果聲明的是char s[12],在后面的時候就會把s理解為數組類型;如果聲明的是char*s,那后面就會把s理解為指針,數組是不能被進行賦值的,而對于指針而言,如果它作為左值,無論右邊是數組的首地址還是純粹的指針,都是正確的。下面的代碼都是正確的。
?
?
?賦值時的長度問題
來看下面的代碼:
char a[3];strcpy(a,"qwerty");
char b[3]="china";
如果將他們兩個進行輸出,會產生什么結果呢?
?
?所以根據這個我們來總結一下不同方法賦值的長度問題:
1.如果使用strcpy函數,那么,無論前面的長度是多少,都是可以的,因為本質上復制的是字符串的地址,與存儲空間無關。
2.如果直接將字符串賦值給字符數組,那么假如字符數組沒有足夠長的空間,就會把字符串進行截斷,只保留它的空間所能容納的長度。
字符串結尾‘\0’問題的研究
這篇文章的大佬講的很細致了,鏈接在這:字符串結束符'\0' -何時自動加- 字符串定義方法
我在這里還是自己再總結一遍加深印象:
如果使用字符數組進行對字符串的定義:
1.char s[5]={"ABCDE"};這種方法不會給字符串末尾加上‘\0’。如果字符串長度等于數組所聲明的長度,他是不會加上‘\0’的。
2.char s[10]={"ABCDE"};字符串長度小于數組所聲明的長度,會把還未初始化的元素自動變成‘\0’,也就是說后面五個元素全部都是'\0'。
3.char s[]="ABCDE";一開始沒有聲明數組長度,進行賦值以后,會在后面加上'\0'。對比1,我試了一下,打印sizeof(s)的話,第一種是5,而第三種是6,這說明第三種方法后面有‘\0’而第一種沒有。
4.char s[]={'A','B','C',‘D’,‘E’};這種方法相當于一個字符一個字符的賦值,所以也不會加上‘\0’.
5.char s[10]={'A','B','C',‘D’,‘E’};和前面的3類似,后面五個會被初始化為‘\0’。
如果使用字符指針對字符串進行定義
使用指針的話,就沒什么大問題了,后面一定是會加上‘\0’的,前面說過,字符指針作為左值是既可以初始化(char* s=“qwert”),又可以直接用自己賦值的(char*s;s=“qwert”)。而字符數組只能夠進行前者(char s[10]="qwert"),不能夠進行后者(char s[10]; s="qwert"),如果想要賦值,必須使用strcpy函數。
轉義字符的小問題
來看代碼
char s[]="\t\v\\\0wwww";
printf("%d",strlen(c));
?輸出的長度是3,我們知道\0是字符串的終止符,所以前面是字符串的內容,關于長度是三這個問題,總結一下:
1.\? 作為C語言中的轉義字符,是用來表達在ASCII碼表中不可見字符和控制符的。類似于\t,\v,\n,他們是控制符,如果你在ASCII表里去尋找這些字符,他們是不會以\t,\v,\n的形式出現的。因此如果想要輸出他們,就需要使用轉義字符和別的字母一起來實現控制符的意思。這些控制符是作為一個整體存在的,也就是說\t,\v是只占一個字符的位置的。
2.\還有的功能是轉換那些單獨打印無法打印的字符的,比如\,",如果你想單獨在printf的引號中去打印這些字符,是無法輸出的,這就需要轉義字符了。比如\\,實際上最后只輸出一個\。
綜上,上面的字符串相當于是有\t,\v,\三個字符,注意,strlen是不會讀取‘\0’的,而sizeof才會輸出包含‘\0’的長度。
指針數組與數組指針
char* s[]={"abc","ABC","QWE","qqqqq"};
上面的這個數組與之前的有所區別,我們可以看到,s前面的類型是char*,也就是說,存放在這個數組里的是指向char的指針,也就是里面字符串的首地址。我們可以把這個理解為一個二維的字符串數組。
下面來介紹幾個輸出的內容:
1.*(s+2)或者s[2],是字符串的首地址,用%s來輸出,代表的是QWE這整個字符串。?
2.*((*(s+2))+2)或者s[2][2]輸出的是E這個字符。
3.*s[2]或者**(s+2),用%c輸出,代表的是Q這個字符,也就是說,把首地址所指向的字符給打印了出來。(這個點容易混淆)
歸根到底,s+i代表的是指向字符串的指針的指針(有點拗口),是一個二級指針;s[i]代表的是指向字符串的指針,是一個一級指針。從此延伸,加上*就是一層一層的取內容了。比如如果想用s+i來取出字符串本身,那就要兩個*,第一個*代表的是字符串的首地址,第二個*才是字符串本身。
char(*s)[10];
這里的s代表的是這樣的含義:首先s是一個指針,去掉*和變量名之后,剩下char[10],也就是說,s所指向的內容是一個長度為10的char型數組,指向的是數組!
我們可以把它叫做行指針,因為在二維的數組中,每一行都是一個一維數組,而這個指針正是指向一個一維的數組的。?
注意數組指針和數組的首地址的區別。數組指針本身雖然也是數組首地址,兩者僅僅是在數值上相等,兩者所指向的內容并不一樣。數組指針指向的是數組整體,而數組首地址如果不是字符數組,而是其他類型比如整型數組的時候,指向的僅僅是第一個元素。比如以下代碼:
char str[3][10]={"qwertyuiop","qwr","uiop"};
char(*s)[10];
s=str;
s+=2;
printf("%s",s);
最后輸出的時候,輸出的是uiop,也就是說,對于一個數組指針,它的加減是以所指向的數組長度決定的,每加一次,他都會跳過整個數組的長度,如果在二維數組中的話,就相當于他跳到了下一行?。
相比之下,普通的數組首地址如果加1的話,由于它所指向的是首元素,因此只會跳到下一個元素去,不會把整個數組越過。
也就是說,指針的加減是由指針所指向的數據類型決定的。加減都會以數據的字節數為單位。這從另外一個角度展示了數組指針和數組首地址的區別。
好了以上就是我自己整理過的關于字符串的很多疑難雜癥和邊邊角角,希望對大家有幫助。
整理不易,最后給個一鍵三連再走吧~~~