C語言字符串安全查找的三種方式詳解
引言
在 C 語言開發(fā)中,字符串操作是安全漏洞的 “重災(zāi)區(qū)”—— 傳統(tǒng)函數(shù)(如strchr、strrchr、strstr)缺乏邊界檢查,若輸入字符串未正確以\0結(jié)尾,極易觸發(fā)緩沖區(qū)溢出,導(dǎo)致程序崩潰或被惡意利用(如注入攻擊)。
為解決這一問題,C11 標(biāo)準(zhǔn)(ISO/IEC 9899:2011) 引入 “邊界檢查接口”(Bounds-checking interfaces),其中strchr_s、strrchr_s、strstr_s便是傳統(tǒng)查找函數(shù)的安全增強(qiáng)版本。它們通過增加長度參數(shù)、明確錯(cuò)誤處理,從根源上降低安全風(fēng)險(xiǎn),成為安全關(guān)鍵型應(yīng)用(如嵌入式系統(tǒng)、金融軟件)的首選工具。
一、安全字符串函數(shù)概述
strchr_s、strrchr_s、strstr_s保留了傳統(tǒng)函數(shù)的核心查找功能,同時(shí)新增以下安全特性:
- 強(qiáng)制傳入字符串長度參數(shù),限制操作范圍,防止越界訪問;
- 通過返回值(錯(cuò)誤碼)報(bào)告異常,而非依賴 “未定義行為”;
- 主動(dòng)校驗(yàn)無效參數(shù)(如空指針、超范圍長度);
- 檢查字符串是否在指定長度內(nèi)正確終止(避免處理不完整字符串)。
注:使用這些函數(shù)需先定義宏__STDC_WANT_LIB_EXT1__(通常在包含<string.h>前),以啟用 C11 標(biāo)準(zhǔn)的安全接口。
二、strchr_s:安全的正向字符查找
1. 函數(shù)簡介
strchr_s用于在字符串中從左到右查找第一個(gè)匹配字符,核心優(yōu)勢(shì)是通過 “長度參數(shù) + 錯(cuò)誤校驗(yàn)”,確保查找不超出緩沖區(qū)邊界。
2. 函數(shù)原型
errno_t strchr_s(char const* str, rsize_t strsz, int c, char const** result);
| 參數(shù)名 | 說明 |
|---|---|
| str | 待查找的源字符串指針(需以\0結(jié)尾,且非空) |
| strsz | 源字符串的最大長度(含\0,通常用sizeof(str)獲取數(shù)組長度) |
| c | 目標(biāo)字符(雖為int類型,實(shí)際會(huì)被轉(zhuǎn)換為char,兼容EOF) |
| result | 輸出參數(shù):存儲(chǔ)查找結(jié)果(成功則指向匹配字符,失敗則為NULL) |
返回值:
- 成功(找到 / 未找到字符):返回
0; - 失敗(參數(shù)無效 / 長度異常):返回非零錯(cuò)誤碼(如
EINVAL、ESPACE)。
錯(cuò)誤碼含義:
- :
str或result為NULL(無效指針); ESPACE:strsz為0、超過RSIZE_MAX(通常為SIZE_MAX/2),或字符串未在strsz內(nèi)終止。
3. 實(shí)現(xiàn)邏輯(偽代碼)
function strchr_s(str: const char*, strsz: rsize_t, c: int, result: const char**) -> errno_t:
// 1. 校驗(yàn)無效指針
if str == NULL or result == NULL:
return EINVAL
// 2. 校驗(yàn)長度合法性
if strsz == 0 or strsz > RSIZE_MAX:
*result = NULL
return ESPACE
// 3. 轉(zhuǎn)換目標(biāo)字符(int→char)
c_char = (char)c
// 4. 正向遍歷查找
for i from 0 to strsz - 1:
if str[i] == c_char:
*result = &str[i] // 找到匹配,記錄位置
return 0
if str[i] == '\0':
break // 已達(dá)字符串實(shí)際結(jié)尾,停止遍歷
// 5. 未找到字符(非錯(cuò)誤,返回0)
*result = NULL
return 04. 工作流程示意圖

