全面學習C語言【五】:字符串、字符串數組、字符串函數(getchar&putchar、strlen、strcmp、strcpy、strcat、strchr、strstr)

十一、字符串

🎈C語言中的字符串

在C語言中 字符串是這樣的:

char msg[]={'H','e','l','l','o','\0'};

在字符數組的末尾有一個\0

在C語言中 字符串是以整數0結尾的一串字符
0和’\0’相同 標誌着字符串的結束 但只是作爲一個標記的作用 (’\0’代表的是整數0 和’0’是完全不一樣的)
但他並不是字符串的一部分 因此在計算字符串長度的時候也不包括這個0
在C語言中 字符串是以字符數組的形式存在的 (這才叫字符"串" 哈哈)
以數組或指針的形式來訪問字符串 但更多以指針訪問

🚩字符串變量

有好幾種方法可以定義字符串變量

char *str="Hello";
char strArr[]="Hello";
char strArr[10]="Hello";

在以字面量的形式定義字符串變量後 編譯器會自動在末尾生成一個0標識符

🚩字符串常量

比如 定義了一個字符串"Hello"
這個"Hello"會被編譯器轉換成一個字符數組放在內存中
這個數組的長度爲6 而不是5 因爲在末尾還有一個表示字符串結束的0 只是看不見而已

兩個相鄰的字符串常量會被自動連接(它們中間不能有任何符號)
比如:

printf("你是一個一個一個一個..."
	"啊啊啊啊啊"); // 你是一個一個一個一個...啊啊啊啊啊

當然 也可以使用/來連接 但此時行的連接處不能有引號換行後前面不能有縮進 否則縮進也會被算在字符串內
比如:

printf("你是一個一個一個一個...\
	啊啊啊啊啊"); // 你是一個一個一個一個...        啊啊啊啊啊

這樣纔對:

printf("你是一個一個一個一個...\
啊啊啊啊啊"); // 你是一個一個一個一個...啊啊啊啊啊

C語言中 字符串是以字符數組的形式存在的 因此 不能用傳統的運算符來對字符串進行運算
(Java可以用加號+來連接字符串 但是C語言不可以)
但是可以用字符串形式的字面量來初始化字符數組
比如:

char *str="Hello";

通過數組的方式可以遍歷字符串

🎈字符串的創建方式

✨字符串可以使用指針的創建方式:

char* str="Hello";

當創建了兩個相同的字符串 它們所指向的位置實際上是同一個地方

char* s1="Hello";
char* s2="Hello";
printf("%p\n",s1); // 00405064
printf("%p\n",s2); // 00405064

在字符串被創建後 實際上該常量是一個指針 指向了內存中的一塊代碼段區域 字符數組就存放在這
這塊區域是只讀的 無法寫入 因此當寫入時候 程序會崩潰
因此 實際上char *str="Hello"是const char *str=“Hello”

✨若要使用可修改的字符串 應該用數組的創建方式char strArr[]="Hello";

char s[]="Hello";
printf("%c\n",s[0]); // H
s[0]='P';
printf("%c\n",s[0]); // P

🎈字符串的創建、賦值、輸入輸出

創建方式的選擇:
  • 若創建爲數組 那麼字符串的存放位置就在當前位置
    而且作爲本地變量 空間是會被自動回收
  • 若創建爲指針 那麼這個字符串的存放位置不知道在哪
    通常用於作爲函數的指針參數 或者用於malloc的動態分配空間

🚩字符串的賦值

char *s1="Hello";
char *s2;
s2=s1;

由於採用的是指針方式創建的字符串 因此實際上在該賦值過程中 並沒有產生新的字符串
只是讓指針s2指向了指針s1所指的字符串的位置 即 對s2的任何操作 都是對s2做的
s1和s2是共享內存位置

🚩字符串的輸入和輸出

對於字符串 在C中 使用 %s 來輸入和輸出 (相信已經猜到了 s就是string)

在讀入字符串的時候 到空格 或 tab 或 回車爲止
空格 或 tab 或 回車是作爲分隔符 因此並不會被算在讀入的字符串中

在讀入的時候 有可能會遇到數組越界問題 有可能會導致程序崩潰

char word[8];
scanf("%7s",word); // 最多讀取7個 剩下留一個位置用於存放最後面的0
printf("%s##",word);

可以用 %ns的格式來 限制最多能讀取多少個字符 比如%7s爲最多讀取7個字符 超過的部分就不讀了

因此 若同時讀入到多個數組 在到達讀取上限後 下個數組會從上次讀取上限的地方開始讀
比如:

char word1[3];
char word2[3];
scanf("%2s %2s",word1,word2); // 輸入123456789
printf("%s\n",word1); // 12
printf("%s",word2); // 34

❗注意

在用指針方式創建數組的時候要進行初始化
不要這麼用:

char *s;
scanf("%s",&s);

因爲 char*並不是字符串類型 而是指針 當指向的位置無法存入 那麼程序就會崩潰
因此 在創建指針的時候 要初始化爲0
像這樣:

char *s=0;
scanf("%s",&s);

🎈字符串數組

用一個數組來表示很多字符串
可以這麼寫:

char *arr[];

其意義是 有個數組 其中的每一項存放的都不是確切的值 而是一個指針 該指針指向的位置是所要存放的字符串單元
比如 arr[0]存放的是Hello\0的指針 該指針指向一個位置 這個位置保存着Hello\0這麼一個字符串
arr[1]存放的是World\0的指針 該指針指向一個位置 這個位置保存着World\0這麼一個字符串
以此類推

