接續上文:暑期自學嵌入式——Day05(C語言階段)-CSDN博客
主頁點關注不迷路喲。你的點贊、收藏,一鍵三連,是我持續更新的動力喲!!!
主頁:
一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm=1010.2135.3001.5343
Day05(補充)
字符數組和字符串:字符串逆序輸出詳解
一、字符串逆序輸出的實現思路與方法
1. 兩種核心實現思路
實現方式 | 核心邏輯 | 優缺點分析 |
---|---|---|
僅逆序顯示(不修改原串) | 不改變字符串存儲順序,僅從最后一個有效字符倒序打印到第一個字符 | 優點:簡單直接,不破壞原數據;缺點:未真正修改字符串(僅輸出層面逆序) |
原地逆序(修改原串后顯示) | 通過交換字符位置,將原字符串改為逆序(如 "abc"→"cba"),再正常打印 | 優點:真正實現字符串逆序存儲;缺點:需修改原數據,邏輯稍復雜 |
2. 基礎實現:僅逆序顯示(不修改原串)
(1)核心步驟
-
獲取輸入:用
gets
或scanf
讀取字符串(gets
支持空格,scanf
遇空格停止)。 -
計算有效長度:用
strlen
獲取字符串實際長度(不含'\0'
)。 -
倒序循環輸出:從
len-1
(最后一個字符下標)循環到0
,逐個輸出字符。
(2)示例代碼
#include <stdio.h> #include <string.h> #define N 20 ?// 數組大小 ? int main() {char arr[N];printf("Please input a string: ");gets(arr); ?// 獲取輸入(支持空格,如"hello world") ?int len = strlen(arr); ?// 計算有效長度(不含'\0')printf("逆序輸出:");// 從最后一個字符(len-1)倒序輸出到第一個(0)for (int i = len - 1; i >= 0; i--) {putchar(arr[i]); ?// 逐個輸出字符}putchar('\n'); ?// 手動換行return 0; }
(3)關鍵技巧
-
strlen
vssizeof
:-
strlen(arr)
:返回字符串有效長度(僅'\0'
之前的字符數,如 "hello" 返回 5)。 -
sizeof(arr)
:返回數組總空間(定義為N=20
,則返回 20,包含未使用的空間)。 -
必須用
strlen
:避免打印未初始化的垃圾字符(如數組定義為 20,但輸入僅 5 個字符)。
-
3. 進階實現:原地逆序(修改原串)
(1)核心算法:雙指針法
-
指針定義:
-
i
:從頭部開始(初始i=0
)。 -
j
:從尾部開始(初始j = strlen(arr) - 1
)。
-
-
操作邏輯:交換
arr[i]
和arr[j]
,然后i++
、j--
,直到i >= j
(兩指針相遇,所有字符交換完成)。
(2)示例代碼
#include <stdio.h> #include <string.h> #define N 20 ? int main() {char arr[N];printf("Please input a string: ");gets(arr);int len = strlen(arr); ?// 雙指針交換(原地逆序)int i = 0; ? ? ? ? ? ? ? ? ?// 頭指針int j = len - 1; ? ? ? ? ? ?// 尾指針while (i < j) { ? ? ? ? ? ? // 兩指針未相遇時循環char temp = arr[i]; ? ? // 臨時變量輔助交換arr[i] = arr[j];arr[j] = temp;i++; ? ? ? ? ? ? ? ? ? ?// 頭指針后移j--; ? ? ? ? ? ? ? ? ? ?// 尾指針前移} ?// 輸出逆序后的字符串printf("逆序后的字符串:%s\n", arr); ?// 此時arr已變為逆序return 0; }
(3)過程演示(以輸入 "apple" 為例)
步驟 | i | j | 交換前 | 交換后 | 操作說明 |
---|---|---|---|---|---|
1 | 0 | 4 | "apple" | "eppla" | 交換a[0] ('a')和a[4] ('e') |
2 | 1 | 3 | "eppla" | "eplpa" | 交換a[1] ('p')和a[3] ('l') |
3 | 2 | 2 | "eplpa" | (停止) | i >= j ,循環終止 |
最終結果 | - | - | - | "elppa" | 完成逆序 |
二、輸入函數的選擇與注意事項
1. gets
、scanf
、fgets
對比
函數 | 特點 | 安全隱患 / 注意事項 |
---|---|---|
gets(arr) | 讀取整行字符串(支持空格),直到換行符為止(自動忽略換行符,添加'\0' ) | 無長度限制,若輸入超過數組大小會導致越界(已棄用,編譯器會警告) |
scanf("%s", arr) | 遇空格、換行等分隔符停止讀取,自動添加'\0' | 無法讀取帶空格的字符串(如 "hello world" 只能讀到 "hello") |
fgets(arr, N, stdin) | 讀取最多N-1 個字符(預留'\0' ),保留換行符,更安全 | 需手動處理換行符(如arr[strcspn(arr, "\n")] = '\0' 去除換行);推薦使用 |
2. 輸入函數使用建議
-
讀取帶空格的字符串:優先用
fgets
(安全),避免gets
(有溢出風險)。 -
讀取無空格的字符串:
scanf("%s", arr)
更簡潔(注意限制輸入長度,如scanf("%19s", arr)
,數組大小為 20 時)。 -
示例(
fgets
替代gets
):#include <stdio.h> #include <string.h> ? int main() {char arr[20];printf("請輸入字符串(支持空格):");fgets(arr, 20, stdin); ?// 最多讀19個字符(留1個給'\0')// 去除fgets保留的換行符(若有)arr[strcspn(arr, "\n")] = '\0'; ?// strcspn計算到'\n'的長度 ?printf("你輸入的是:%s\n", arr);return 0; }
三、知識小結
知識點 | 核心內容 | 考試重點 / 易混淆點 | 難度系數 |
---|---|---|---|
逆序顯示(方法 1) | 倒序循環輸出(i從len-1到0 ),不修改原串;依賴strlen 獲取有效長度 | sizeof (總空間)與strlen (有效長度)的區別;避免打印未初始化字符 | ??? |
原地逆序(方法 2) | 雙指針法交換字符(i=0 與j=len-1 交換,直到i>=j );需臨時變量輔助 | 循環終止條件(i < j 而非i <= j ,避免重復交換);奇 / 偶長度字符串的處理(自動兼容) | ???? |
輸入函數對比 | gets 支持空格但不安全;scanf 遇空格停止;fgets 安全且支持空格 | gets 的溢出風險;fgets 保留換行符的處理方法 | ?? |
字符串長度計算 | strlen 返回有效長度(不含'\0' ),需包含<string.h> ;也可手動循環計算 | 手動計算長度的循環邏輯(while(arr[i] != '\0') i++ ) | ?? |
數組越界預防 | 輸入時限制長度(如fgets 的N 參數、scanf 的%19s );初始化數組避免垃圾值 | gets 無長度限制導致的溢出;未初始化數組的隨機值打印 | ??? |
四、編程技巧
-
調試技巧: 逆序邏輯出錯時,可打印中間過程(如雙指針交換時的
i
、j
值和數組內容):while (i < j) {printf("交換前:i=%d, j=%d, arr=%s\n", i, j, arr);// 交換代碼printf("交換后:i=%d, j=%d, arr=%s\n", i, j, arr); }
-
邊界條件處理: 考慮特殊輸入(如空字符串
""
、單字符"a"
),確保程序不崩潰:-
空字符串:
strlen
返回 0,循環不執行(正確)。 -
單字符:
i=0
,j=0
,i < j
不成立,循環不執行(無需交換,正確)。
-
-
函數封裝: 將逆序邏輯封裝為函數,提高復用性:
// 原地逆序函數 void reverseString(char* arr) {int len = strlen(arr);int i = 0, j = len - 1;while (i < j) {char temp = arr[i];arr[i] = arr[j];arr[j] = temp;i++;j--;} }
通過以上內容,可掌握字符串逆序的兩種核心方法,理解輸入函數的特性及安全用法,同時規避數組越界等常見問題。
字符串函數:從基礎到應用
一、字符串函數概述
1. 核心概念
-
定義:C 語言標準庫(
string.h
頭文件)中封裝的專用函數,用于簡化字符串操作(如計算長度、拷貝、比較等)。 -
重要性:字符串是編程中最常用的數據形式之一,直接手寫邏輯(如遍歷計算長度)低效且易出錯,函數封裝可大幅提升開發效率。
-
使用前提:所有字符串函數必須通過
#include <string.h>
聲明頭文件,否則編譯器會報錯(“未聲明的函數”)。
二、常用字符串函數詳解
1. 字符串長度函數 strlen
(1)功能與原型
-
功能:計算字符串中 有效字符的個數(從首字符開始,直到遇到
'\0'
為止,不含'\0'
)。 -
函數原型:
size_t strlen(const char *s); ?// size_t是無符號整數類型(unsigned int)
-
參數
s
:字符串的首地址(字符數組名或字符串常量)。 -
返回值:有效字符數(無符號整數,如
"abc"
返回 3)。
-
(2)應用示例與解析
基礎示例
#include <stdio.h> #include <string.h> ? int main() {// 示例1:顯式包含'\0'的字符數組char s1[10] = {'A', '\0', 'B', 'C', '\0', 'D'};printf("s1長度:%zu\n", strlen(s1)); ?// 結果:1(遇到第一個'\0'即停止) ?// 示例2:字符串常量初始化(隱含'\0')char s2[] = "maker"; ?// 存儲為'm','a','k','e','r','\0'printf("s2長度:%zu\n", strlen(s2)); ?// 結果:5(不含'\0')return 0; }
轉義字符處理
轉義字符(如 \t
、\n
)雖由多個字符組成(反斜杠 + 字母),但在字符串中視為 單個字符,strlen
計為 1。
// 示例:轉義字符的長度計算 char s3[] = "\tab\n\v\w\e"; ?// 包含\t、\a、\n、\v、\w、\e(共6個字符) printf("s3長度:%zu\n", strlen(s3)); ?// 結果:6
特殊編碼(十六進制 / 八進制)
字符串中可通過 \xhh
(十六進制)或 \ooo
(八進制)表示字符,strlen
計為 1 個字符。
// 示例:十六進制和八進制編碼 char s4[] = "\x69\141"; ?// \x69是十六進制69(ASCII碼105,對應'i');\141是八進制141(ASCII碼97,對應'a') printf("s4內容:%s\n", s4); ?// 輸出"ia" printf("s4長度:%zu\n", strlen(s4)); ?// 結果:2
(3)strlen
與 sizeof
的核心區別
對比項 | strlen(s) | sizeof(s) |
---|---|---|
本質 | 函數,計算 有效字符數(不含 '\0' ) | 運算符,計算 變量 / 類型的總內存字節數(含 '\0' 及未使用空間) |
示例(char s[] = "maker" ) | 返回 5('m','a','k','e','r' ) | 返回 6(5 個字符 + 1 個 '\0' ) |
示例(char s[10] = "maker" ) | 返回 5(有效字符不變) | 返回 10(數組總空間固定為 10 字節) |
計算時機 | 運行時計算(遍歷字符串直到 '\0' ) | 編譯時計算(已知類型 / 數組大小) |
2. 其他重要字符串函數(基礎認知)
函數 | 功能 | 原型示例 | 關鍵注意事項 |
---|---|---|---|
strcpy | 將源字符串拷貝到目標數組(包括 '\0' ) | char* strcpy(char *dest, const char *src); | 目標數組需足夠大(否則越界);源字符串必須以 '\0' 結尾 |
strcat | 將源字符串連接到目標字符串末尾(覆蓋目標原 '\0' ,添加新 '\0' ) | char* strcat(char *dest, const char *src); | 目標數組需預留足夠空間;源和目標不能重疊(如 strcat(s, s) 會出錯) |
strcmp | 按 ASCII 碼比較兩個字符串(逐個字符對比,直到不同或 '\0' ) | int strcmp(const char *s1, const char *s2); | 返回值:s1 > s2 為正,s1 < s2 為負,相等為 0;不能用 == 直接比較字符串 |
三、知識小結
知識點 | 核心內容 | 考試重點 / 易混淆點 | 難度系數 |
---|---|---|---|
字符串函數基礎 | 需包含 <string.h> ;封裝高頻操作(如 strlen 計算長度) | 忘記包含頭文件導致的編譯錯誤;函數參數類型(const char* 表示只讀) | ?? |
strlen 函數 | 計算有效字符數(不含 '\0' );轉義字符、特殊編碼均計為 1 個字符 | 與 sizeof 的區別(運行時 vs 編譯時;有效字符 vs 總內存);遇到第一個 '\0' 即停止 | ??? |
轉義字符與特殊編碼 | \t 、\n 等轉義字符計為 1;\x69 (十六進制)、\141 (八進制)對應單個字符 | 八進制 / 十六進制編碼的轉換(如 \141 對應 'a' );轉義字符的計數規則 | ???? |
其他函數(strcpy 等) | strcpy 拷貝、strcat 連接、strcmp 比較;均依賴 '\0' 作為終止標志 | strcpy 的越界風險;strcmp 的返回值判斷(非 0 即不等,正 / 負表示大小) | ??? |
四、編程建議
-
strlen
使用注意:-
僅用于以
'\0'
結尾的字符串(字符數組無'\0'
時,strlen
會越界讀取,結果隨機)。 -
返回值是
size_t
(無符號),避免與有符號整數比較(如if (strlen(s) >= -1)
永遠成立,因無符號不會為負)。
-
-
sizeof
與strlen
對比記憶:-
記口訣:
strlen
算 “內容長度”(有效字符),sizeof
算 “房子大小”(總內存)。 -
示例:
char s[10] = "abc"
→strlen=3
(內容),sizeof=10
(房子)。
-
-
函數學習方法: 先掌握
strlen
等基礎函數的用法和原理,遇到未知函數(如strstr
查找子串)時,學會查閱 C 語言手冊(如cplusplus.com),重點關注功能、參數、返回值和注意事項。
通過以上內容,可掌握 strlen
的核心用法及字符串函數的基礎認知,明確 strlen
與 sizeof
的關鍵區別,為后續學習字符串拷貝、比較等函數奠定基礎。
字符串函數:字符串拷貝函數strcpy
詳解
一、strcpy
函數的基本介紹
1. 函數格式與核心功能
-
函數原型:
char *strcpy(char *dest, const char *src);
-
核心功能:將源字符串(
src
)完整拷貝到目標字符數組(dest
)中,包括字符串結束符'\0'
,最終目標數組成為源字符串的副本。 -
參數與返回值:
-
dest
:目標字符數組(必須是可修改的左值,如char arr[20]
,不能是字符串常量)。 -
src
:源字符串(可以是字符數組或字符串常量,如"hello"
,必須以'\0'
結尾)。 -
返回值:目標數組
dest
的首地址(方便鏈式調用,如printf("%s", strcpy(dest, src))
)。
-
2. 關鍵使用規則(必須遵守)
-
目標數組空間必須足夠大: 需能容納源字符串的所有字符(包括
'\0'
),否則會導致緩沖區溢出(覆蓋相鄰內存,程序可能崩潰或輸出亂碼)。-
示例:源字符串
"hello"
(長度 5,含'\0'
共 6 字節),目標數組至少需 6 字節(如char dest[6]
)。
-
-
源字符串必須以
'\0'
結尾:strcpy
通過'\0'
判斷拷貝結束,若源字符串無'\0'
,會越界拷貝(直到意外遇到'\0'
)。 -
源和目標不能重疊: 如
strcpy(arr+2, arr)
(目標是源的一部分)會導致拷貝結果異常(標準未定義行為)。 -
數組名不能直接賦值: 錯誤寫法:
dest = src;
(數組名是地址常量,不能被修改),必須用strcpy
逐字符拷貝。
二、strcpy
函數的使用示例與原理
1. 基礎使用示例
#include <stdio.h> #include <string.h> ? int main() {char src[] = "hello world"; ?// 源字符串(含'\0'共12字節)char dest[20]; ?// 目標數組(空間足夠大) ?// 拷貝src到deststrcpy(dest, src); ?// 輸出結果(兩者內容相同)printf("源字符串:%s(地址:%p)\n", src, src);printf("目標數組:%s(地址:%p)\n", dest, dest);return 0; }
-
輸出結果: 源字符串和目標數組均輸出
hello world
,證明拷貝完整(包括'\0'
)。
2. strcpy
的手動實現(理解底層原理)
strcpy
本質是通過循環逐字符拷貝,直到遇到源字符串的'\0'
并拷貝它。
// 手動實現strcpy功能(簡化版) char* my_strcpy(char* dest, const char* src) {// 保存目標首地址(用于返回)char* original_dest = dest; ?// 逐字符拷貝(包括'\0')while (*src != '\0') {*dest = *src; ?// 拷貝當前字符dest++; ? ? ? ?// 目標指針后移src++; ? ? ? ? // 源指針后移}*dest = '\0'; ?// 拷貝結束符(關鍵!) ?return original_dest; ?// 返回目標首地址 }
-
核心邏輯: 循環拷貝每個字符(
*src
)到目標地址(*dest
),直到*src
為'\0'
,最后單獨拷貝'\0'
(確保目標數組以'\0'
結尾)。
3. 常見錯誤與規避
(1)目標數組空間不足(高危)
char src[] = "this is a long string"; ?// 長度21(含'\0'共22字節) char dest[10]; ?// 空間不足(僅10字節) strcpy(dest, src); ?// 緩沖區溢出!覆蓋相鄰內存,程序可能崩潰
-
規避方法:
拷貝前檢查目標數組長度是否 ≥ 源字符串長度 + 1(
strlen(src)+1
):
if (sizeof(dest) >= strlen(src) + 1) {strcpy(dest, src); ?// 安全拷貝 } else {printf("目標空間不足!\n"); }
(2)忘記拷貝'\0'
(手動實現時)
// 錯誤示例:循環條件遺漏'\0' void bad_copy(char* dest, const char* src) {int i = 0;while (src[i] != '\0') { ?// 僅拷貝到'\0'前的字符dest[i] = src[i];i++;}// 未拷貝'\0',dest不是合法字符串
-
后果:目標數組無
'\0'
,用%s
輸出時會越界讀取(亂碼)。 -
正確做法:循環結束后手動添加'\0':
dest[i] = '\0'; ?// 必須添加結束符
(3)直接賦值數組名(語法錯誤)
char dest[20], src[] = "hello"; dest = src; ?// 錯誤!數組名是地址常量,不能被賦值
-
原因:數組名代表首地址(常量),不能作為左值修改,必須通過
strcpy
逐字符拷貝。
二、strcpy
函數的總結與對比
核心要點 | 說明 | 易錯點提醒 |
---|---|---|
拷貝范圍 | 從src[0] 到src[n] (n 為strlen(src) ),包括src[n] (即'\0' ) | 手動實現時需確保'\0' 被拷貝(否則目標數組不是合法字符串) |
目標數組要求 | 必須可修改(非字符串常量)、空間足夠(≥strlen(src)+1 ) | 字符串常量(如"test" )不能作為dest (const 類型不可修改) |
與直接賦值的區別 | strcpy 是逐字符拷貝內容,dest = src 是地址賦值(語法錯誤) | 數組名是常量,不能直接賦值,必須用strcpy |
安全性 | 本身不檢查空間(需程序員手動確保),存在溢出風險 | 實際開發中可使用strncpy (指定最大拷貝長度)替代,更安全 |
三、知識小結
知識點 | 核心內容 | 考試重點 / 易混淆點 | 難度系數 |
---|---|---|---|
strcpy 基本用法 | 格式:strcpy(dest, src) ;拷貝源字符串(含'\0' )到目標數組;返回dest 首地址 | 參數順序(dest 在前,src 在后);必須包含'\0' 的拷貝 | ??? |
空間要求 | 目標數組長度 ≥ strlen(src) + 1 (否則溢出) | 緩沖區溢出的后果(程序崩潰、亂碼);如何提前檢查空間(sizeof(dest) >= ... ) | ???? |
手動實現原理 | 循環逐字符拷貝,最后拷貝'\0' ;返回目標首地址 | 循環終止條件(需包含'\0' );手動添加'\0' 的必要性 | ??? |
常見錯誤 | 目標空間不足、忘記拷貝'\0' 、直接賦值數組名 | 數組名不能直接賦值(dest = src 錯誤);'\0' 對字符串的重要性(%s 輸出依賴) | ??? |
安全性提升 | 可使用strncpy(dest, src, n) 限制拷貝長度(n 為目標數組最大容量 - 1) | strncpy 需手動添加'\0' (若源字符串超長);與strcpy 的適用場景區別 | ??? |
四、編程建議
-
優先使用標準庫函數: 理解
strcpy
原理后,實際開發中直接使用標準庫版本(經過優化和測試,更可靠),無需重復造輪子。 -
強制檢查空間: 拷貝前必須驗證目標數組長度 ≥ 源字符串長度 + 1,例如:
#define DEST_LEN 20 char dest[DEST_LEN]; char src[] = "example"; ? if (strlen(src) + 1 <= DEST_LEN) {strcpy(dest, src); ?// 安全 } else {// 處理空間不足(如截斷或提示錯誤) }
-
了解替代函數:
strncpy(dest, src, n)
可指定最大拷貝長度(n
),避免溢出(但需手動添加'\0'
):strncpy(dest, src, DEST_LEN - 1); ?// 最多拷貝19個字符(留1個給'\0') dest[DEST_LEN - 1] = '\0'; ?// 確保結尾有'\0'
通過以上內容,可掌握strcpy
的用法、原理及安全注意事項,明確其在字符串操作中的核心作用 —— 實現字符串的完整拷貝,同時規避緩沖區溢出等高危錯誤。
字符串函數(進階):strcat 與 strcmp 詳解
一、字符串連接函數strcat
1. 函數基礎與核心功能
-
函數原型:
char *strcat(char *dest, const char *src);
-
功能:將源字符串(
src
)連接到目標字符數組(dest
)的末尾,形成新的字符串。 -
核心邏輯:
-
在
dest
中尋找第一個'\0'
(原字符串的結尾); -
從該位置開始,將
src
的字符(包括src
的'\0'
)逐個拷貝到dest
; -
最終
dest
以src
的'\0'
結尾,原dest
的'\0'
被覆蓋。
-
-
示例:
#include <stdio.h> #include <string.h> ? int main() {char dest[20] = "Hello"; ?// 目標數組(初始字符串:"Hello\0...")char src[] = " World"; ? ?// 源字符串(" World\0")strcat(dest, src); ? ? ? ?// 連接:在dest的'\0'位置開始拷貝srcprintf("連接后:%s\n", dest); ?// 輸出"Hello World"return 0; }
2. 使用規則與注意事項
(1)必須滿足的前提條件
-
dest
和src
均為合法字符串(均以'\0'
結尾):-
若
dest
無'\0'
,strcat
會越界尋找結尾(結果隨機); -
若
src
無'\0'
,會越界拷貝(覆蓋dest
后續內存)。
-
-
dest
空間必須足夠大: 所需空間 =strlen(dest) + strlen(src) + 1
(原dest
長度 +src
長度 + 新'\0'
)。-
示例:
dest
初始長度 5("Hello"),src
長度 6("World"),需至少 5+6+1=12 字節(dest
定義為[20]
足夠)。
-
-
dest
必須是可修改的字符數組: 不能是字符串常量(如strcat("Hello", "World")
錯誤,字符串常量不可修改)。
3. 常見錯誤與規避
(1)空間不足導致溢出
char dest[10] = "Hello"; ?// 空間10字節(當前已用6字節:5字符+'\0') char src[] = " World"; ? ?// 需6字節(5字符+'\0') strcat(dest, src); ? ? ? ?// 總需6+6=12字節 > 10 → 溢出!
-
后果:覆蓋
dest
相鄰內存,可能導致程序崩潰或輸出亂碼。 -
規避:連接前檢查空間:
if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) {strcat(dest, src); ?// 安全連接 } else {printf("目標空間不足!\n"); }
(2)dest
不是合法字符串(無'\0'
)
char dest[10] = {'H', 'i'}; ?// 未顯式添加'\0',且未初始化的元素可能非0 char src[] = "!"; strcat(dest, src); ?// 尋找'\0'時越界,可能將src連接到隨機位置
-
后果:連接位置錯誤(可能覆蓋
dest
的有效字符)。 -
規避:確保
dest
初始化為合法字符串:
char dest[10] = "Hi"; ?// 雙引號初始化,自動添加'\0'(安全)
4. 綜合應用:組合strcpy
與strcat
通過先拷貝、再連接,可構建復雜字符串:
// 示例:構建"Turbo C++" char result[20]; // 步驟1:先拷貝"Turbo"到result strcpy(result, "Turbo"); // 步驟2:連接空格 strcat(result, " "); // 步驟3:連接"C++" strcat(result, "C++"); printf("結果:%s\n", result); ?// 輸出"Turbo C++"
-
優勢:每次操作自動維護
'\0'
位置,無需手動管理結尾。
二、字符串比較函數strcmp
1. 函數基礎與比較規則
-
函數原型:
int strcmp(const char *str1, const char *str2);
-
功能:按 ASCII 碼值逐字符比較兩個字符串,返回比較結果。
-
比較規則:
-
從第一個字符開始,逐個比較對應位置的字符(
str1[i]
vsstr2[i]
); -
若遇到不同字符,返回兩者 ASCII 碼的差值(
str1[i] - str2[i]
); -
若所有字符相同,比較長度:
-
長度相同(均到
'\0'
):返回 0(相等); -
長度不同(一個先到
'\0'
):返回 “較長字符串 - 較短字符串” 的差值(通常簡化為 ±1)。
-
-
-
返回值約定:
-
>0
:str1 > str2
(str1
在字典序中更靠后); -
=0
:str1 == str2
(完全相同); -
<0
:str1 < str2
(str1
在字典序中更靠前)。
-
2. 比較示例與解析
示例 | 比較過程 | 返回值 | 結論 |
---|---|---|---|
strcmp("ABC", "ABC") | 所有字符相同,均到'\0' | 0 | 相等 |
strcmp("BBC", "ABC") | 第一個字符:'B'(66) > 'A'(65),停止比較 | 1 | str1 > str2 |
strcmp("ABD", "ABC") | 前兩位相同,第三位 'D'(68) > 'C'(67),停止比較 | 1 | str1 > str2 |
strcmp("AB", "ABC") | 前兩位相同,str1 先到'\0' (str2 還有 'C') | -1 | str1 < str2 |
strcmp("a", "A") | 'a'(97) > 'A'(65) | 32 | str1 > str2 (ASCII 差值) |
3. 使用注意事項
(1)不能用==
直接比較字符串
// 錯誤示例:比較的是地址,而非內容 if ("hello" == "world") { ... } ?// 比較兩個字符串常量的地址(永遠為假) if (str1 == str2) { ... } ? ? ? ?// 比較數組首地址(非內容)
-
正確做法:用
strcmp
比較內容:
if (strcmp(str1, str2) == 0) { ?// 內容相等printf("兩字符串相同\n"); }
(2)返回值不一定是 ±1(依賴實現)
標準僅規定返回 “正 / 負 / 零”,未規定具體數值(如strcmp("a", "A")
可能返回 32,而非 1)。
-
編程建議
:判斷時用
>0
/
<0
/
==0
,而非固定值:
if (strcmp(s1, s2) > 0) { ... } ?// 正確:s1 > s2 // 錯誤:依賴具體返回值(如1) if (strcmp(s1, s2) == 1) { ... }
(3)必須傳入合法字符串(以'\0'
結尾)
若str1
或str2
無'\0'
,strcmp
會越界比較(結果隨機):
char s1[] = {'a', 'b'}; ?// 無'\0' char s2[] = "abc"; strcmp(s1, s2); ?// 越界比較(結果不可預測)
三、知識小結
知識點 | 核心內容 | 考試重點 / 易混淆點 | 難度系數 |
---|---|---|---|
strcat 函數 | 連接src 到dest 末尾(覆蓋dest 的'\0' ,添加src 的'\0' );需dest 空間足夠 | 空間計算(strlen(dest)+strlen(src)+1 );dest 和src 必須有'\0' | ??? |
strcat 常見錯誤 | 空間不足導致溢出;dest 無'\0' 導致連接位置錯誤 | 如何提前檢查空間(sizeof(dest) >= ... );初始化dest 為合法字符串 | ???? |
strcmp 比較規則 | 逐字符比較 ASCII 碼;遇不同字符或'\0' 停止;返回正 / 負 / 零表示大小關系 | 與== 的區別(地址比較 vs 內容比較);返回值的判斷(>0 而非==1 ) | ??? |
strcmp 特殊案例 | 前序字符相同但長度不同時,較短字符串更小(如 "AB" < "ABC") | 區分 “長度” 與 “字典序”(短字符串不一定小,需前序字符相同) | ??? |
綜合應用 | strcpy 拷貝初始內容 + strcat 連接后續內容,構建復雜字符串 | 每次操作的空間檢查;'\0' 的自動維護機制 | ??? |
四、編程建議
-
strcat
使用流程:// 1. 定義足夠大的目標數組 char dest[100]; // 2. 初始化目標數組(確保有'\0') strcpy(dest, "初始內容"); // 3. 連接前檢查空間 if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) {strcat(dest, src); ?// 4. 安全連接 }
-
strcmp
正確判斷方式:int res = strcmp(s1, s2); if (res == 0) {printf("相等\n"); } else if (res > 0) {printf("s1 > s2\n"); } else {printf("s1 < s2\n"); }
-
替代函數(更安全):
-
strncat(dest, src, n)
:限制連接的最大字符數(n
),避免溢出; -
strncmp(s1, s2, n)
:僅比較前n
個字符,適合長字符串部分比較。
-
通過以上內容,可掌握strcat
(連接)和strcmp
(比較)的核心用法,理解字符串操作中 “空間管理” 和 “'\0'
維護” 的重要性,規避緩沖區溢出、比較錯誤等常見問題。
字符串函數(擴展):帶長度限制與查找類函數詳解
一、帶長度限制的拷貝與連接函數
1. 字符串部分拷貝函數strncpy
(1)函數功能與核心區別
-
函數原型:
char *strncpy(char *dest, const char *src, size_t n);
-
核心功能:從源字符串
src
拷貝最多n
個字符到目標數組dest
(區別于strcpy
的 “完整拷貝”)。 -
與
strcpy
的關鍵差異:特性 strcpy(dest, src)
strncpy(dest, src, n)
拷貝范圍 完整拷貝(直到 src
的'\0'
)最多拷貝 n
個字符(無論src
是否結束)'\0'
處理自動拷貝 src
的'\0'
僅當 src
長度≤n
時,才用'\0'
填充剩余空間(否則不補'\0'
)目標串剩余部分 被覆蓋(直到 '\0'
)未被覆蓋的部分保留原值(僅替換前 n
個字符)
(2)示例與特殊行為解析
#include <stdio.h> #include <string.h> ? int main() {char dest[10] = "dot.com"; ?// 初始:d o t . c o m \0 ...char src[] = "make"; ? ? ? ?// src長度4(不含'\0')strncpy(dest, src, 4); ? ? ?// 拷貝src的前4個字符到destprintf("dest結果:%s\n", dest); ?// 輸出"make.com"(前4個字符被替換,剩余保留)return 0; }
-
行為分析:
-
僅替換
dest
的前 4 個字符('d','o','t','.'
→'m','a','k','e'
); -
src
長度(4)等于n
(4),且src
無'\0'
("make"
的'\0'
未被拷貝),但dest
原有'\0'
在第 7 位('m','a','k','e','.','c','o','m','\0'
),因此可正常輸出。
-
(3)使用注意事項
-
'\0'
可能缺失:若src
長度≥n
,拷貝后dest
前n
個字符無'\0'
(需手動添加,否則不是合法字符串)。char dest[5], src[] = "hello"; strncpy(dest, src, 4); ?// dest為'h','e','l','l'(無'\0') dest[4] = '\0'; ? ? ? ? // 手動添加結束符(必須!)
-
空間仍需足夠:目標數組
dest
長度至少為n
(否則拷貝時越界)。 -
短源字符串的填充:若
src
長度<n
,剩余位置會自動用'\0'
填充(如src
長度 2,n=5
,則拷貝 2 個字符后補 3 個'\0'
)。
2. 字符串部分連接函數strncat
(1)函數功能與使用
-
函數原型:
char *strncat(char *dest, const char *src, size_t n);
-
功能:在
dest
的末尾追加src
的前n
個字符(或src
的全部字符,以較短者為準),自動添加'\0'
。 -
與
strcat
的區別:strcat
追加完整src
,strncat
可限制追加長度(避免溢出)。 -
示例:
char dest[20] = "Hello"; char src[] = "World!"; strncat(dest, src, 3); ?// 追加src的前3個字符"Wor" printf("結果:%s\n", dest); ?// 輸出"HelloWor"(自動添加'\0')
(2)關鍵特性
-
自動添加
'\0'
:無論追加多少字符,最終dest
都會以'\0'
結尾(安全)。 -
目標空間要求:
dest
總長度需≥strlen(dest) + min(n, strlen(src)) + 1
(原長度 + 追加長度 +'\0'
)。
3. 字符串部分比較函數strncmp
(1)函數功能與應用
-
函數原型:
int strncmp(const char *str1, const char *str2, size_t n);
-
功能:比較
str1
和str2
的前n
個字符(區別于strcmp
的 “比較到'\0'
”)。 -
返回值:與
strcmp
一致(正 / 負 / 零表示大于 / 小于 / 等于),但僅基于前n
個字符。 -
典型應用:忽略字符串末尾的無關字符(如換行符)。
// 比較"quit"和"quit\n"的前4個字符(結果相等) if (strncmp(input, "quit", 4) == 0) {printf("檢測到退出指令\n"); }
(2)注意事項
-
若
n
大于兩字符串的長度,比較到較短字符串的'\0'
時停止(與strcmp
邏輯一致)。 -
適用于 “前綴匹配” 場景(如判斷字符串是否以特定前綴開頭)。
二、忽略大小寫比較函數strcasecmp
-
函數原型:
int strcasecmp(const char *str1, const char *str2);
-
功能:比較兩個字符串,忽略字母大小寫(如
'A'
與'a'
視為相等)。 -
應用場景:用戶輸入容錯(如 “QUIT”“Quit”“quit” 均視為相同指令)。
-
示例:
if (strcasecmp(input, "quit") == 0) {printf("退出程序\n"); ?// 匹配"quit"、"QUIT"、"Quit"等 }
-
注意:非字母字符(如數字、符號)仍按 ASCII 碼嚴格比較(如
'!'
與'!'
相等,'1'
與'2'
不等)。
三、字符與子串查找函數
1. 字符查找函數strchr
與strrchr
(1)strchr
:查找字符首次出現位置
-
函數原型:
char *strchr(const char *s, int c); ?// c為字符的ASCII碼(如查找'a'可傳97或'a')
-
功能:在字符串
s
中查找字符c
首次出現的位置,返回該位置的地址;未找到返回NULL
。 -
示例:
char s[] = "hello world"; char *p = strchr(s, 'o'); ?// 查找首個'o' if (p != NULL) {printf("找到字符,位置:%ld\n", p - s); ?// 輸出4(下標從0開始)printf("從該位置開始的字符串:%s\n", p); ?// 輸出"o world" }
(2)strrchr
:查找字符最后一次出現位置
-
函數原型:
char *strrchr(const char *s, int c);
-
功能:查找字符c在s中最后一次出現的位置區別于
strchr
的 “首次”。
char s[] = "hello world"; char *p = strrchr(s, 'o'); ?// 查找最后一個'o' printf("位置:%ld\n", p - s); ?// 輸出7("world"中的'o')
(3)關鍵技巧
-
計算下標:找到的地址與字符串首地址的差值即為字符下標(
p - s
)。 -
查找
'\0'
:strchr(s, '\0')
會返回s
末尾'\0'
的地址(可用于計算字符串長度:strchr(s, '\0') - s
等價于strlen(s)
)。
2. 子串查找函數strstr
(1)函數功能與應用
-
函數原型:
char *strstr(const char *haystack, const char *needle);
-
功能:在長字符串
haystack
中查找子串needle
首次出現的位置(如在 “hello world” 中查找 “lo”)。 -
返回值:找到則返回子串首字符地址;未找到返回
NULL
。 -
示例:
char str[] = "ho are you"; char sub[] = "are"; char *p = strstr(str, sub); ?// 查找"are"在str中的位置 if (p != NULL) {printf("子串位置:%ld\n", p - str); ?// 輸出4(從0開始計數)printf("子串及后續:%s\n", p); ? ? ? // 輸出"are you" }
(2)使用注意事項
-
子串為空:若
needle
是空字符串(""
),返回haystack
的首地址(標準規定)。 -
區分大小寫:如需忽略大小寫,使用
strcasestr
(非標準但多數編譯器支持)。 -
空指針檢查:未找到時返回
NULL
,使用前必須判斷(否則訪問NULL
會崩潰)。
三、知識小結
知識點 | 核心內容 | 考試重點 / 易混淆點 | 難度系數 |
---|---|---|---|
strncpy | 拷貝src 的前n 個字符到dest ;若src 長≥n ,需手動加'\0' | 與strcpy 的區別(是否限制長度);'\0' 的手動添加(否則目標串不合法) | ??? |
strncat | 追加src 的前n 個字符到dest 末尾,自動加'\0' ;需目標空間足夠 | 目標空間計算(原長度 + 追加長度 + 1);與strcat 的長度限制差異 | ?? |
strncmp | 比較前n 個字符,用于前綴匹配或忽略末尾字符(如"quit" 與"quit\n" ) | 比較范圍僅限前n 個字符;返回值規則與strcmp 一致 | ??? |
strcasecmp | 忽略大小寫比較字符串(字母不區分大小寫,符號嚴格比較) | 應用場景(用戶輸入容錯);非字母字符的嚴格比較 | ?? |
strchr /strrchr | 查找字符首次 / 最后一次出現位置(返回地址);通過地址差值計算下標 | 地址差值轉下標(p - s );查找'\0' 的特殊用法(等價于strlen ) | ??? |
strstr | 在長串中查找子串首次出現位置(返回地址);未找到返回NULL | 子串位置計算(p - haystack );NULL 返回值的檢查(避免崩潰) | ???? |
四、編程實踐建議
-
優先使用帶長度限制的函數:
strncpy
、strncat
、strncmp
比無長度限制的函數更安全(減少溢出風險)。 -
手動添加
'\0'
:使用strncpy
時,若拷貝長度等于n
且src
無'\0'
,務必手動添加(dest[n] = '\0'
)。 -
檢查返回值:查找類函數(
strchr
、strstr
)返回NULL
時,需避免訪問(如if (p != NULL) { ... }
)。 -
結合指針運算:通過 “找到的地址 - 首地址” 快速計算下標(無需循環遍歷)。