5. 使用場景
strchr_s適用于所有需查找單個(gè)字符的場景,尤其適合:
- 處理不可信輸入(如用戶輸入的郵箱、配置項(xiàng));
- 驗(yàn)證字符串格式(如檢查郵箱是否含
@、路徑是否含分隔符); - 分割字符串(如按
:分割鍵值對(duì)"name:zhangsan")。
6. 示例代碼
#define __STDC_WANT_LIB_EXT1__ 1 // 啟用C11安全函數(shù)
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
// 場景1:驗(yàn)證郵箱格式(查找@)
char email[] = "user@example.com";
const char* result;
errno_t err; // 存儲(chǔ)錯(cuò)誤碼
// 調(diào)用strchr_s查找@
err = strchr_s(email, sizeof(email), '@', &result);
// 錯(cuò)誤處理
if (err != 0) {
printf("查找失敗!錯(cuò)誤碼:%d\n", err);
return 1;
}
// 結(jié)果處理
if (result != NULL) {
printf("找到'@',位置(索引):%td\n", result - email);
printf("郵箱用戶名:%.*s\n", (int)(result - email), email); // 截取用戶名
printf("郵箱域名:%s\n", result + 1); // 截取域名
} else {
printf("未找到'@',郵箱格式非法!\n");
}
// 場景2:查找超出字符串長度的字符(邊界測試)
char buffer[10] = "test"; // 緩沖區(qū)大小10,實(shí)際字符串長度4(含\0)
err = strchr_s(buffer, 10, 'x', &result);
if (err == 0 && result == NULL) {
printf("\n在buffer中未找到'x'(邊界檢查生效)\n");
}
return 0;
}運(yùn)行結(jié)果:
找到'@',位置(索引):4
郵箱用戶名:user
郵箱域名:example.com
在buffer中未找到'x'(邊界檢查生效)
7 注意事項(xiàng)
- 長度參數(shù)必須含
\0:strsz需包含字符串的終止符\0,如char str[] = "abc"的strsz應(yīng)為4(而非3); - 錯(cuò)誤碼優(yōu)先校驗(yàn):不能僅通過
result是否為NULL判斷成功 —— 需先檢查err是否為0(如err=EINVAL時(shí)result也為NULL); c的類型轉(zhuǎn)換:c為int是為兼容EOF(值為-1),函數(shù)內(nèi)部會(huì)轉(zhuǎn)為char,無需手動(dòng)處理;- 查找
\0的特殊情況:若c='\0',函數(shù)會(huì)返回指向字符串末尾\0的指針(符合 C11 標(biāo)準(zhǔn))。
三、strrchr_s:安全的反向字符查找
1. 函數(shù)簡介
strrchr_s是strchr_s的反向版本,用于從字符串末尾(右→左)查找最后一個(gè)匹配字符。同樣通過邊界檢查,避免越界訪問,核心場景是 “提取最右側(cè)字符相關(guān)內(nèi)容”(如文件名、后綴)。
2. 函數(shù)原型
errno_t strrchr_s(char const* str, rsize_t strsz, int c, char const** result);
參數(shù)、返回值、錯(cuò)誤碼與strchr_s完全一致,僅查找方向不同。
3. 實(shí)現(xiàn)邏輯(偽代碼)
function strrchr_s(str: const char*, strsz: rsize_t, c: int, result: const char**) -> errno_t:
// 1. 校驗(yàn)無效指針
if str == NULL or result == NULL:
return EINVAL
// 2. 校驗(yàn)長度合法性
if strsz == 0 or strsz > RSIZE_MAX:
*result = NULL
return ESPACE
// 3. 轉(zhuǎn)換目標(biāo)字符
c_char = (char)c
// 4. 第一步:定位字符串實(shí)際結(jié)尾(找到\0)
str_end = NULL
for i from 0 to strsz - 1:
if str[i] == '\0':
str_end = &str[i]
break
if str_end == NULL: // 字符串未在strsz內(nèi)終止
*result = NULL
return ESPACE
// 5. 第二步:從結(jié)尾反向查找
current = str_end - 1 // 從\0的前一個(gè)字符開始
while current >= str:
if *current == c_char:
*result = current
return 0
current = current - 1
// 6. 特殊情況:查找\0
if c_char == '\0':
*result = str_end
return 0
// 7. 未找到字符
*result = NULL
return 04. 工作流程示意圖