例子:

int i;
char *arr[]={"AA","BB","CC","DD","EE"};
for (i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
{
	printf("%s\n",arr[i]);
}

🎈字符串函數

字符串處理函數放置於string.h中 因此若要使用字符串 則必須先引入#include <string.h>

裏面有:

  • strlen
  • strcmp
  • strcpy
  • strcat
  • strchr
  • strstr

🚩getchar & putchar / 讀 & 寫

【getchar】:

從標準輸入讀入一個字符

語法:

int getchar(void)

返回值是int 這是因爲方便返回EOF(-1)代表輸入結束

【putchar】:

向標準輸出(即黑窗口)寫一個字符

語法:

int putchar(int c)

返回值是 寫了幾個字符 若返回EOF(-1) 則代表寫失敗

鍵盤輸入的值是先交給shell 然後shell處理後再交給程序
當未按下回車前 所有輸入的值都在shell裏
然後一旦按下回車 就會將值存入shell中的緩衝區(回車符也會被存入緩衝區) 程序的getchar從shell的緩衝區中讀數據 然後進行處理

因此 當鍵盤按下Ctrl+V(Unix是Ctrl+D)時 在shell內部會通過一種方式 改變控制程序將要退出的狀態碼
然後再通過getchar去讀取的時候 讀到了程序將要退出的狀態碼 那麼便會退出
當鍵盤按下Ctrl+C 那麼直接關閉程序了

int ch;
while((ch=getchar())!=EOF)
{
	putchar(ch);
}

printf("EOF\n");

🚩strlen / 獲取長度

strlen:string length
返回傳入的字符串的長度

語法:

size_t strlen(const char *s)

例:

char c[]="Hello";
printf("%lu\n",strlen(c)); // 5
printf("%lu",sizeof(c)); // 6 因爲後面還有一個\0代表字符串的結束

🚩strcmp / 比較

strcmp:string compare
比較兩個字符串 不僅能比較是否相等 還能比較大小

語法:

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

返回的是兩個字符串之間的差值
因此 若相等 則返回 0

char s1[]="Hello";
char s2[]="Hello";
printf("%d",strcmp(s1,s2)); // 0

後面的若大於前面的 則返回 1
後面的若小於前面的 則返回 -1

char s1[]="abc";
char s2[]="CBc";
printf("%d\n",strcmp(s1,s2)); // 1

當然 該函數還有一個版本 可以限定只比較前面多少位字符

int *strncmp(char *restrict s1,const char *restrict s2,size_t n);

🚩strcpy / 複製

strcpy:string copy
第二個參數的字符串拷貝第一個參數的字符串空間中

語法:

char *strcpy(char *restrict dst,const char *restrict src)
// 參數是(目的,源),而不是(源,目的)

restrict是C99的關鍵字 意爲dst和src不能是重疊的(即字符空間不能有重疊)

返回值是dst 即拷貝後的值(或者可以說是複製品)
其目的是爲了能夠使用鏈式編程 使得代碼能夠串聯 返回值再次作爲其它函數的參數

複製一個字符串:

char src[]="Hello";
char *dst=(char*)malloc(strlen(src)+1); // 切記要加一 留給最末尾的0
strcpy(dst,src);
printf("%s",dst); // Hello

🚩strcat / 連接

strcat:string concat
將第二個參數的字符串接到第一個參數的字符串後面
返回拼接後的第一個字符串(前提是第一個字符串必須要具有足夠的空間)

語法:

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

其基本實現思路就是將第一個參數最後一位(即結尾符0)替換爲第二個參數的字符串的第一位
連接也可以看作是一種拷貝

🚧安全問題

strcpy和strcat都可能產生安全問題
若被拷貝的"目的地"沒有足夠的空間 那麼可能會造成數組越界

解決方法就是使用安全版本:

char *strncpy(char *restrict dst,const char *restrict src,size_t n);
char *strncat(char *restrict s1,const char *restrict s2,size_t n);

安全版本加了個n 其意思爲 限制最多能夠拷貝/連接多少個字符 若超出限定值 那麼會去掉 超出的範圍不會被拷貝/連接

🚩str[r]chr / 字符串中查找字符

strchr:string character
strrchr:string rear/right character

搜索函數可以在字符串中查找指定的字符(可以從左找也可以從右找)

語法:

char *strchr(const char *s,int c); // 從左找char *strrchr(const char *s,int c); // 從右找(right)

若返回NULL 則代表未找到
若找到了 則會返回要找的那個字符在該字符串中的指針

char s[]="Hello";
char *p=strchr(s,'e');
printf("%s",p); // ello

🚩str[case]str / 字符串中查找字符串

strstr:string string
strcasestr:string case string

在字符串中查找指定的字符串(可以忽略大小寫)

語法:

char *strstr(const char *s1,const char *s2); // 在字符串中查找指定的字符串
char *strcasestr(const char *s1,const char *s2); // 在字符串中忽略大小寫查找指定的字符串

若返回NULL 則代表未找到
若找到了 則會返回要找的那個字符在該字符串中的指針

char s1[]="Hello";
char s2[]="ell";
char *p=strstr(s1,s2);
printf("%s",p); // ello

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章