5. 使用場景
strrchr_s的核心場景是 “提取最右側(cè)字符后的內(nèi)容”,典型案例:
- 從文件路徑中提取文件名(如
"/home/user/test.c"→"test.c",查找最后一個(gè)/); - 獲取文件后綴(如
"image.png"→".png",查找最后一個(gè).); - 分割帶重復(fù)分隔符的字符串(如
"a,b,c,d"→"d",查找最后一個(gè),)。
6. 示例代碼(提取文件名與后綴)
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
// 安全提取文件名(支持/和\路徑分隔符)
const char* get_filename_s(const char* path, rsize_t path_len) {
const char* result;
errno_t err;
// 第一步:查找最后一個(gè)/
err = strrchr_s(path, path_len, '/', &result);
if (err != 0) return NULL; // 參數(shù)錯(cuò)誤,返回空
if (result != NULL) {
return result + 1; // 跳過/,返回文件名起始位置
}
// 第二步:若未找到/,查找最后一個(gè)\(Windows路徑)
err = strrchr_s(path, path_len, '\\', &result);
if (err != 0) return NULL;
return (result != NULL) ? (result + 1) : path; // 無分隔符則返回原路徑
}
int main() {
// 測試不同路徑格式
char path1[] = "/home/user/documents/report.pdf"; // Linux路徑
char path2[] = "C:\\Users\\user\\photos\\vacation.jpg"; // Windows路徑
char path3[] = "readme.txt"; // 無路徑(僅文件名)
// 提取文件名
const char* filename1 = get_filename_s(path1, sizeof(path1));
const char* filename2 = get_filename_s(path2, sizeof(path2));
const char* filename3 = get_filename_s(path3, sizeof(path3));
printf("路徑1的文件名:%s\n", filename1);
printf("路徑2的文件名:%s\n", filename2);
printf("路徑3的文件名:%s\n", filename3);
// 提取文件后綴(查找最后一個(gè).)
const char* ext;
errno_t err = strrchr_s(filename1, strlen(filename1) + 1, '.', &ext);
if (err == 0 && ext != NULL) {
printf("\n文件1的后綴:%s\n", ext);
}
return 0;
}運(yùn)行結(jié)果:
路徑1的文件名:report.pdf 路徑2的文件名:vacation.jpg 路徑3的文件名:readme.txt 文件1的后綴:.pdf
7 注意事項(xiàng)
- 路徑以分隔符結(jié)尾的處理:若路徑為
"/home/user/docs/"(末尾為/),result會(huì)指向該/,此時(shí)result+1為\0(空字符串),需額外判斷; - 性能對(duì)比:
strrchr_s需先遍歷到字符串結(jié)尾,再反向查找,效率略低于strchr_s,但安全性無差異; - 與
strchr_s的共性問題:同樣需注意長度參數(shù)含\0、錯(cuò)誤碼優(yōu)先校驗(yàn)、c的類型轉(zhuǎn)換。
四、strstr_s:安全的子串查找
1. 函數(shù)簡介
strstr_s用于在主串(haystack)中查找子串(needle)的首次出現(xiàn)位置,是strstr的安全版本。它通過同時(shí)校驗(yàn)主串和子串的長度,防止 “子串越界” 或 “主串未終止” 導(dǎo)致的安全問題,核心場景是 “多字符匹配”(如關(guān)鍵詞搜索、協(xié)議解析)。
2. 函數(shù)原型
errno_t strstr_s(char const* haystack, rsize_t haystacksz,
char const* needle, rsize_t needlesz,
char const** result);| 參數(shù)名 | 說明 |
|---|---|
haystack | 主串(待查找的字符串,需以\0結(jié)尾) |
haystacksz | 主串的最大長度(含\0) |
needle | 子串(目標(biāo)查找內(nèi)容,需以\0結(jié)尾) |
needlesz | 子串的最大長度(含\0) |
result | 輸出參數(shù):成功則指向主串中子串的起始位置,失敗則為NULL |
返回值與錯(cuò)誤碼:
- 成功(找到 / 未找到子串):返回
0; - 失?。悍祷胤橇沐e(cuò)誤碼(
EINVAL/ESPACE/EILSEQ)。
新增錯(cuò)誤碼EILSEQ:子串長度(實(shí)際長度)大于主串長度,不可能匹配。
3. 實(shí)現(xiàn)邏輯(偽代碼:樸素算法)
strstr_s的核心是 “主串遍歷 + 子串逐字符比對(duì)”,以下為易理解的樸素實(shí)現(xiàn)(標(biāo)準(zhǔn)庫通常用 KMP/BM 算法優(yōu)化,時(shí)間復(fù)雜度從O(n*m)降至O(n+m)):
function strstr_s(haystack: const char*, haystacksz: rsize_t,
needle: const char*, needlesz: rsize_t,
result: const char**) -> errno_t:
// 1. 校驗(yàn)無效指針
if haystack == NULL or needle == NULL or result == NULL:
return EINVAL
// 2. 校驗(yàn)長度合法性
if haystacksz == 0 or haystacksz > RSIZE_MAX or
needlesz == 0 or needlesz > RSIZE_MAX:
*result = NULL
return ESPACE
// 3. 計(jì)算主串/子串的實(shí)際長度(不含\0)
haystack_len = 0
while haystack_len < haystacksz and haystack[haystack_len] != '\0':
haystack_len += 1
if haystack_len == haystacksz and haystack[haystack_len-1] != '\0':
*result = NULL
return ESPACE // 主串未終止
needle_len = 0
while needle_len < needlesz and needle[needle_len] != '\0':
needle_len += 1
if needle_len == needlesz and needle[needle_len-1] != '\0':
*result = NULL
return ESPACE // 子串未終止
// 4. 特殊情況:子串為空(返回主串起始位置)
if needle_len == 0:
*result = haystack
return 0
// 5. 子串長于主串(直接返回未找到)
if needle_len > haystack_len:
*result = NULL
return 0
// 6. 樸素查找:主串遍歷+子串比對(duì)
max_pos = haystack_len - needle_len // 主串遍歷的最大位置
for i from 0 to max_pos:
match = true
// 逐字符比對(duì)子串
for j from 0 to needle_len - 1:
if haystack[i+j] != needle[j]:
match = false
break
if match:
*result = &haystack[i]
return 0
// 7. 未找到子串
*result = NULL
return 04. 工作流程示意圖

5. 使用場景
strstr_s是多字符匹配的核心工具,典型場景:
- 文本關(guān)鍵詞搜索(如日志中查找 “error”“warning”);
- 協(xié)議解析(如從
"HTTP/1.1 200 OK"中提取 “200” 狀態(tài)碼); - URL 處理(如從
"https://github.com"中提取"github.com"); - 敏感詞過濾(如用戶輸入中檢測違規(guī)詞匯)。
6. 示例代碼(敏感詞檢測與 URL 解析)
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <string.h>
#include <errno.h>
// 安全檢測文本是否含敏感詞
int check_sensitive_word(const char* text, rsize_t text_len,
const char* word, rsize_t word_len) {
const char* result;
errno_t err = strstr_s(text, text_len, word, word_len, &result);
if (err != 0) {
printf("檢測失??!錯(cuò)誤碼:%d\n", err);
return -1; // 錯(cuò)誤標(biāo)識(shí)
}
return (result != NULL) ? 1 : 0; // 1=含敏感詞,0=不含
}
// 安全提取URL域名(支持http/https)
const char* get_url_domain(const char* url, rsize_t url_len) {
const char* result;
errno_t err;
// 先查找"https://"(長度8)
err = strstr_s(url, url_len, "https://", 9, &result); // 9=8+1(含\0)
if (err == 0 && result != NULL) {
return result + 8;
}
// 再查找"http://"(長度7)
err = strstr_s(url, url_len, "http://", 8, &result); // 8=7+1
if (err == 0 && result != NULL) {
return result + 7;
}
return url; // 非HTTP/HTTPS協(xié)議,返回原URL
}
int main() {
// 場景1:敏感詞檢測
char log[] = "2024-06-01: user input contains 'badword'";
char sensitive[] = "badword";
int has_sensitive = check_sensitive_word(log, sizeof(log),
sensitive, sizeof(sensitive));
if (has_sensitive == 1) {
printf("日志含敏感詞:%s\n", sensitive);
} else if (has_sensitive == 0) {
printf("日志無敏感詞\n");
}
// 場景2:URL域名提取
char url1[] = "https://github.com";
char url2[] = "http://www.baidu.com";
char url3[] = "ftp://ftp.example.com"; // 非HTTP協(xié)議
const char* domain1 = get_url_domain(url1, sizeof(url1));
const char* domain2 = get_url_domain(url2, sizeof(url2));
const char* domain3 = get_url_domain(url3, sizeof(url3));
printf("\nURL1域名:%s\n", domain1);
printf("URL2域名:%s\n", domain2);
printf("URL3域名:%s\n", domain3);
return 0;
}運(yùn)行結(jié)果:
日志含敏感詞:badword URL1域名:github.com URL2域名:www.baidu.com URL3域名:ftp://ftp.example.com
7. 注意事項(xiàng)
- 雙長度參數(shù)需同步校驗(yàn):
haystacksz和needlesz均需包含各自的\0,如子串"abc"的needlesz應(yīng)為4; - 子串為空的特殊處理:根據(jù) C11 標(biāo)準(zhǔn),若
needle為空(""),result會(huì)返回主串起始位置(而非NULL); - 大小寫敏感:
strstr_s區(qū)分大小寫(如"ABC"≠"abc"),如需不敏感匹配,需先將主串 / 子串統(tǒng)一轉(zhuǎn)為大寫 / 小寫; - 性能優(yōu)化:樸素算法在 “主串 / 子串均為 AAAAA...B” 時(shí)效率低,實(shí)際開發(fā)中可優(yōu)先使用標(biāo)準(zhǔn)庫實(shí)現(xiàn)(如 GCC 的
strstr_s用 KMP 優(yōu)化)。
五、安全函數(shù)與傳統(tǒng)函數(shù)的差異對(duì)比
為清晰區(qū)分strchr_s/strrchr_s/strstr_s與傳統(tǒng)函數(shù),下表從核心特性維度對(duì)比:
| 對(duì)比維度 | strchr(傳統(tǒng)) | strchr_s(安全) | strstr(傳統(tǒng)) | strstr_s(安全) |
|---|---|---|---|---|
| 函數(shù)原型 | char* strchr(const char*, int) | errno_t strchr_s(..., rsize_t, ..., **) | char* strstr(const char*, const char*) | errno_t strstr_s(..., rsize_t, ..., rsize_t, **) |
| 返回值含義 | 直接返回查找結(jié)果(指針 / NULL) | 返回錯(cuò)誤碼(0 = 成功),結(jié)果通過輸出參數(shù)獲取 | 直接返回查找結(jié)果(指針 / NULL) | 返回錯(cuò)誤碼(0 = 成功),結(jié)果通過輸出參數(shù)獲取 |
| 長度參數(shù) | 無(依賴\0終止) | 有(strsz,強(qiáng)制邊界檢查) | 無(依賴\0終止) | 有(haystacksz+needlesz) |
| 錯(cuò)誤處理 | 未定義(如空指針會(huì)崩潰) | 明確錯(cuò)誤碼(EINVAL/ESPACE) | 未定義(如子串未終止會(huì)越界) | 明確錯(cuò)誤碼(EINVAL/ESPACE/EILSEQ) |
| 空指針處理 | 程序崩潰或不可預(yù)測行為 | 返回 EINVAL,result=NULL | 程序崩潰或不可預(yù)測行為 | 返回 EINVAL,result=NULL |
| 未終止字符串處理 | 越界訪問(緩沖區(qū)溢出風(fēng)險(xiǎn)) | 返回 ESPACE,拒絕處理 | 越界訪問(緩沖區(qū)溢出風(fēng)險(xiǎn)) | 返回 ESPACE,拒絕處理 |
| 適用場景 | 信任輸入的簡單場景 | 不可信輸入、安全關(guān)鍵場景 | 信任輸入的簡單場景 | 不可信輸入、安全關(guān)鍵場景 |
| 標(biāo)準(zhǔn)依賴 | C89 及以后(所有編譯器支持) | C11 及以后(需定義__STDC_WANT_LIB_EXT1__) | C89 及以后(所有編譯器支持) | C11 及以后(需定義__STDC_WANT_LIB_EXT1__) |
六、經(jīng)典面試題
??問:strchr_s在什么情況下會(huì)返回 EINVAL錯(cuò)誤?請(qǐng)列舉至少三種情況。??
??答:??
- 當(dāng)
str參數(shù)為NULL但strsz不為 0 時(shí) - 當(dāng)
result輸出參數(shù)為NULL指針時(shí) - 當(dāng)
strsz參數(shù)為 0 但str不為NULL時(shí)(部分實(shí)現(xiàn)) - 當(dāng)任何參數(shù)不滿足前置條件,如指針對(duì)齊問題等
問:在處理網(wǎng)絡(luò)數(shù)據(jù)包時(shí),為什么 strstr_s比 strstr更安全?請(qǐng)從攻擊者角度分析。??
??答:?? 攻擊者可以構(gòu)造特殊數(shù)據(jù)包:
- 發(fā)送不含
\0終止符的長字符串,導(dǎo)致strstr越界讀取 - 精心設(shè)計(jì)偏移使查找操作跨越緩沖區(qū)邊界
- 利用越界讀取獲取敏感信息或?qū)е鲁绦虮罎?/li>
strstr_s通過 destsz和 srcsz雙重限制,確保搜索在預(yù)定邊界內(nèi)進(jìn)行,即使面對(duì)惡意數(shù)據(jù)也能安全處理。
問:如果項(xiàng)目需要同時(shí)支持 Windows 和 Linux,如何實(shí)現(xiàn)安全字符串函數(shù)的跨平臺(tái)兼容???
??答:?? 推薦三種方案:
- ??特性檢測宏??:使用
#ifdef __STDC_LIB_EXT1__檢測編譯器支持 - ??適配層封裝??:實(shí)現(xiàn)統(tǒng)一接口,底層調(diào)用平臺(tái)相關(guān)實(shí)現(xiàn)
- ??第三方庫??:引入
safeclib等跨平臺(tái)兼容庫
最佳實(shí)踐是創(chuàng)建項(xiàng)目內(nèi)的安全字符串包裝層,集中處理平臺(tái)差異,為業(yè)務(wù)代碼提供統(tǒng)一接口。
問:將傳統(tǒng)函數(shù)strchr的調(diào)用代碼遷移為strchr_s,需注意哪些關(guān)鍵步驟?請(qǐng)舉例說明。
答:遷移需圍繞 “參數(shù)適配”“錯(cuò)誤處理”“結(jié)果獲取” 三個(gè)核心步驟,具體如下:
步驟 1:啟用 C11 安全函數(shù)
在包含<string.h>前定義宏__STDC_WANT_LIB_EXT1__,確保編譯器啟用安全接口:
#define __STDC_WANT_LIB_EXT1__ 1 #include <string.h>
步驟 2:調(diào)整函數(shù)參數(shù)(新增長度參數(shù)與輸出參數(shù))
- 傳統(tǒng)
strchr調(diào)用:char* pos = strchr(str, 'a'); - 安全
strchr_s需新增:① 長度參數(shù)(strsz);② 輸出參數(shù)(result指針)。
步驟 3:新增錯(cuò)誤處理邏輯
傳統(tǒng)函數(shù)不返回錯(cuò)誤碼,遷移后需先校驗(yàn)err是否為0,再處理result(避免將 “參數(shù)錯(cuò)誤” 與 “未找到” 混淆)。
步驟 4:調(diào)整結(jié)果獲取方式
strchr_s的結(jié)果通過輸出參數(shù)result獲取,而非直接返回。
遷移示例(從strchr到strchr_s):
傳統(tǒng)代碼:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "name:zhangsan";
// 傳統(tǒng)調(diào)用:直接返回結(jié)果
char* pos = strchr(str, ':');
if (pos != NULL) {
printf("找到':',后續(xù)內(nèi)容:%s\n", pos + 1);
} else {
printf("未找到':'\n");
}
return 0;
}遷移后代碼:
#define __STDC_WANT_LIB_EXT1__ 1 // 步驟1:啟用安全函數(shù)
#include <stdio.h>
#include <string.h>
#include <errno.h> // 步驟1:包含錯(cuò)誤碼頭文件
int main() {
char str[] = "name:zhangsan";
const char* result; // 步驟2:新增輸出參數(shù)
errno_t err; // 步驟2:新增錯(cuò)誤碼變量
// 步驟2:調(diào)用strchr_s,新增長度參數(shù)sizeof(str)
err = strchr_s(str, sizeof(str), ':', &result);
// 步驟3:優(yōu)先處理錯(cuò)誤
if (err != 0) {
printf("查找錯(cuò)誤!錯(cuò)誤碼:%d\n", err);
return 1;
}
// 步驟4:通過result獲取結(jié)果
if (result != NULL) {
printf("找到':',后續(xù)內(nèi)容:%s\n", result + 1);
} else {
printf("未找到':'\n");
}
return 0;
}關(guān)鍵注意點(diǎn):
- 長度參數(shù)必須包含
\0(如sizeof(str)而非strlen(str)); - 錯(cuò)誤碼
err的校驗(yàn)優(yōu)先級(jí)高于result(如err=EINVAL時(shí)result也為NULL,但屬于參數(shù)錯(cuò)誤,需單獨(dú)處理)。
strchr_s、strrchr_s、strstr_s是 C11 標(biāo)準(zhǔn)為解決傳統(tǒng)字符串函數(shù)安全問題而設(shè)計(jì)的核心工具,它們通過 “強(qiáng)制長度參數(shù) + 明確錯(cuò)誤處理”,從根源上規(guī)避了緩沖區(qū)溢出等風(fēng)險(xiǎn)。
開發(fā)實(shí)踐中,需根據(jù)場景選擇函數(shù):
- 簡單信任輸入場景:傳統(tǒng)函數(shù)(
strchr等)更簡潔; - 不可信輸入(如用戶輸入、網(wǎng)絡(luò)數(shù)據(jù))或安全關(guān)鍵模塊:必須使用安全函數(shù)(
strchr_s等)。
掌握安全函數(shù)的使用與遷移方法,不僅能提升代碼健壯性,更是 C 語言開發(fā)者應(yīng)對(duì)安全面試的核心考點(diǎn)。
以上就是C語言字符串安全查找的三種方式詳解的詳細(xì)內(nèi)容,更多關(guān)于C語言字符串安全查找的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++實(shí)現(xiàn)班車管理系統(tǒng)課程設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)班車管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Win10下最新版CLion(2020.1.3)安裝及環(huán)境配置教程詳解
這篇文章主要介紹了Win10下最新版CLion(2020.1.3)安裝及環(huán)境配置,CLion 是 JetBrains 推出的全新的 C/C++ 跨平臺(tái)集成開發(fā)環(huán)境,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2020-08-08
QT線程池的使用(QThreadPool類和QRunnable類)
本文主要介紹了QT線程池的使用(QThreadPool類和QRunnable類),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04

