【C語言】字符串和內存函數的介紹 -- 詳解

重點介紹處理字符和字符串的庫函數的使用和注意事項。
C語言中對字符和字符串的處理很是頻繁,但是C語言本身是沒有字符串類型的,字符串通常放在常量字符串中或者字符數組中。字符串常量適用于那些對它不做修改的字符串函數。

一、求字符串長度?strlen?

  • 字符串以?'\0' 作為結束標志,strlen?函數返回的是在字符串中 '\0' 前面出現的字符個數不包含 '\0' )
  • 參數指向的字符串必須要以 '\0' 結束。
  • 易錯點):注意函數的返回值為?size_t,是無符號(unsigned)的。

strlen - C++ Reference (cplusplus.com)

?【模擬實現】(三種不同方法)

#include <stdio.h>// 1、計數器方式
size_t my_strlen(const char* str)
{size_t count = 0;while (*str){count++;str++;}return count;
}// 2、不能創建臨時變量計數器
size_t my_strlen(const char* str) 
{ if(*str == '\0'){return 0;}else {return 1 + my_strlen(str+1);}
}// 3、指針-指針的方式
size_t my_strlen(char* str) 
{char *p = str; while(*p != '\0' ) {p++;} return p-str; 
}int main()
{char arr[] = "hello world";size_t count = my_strlen(arr);printf("%zu\n", count);return 0;
}


二、長度不受限制的字符串函數

1、strcpy

Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

  • 源字符串 src?必須'\0' 結束
  • 會將源字符串 src 中的 '\0' 拷貝到目標空間 dest。
  • 目標空間必須足夠大,以確保能存放源字符串。
  • 目標空間必須可變,即目標空間 dest 不可以被 const 聲明

strcpy - C++ Reference (cplusplus.com)

?【模擬實現】

#include <stdio.h>
#include <assert.h>char* my_strcpy(char* str2, const char* str1)
{assert(str1 && str2);char* ret = str2;while (*str2++ = *str1++){;}return ret;
}int main()
{char s1[] = "hello world";char s2[20] = { 0 };char* ret = my_strcpy(s2, s1);printf("%s\n", ret);return 0;
}


2、strcat

Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the fifirst character of source, and?a null-character is included at the end of?the new string formed by the concatenation of both in destination.
  • 源字符串 src?必須'\0' 結束
  • 將源字符串 src 中的 '\0' 一同拷貝到目標空間 dest ,并刪除 *dest 原來末尾的 '\0'
  • 目標空間必須有足夠大,能容納下源字符串的內容。
  • 目標空間必須可修改,即目標空間 dest 不可以被 const 聲明

strcat - C++ Reference (cplusplus.com)

🔺字符串自己給自己追加,會如何?

????????當使用 strcat 函數將一個字符串追加到自身時,可能會導致未定義的行為。strcat 函數的工作原理是在源字符串的結尾處追加目標字符串的內容,并在結尾加上空字符 '\0'。當源字符串和目標字符串是同一個字符串時,追加操作會導致源字符串的內容被破壞,因為在追加過程中,源字符串的內容會被覆蓋,最終結果會是一個不可預測的字符串。因為根據不同的編譯器和庫的版本,strcat 函數在某些情況下可能不會導致未定義行為。但是將一個字符串追加到自身仍然是一個不好的編程實踐,因為它容易引發錯誤和混亂。

????????因此,不推薦使用 strcat 函數將字符串追加到自身。如果需要將一個字符串復制到另一個字符串末尾,可以使用 strcpy 函數進行復制操作。

【模擬實現】

#include <stdio.h>
#include <assert.h>char* my_strcat(char* dest, const char* src)
{char* tmp = dest;assert(dest && src);while (*dest){dest++;}while (*dest = *src){dest++;src++;}return tmp;
}int main()
{char s1[20] = "hello";char s2[] = " world";char* ret = my_strcat(s1, s2);printf("%s\n", ret);return 0;
}


3、strcmp

This function starts comparing the fifirst character of each string. If they are equal to each other, it continues with the following pairs until the characters diffffer or until a terminating null-character is reached.

?標準規定
  • 第一個字符串大于第二個字符串,則返回大于?0?的數字
  • 第一個字符串等于第二個字符串,則返回?0
  • 第一個字符串小于第二個字符串,則返回小于?0?的數字

strcmp - C++ Reference (cplusplus.com)

🔺那么如何判斷兩個字符串?

返回值只需要滿足要求即可,比如大于 0 的數字,不一定是1,只需滿足條件即可。

strcmp 函數的比較是基于字符的 ASCII 碼進行的。它從兩個字符串的第一個字符開始逐個比較,直至找到不相等的字符或者其中一個字符串的結束符 '\0'。在比較的時候,它會將兩個字符的 ASCII 碼進行減法運算,返回結果作為比較的結果。

????????需要注意的是,strcmp 函數是區分大小寫的。也就是說,大寫字母和小寫字母被認為是不同的字符。如果需要不區分大小寫的字符串比較,可以使用 strcasecmp 函數(在某些編程環境中可能被稱為_stricmp)。

?【模擬實現】

#include <stdio.h>
#include <assert.h>int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);while (*str1 == *str2){if (*str1 == '\0'){return 0;}str1++;str2++;}return (*str1 - *str2);
}int main()
{char s1[] = "abcdef";char s2[] = "abcq";int ret = my_strcmp(s1, s2);if (ret > 0){printf(">\n");}else if (ret == 0){printf("=\n");}else{printf("<\n");}return 0;
}

??注意:根據編譯器的不同,返回的結果也不同。

VS2019?中,大于返回 1,等于返回 0,小于返回 -1。但在 Linux-gcc 中,大于返回正數,等于返回0,小于返回負數。

// 推薦
if(strcmp(p1, p2) > 0)
{printf("p1 > p2");
}
else if(strcmp(p1, p2 == 0))
{printf("p1 == p2");
}
else if(strcmp(p1, p2) < -1)
{printf("p1 < p2");
}// 不推薦
if(strcmp(p1, p2) == 1)
{printf("p1 > p2");
}
else if(strcmp(p1, p2 == 0))
{printf("p1 == p2");
} else if(strcmp(p1, p2) == -1)
{printf("p1 < p2");
}

三、長度受限制的字符串函數介紹

1、strncpy

Copies the fifirst num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
  • 拷貝 count?個字符從源字符串到目標空間。
  • 如果源字符串的長度小于 count,則拷貝完源字符串之后,在目標的后邊追加?0直到 count?
  • dest 和 src 不應該重疊(重疊時可以用更安全的 memmove 替代)。

  • 目標空間 dest?必須足夠大,以確保能夠存放源字符串。

  • 目標空間 dest?必須可變,即目標空間 dest 不可以被 const 聲明

strncpy - C++ Reference (cplusplus.com)

?【模擬實現】

#include <stdio.h>
#include <assert.h>char* my_strncpy(char* dest, const char* src, size_t count)
{assert(dest && src);char* cur = dest;while (count--){if ((*dest++ = *src++) == '\0'){break;}}if (count != 0){while (count--){*dest++ = '\0';}}return cur;
}int main()
{char s1[20] = { 0 };char s2[] = "hello world";int sz = sizeof(s2) / sizeof(s2[0]);printf("%s\n", my_strncpy(s1, s2, sz));return 0;
}


2、strncat

Appends the fifirst num characters of source to destination, plus a terminating null-character. If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.
  • 如果源字符串的長度小于 count,則只復制 '\0' 之前的內容。
strncat - C++ Reference (cplusplus.com)

?【模擬實現】

#include <stdio.h>
#include <assert.h>char* my_strncat(char* dest, const char* src, size_t count)
{assert(dest && src);char* cur = dest;while (*dest){dest++;}while (count--){if ((*dest++ = *src++) == '\0'){return cur;}}*dest = '\0';return cur;
}int main()
{char s1[20] = "hello";char s2[] = " world";size_t sz = sizeof(s2) / sizeof(s2[0]);printf("%s\n", my_strncat(s1, s2, sz)); // 從s2中取sz個追加到s1中return 0;
}


3、strncmp

  • 比較到出現另個字符不一樣或者一個字符串結束或者 count?個字符全部比較完。

strncmp - C++ Reference (cplusplus.com)

【代碼演示】?

#include <stdio.h>
#include <string.h>int main()
{const char* p1 = "abczdef";const char* p2 = "abcqwer";int ret1 = strncmp(p1, p2, 1);int ret2 = strncmp(p1, p2, 4);printf("%d %d\n", ret1, ret2);return 0;
}


四、字符串查找

1、strstr


Returns a pointer to the fifirst occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
  • 返回字符串中首次出現子串的地址。若 str2 是 str1 的子串,則返回 str2 在 str1 中首次出現的地址。如果 str2 不是 str1 的子串,則返回 NULL 。
strstr - C++ Reference (cplusplus.com)

?【模擬實現】

#include <stdio.h>
#include <assert.h>char* my_strstr(const char* str1, const char* str2)
{assert(str1 && str2);if (*str2 == '\0'){return (char*)str1;}char* cur = (char*)str1;char* s1, *s2;while (*cur != '\0'){s1 = cur;s2 = (char*)str2;while (*s1 && *s2 && (*s1 == *s2)){s1++;s2++;}if (*s2 == '\0'){return cur;}cur++;}return NULL;
}int main()
{char s1[] = "abbcde";char s2[] = "bcd";char s3[] = "abcd";char* ret1 = my_strstr(s1, s2);char* ret2 = my_strstr(s1, s3);if (ret1 == NULL){printf("未找到匹配的子串!\n");}else{printf("%s\n", ret1);}if (ret2 == NULL){printf("未找到匹配的子串!\n");}else{printf("%s\n", ret2);}return 0;
}


2、strtok

  • sep(delimit)?參數是個字符串,定義了用作分隔符的字符集合
  • 第一個參數指定一個字符串,它包含了?0?個或者多個由?sep?字符串中一個或者多個分隔符分割的標記。
  • strtok?函數找到?str?中的下一個標記,并將其用 '\0'?結尾,返回一個指向這個標記的指針。(注:strtok?函數會改變被操作的字符串,所以在使用 strtok?函數切分的字符串一般都是臨時拷貝的內容并且可修改。)
  • strtok?函數的第一個參數不為 NULL ,函數將找到?str?中第一個標記,strtok?函數將保存它在字符串中的位置。
  • strtok?函數的第一個參數為 NULL ,函數將在同一個字符串中被保存的位置開始,查找下一個標記。
  • 如果字符串中不存在更多的標記,則返回 NULL 指針。

strtok - C++ Reference (cplusplus.com)

? 注意strtok 會破壞原字符串,分割后原字符串保留第一個分割符前的字符。?

【代碼演示】

#include <stdio.h>
#include <string.h>int main()
{char arr[] = "3031899646@qq.com";printf("原字符串: %s\n", arr);const char* sep = "@."; // 創建sepchar arr1[30];char* ret = NULL;strcpy(arr1, arr); // 將數據拷貝一份,保留arr數組的內容// 分行打印切割內容for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep)){printf("%s\n", ret);}printf("分割后原字符串被破壞: %s\n", arr); // 分割后原字符串保留第一個分割符前的字符return 0;
}


五、錯誤信息報告?strerror

  • 返回錯誤碼,所對應的錯誤信息。
  • errno 是一個全局的錯誤碼變量。當 C 語言的庫函數在執行過程中,發生了錯誤后就會把對應的錯誤碼賦值到errno中。

strerror - C++ Reference (cplusplus.com)

?【模擬實現】?

#include <stdio.h>
#include <string.h>
#include <errno.h>// 錯誤碼   錯誤信息
// 0 -      No error
// 1 -      Operation not permitted
// 2 -      No such file or directory
//int main()
{char* str = strerror(errno);printf("%s\n", str);return 0;
}


六、字符操作

1、字符分類函數

?【代碼演示】

#include <stdio.h>
#include <ctype.h>int main()
{char ch1 = 'a';int ret = islower(ch1); // 判斷ch1是否為小寫 -- 非0為真printf("%d\n", ret);char ch2 = 'B';int res = islower(ch2); // 判斷ch2是否為小寫 -- 0為假printf("%d\n", res);return 0;
}

? 注意:需引入頭文件 ctype.h 頭文件。?


2、字符轉換

int tolower ( int c ); 
int toupper ( int c );

?【代碼演示】?

#include <stdio.h>int main()
{char ch = tolower('Q'); // 大寫轉小寫putchar(ch);return 0;
}


七、內存操作函數

1、memcpy

?

  • 函數?memcpy?從?src?的位置開始向后復制 count?個字節的數據到?dest?的內存位置。
  • 這個函數在遇到 '\0' 的時候并不會停下來
  • 如果?src?和?dest?有任何的重疊,復制的結果都是未定義的。

?memcpy - C++ Reference (cplusplus.com)

?【模擬實現】?

#include <stdio.h>
#include <assert.h>void* my_memcpy(void* dest, const void* src, size_t count)
{assert(dest && src);void* ret = dest;while (count--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}int main()
{char s1[] = "abcdefgh";char s2[20] = "xxxxxxxxxx";my_memcpy(s2, s1, 5);printf("%s\n", s2);return 0;
}

?【代碼演示】?

// 拷貝結構體
#include <stdio.h>
#include <string.h>struct S
{char name[20];int age;
};int main()
{struct S arr3[] = { {"張三", 20}, {"李四", 30} };struct S arr4[3] = { 0 };memcpy(arr4, arr3, sizeof(arr3));return 0;
}

?


2、memmove

  • 和?memcpy?的差別就是?memmove?函數處理的源內存塊和目標內存塊是可以重疊的
  • 如果源空間和目標空間出現重疊,就得使用?memmove?函數處理。

C語言標準要求:

memcpy 用來處理不重疊的內存拷貝,而 memmove 用來處理重疊內存的拷貝

memmove - C++ Reference (cplusplus.com)

?【模擬實現】

#include <stdio.h>
#include <assert.h>void* my_memmove(void* dest, const void* src, size_t count)
{assert(dest && src);void* ret = dest;//從前->后if (dest <= src){while (count--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}//從后->前else{dest = (char*)dest + count - 1;src = (char*)src + count - 1;while (count--){*(char*)dest = *(char*)src;dest = (char*)dest - 1;src = (char*)src - 1;}}return ret;
}int main()
{char arr[] = "abcdefgh";my_memmove(arr, arr + 3, 2);printf("%s\n", arr);return 0;
}


3、memset

?

  • memset 是以字節為單位設置內存的

memset - C++ Reference (cplusplus.com)

?【代碼實現】?

#include <stdio.h>
#include <string.h>int main()
{int arr[10] = { 0 };memset(arr, 1, 20); // 將前20個字節全部設置為1return 0;
}


4、memcmp

  • 比較從 buf1?和 buf2?指針開始的 count?個字節。

? 注意memcmp 不同于 strcmp,memcmp 遇到 '\0' 不會停止比較。

memcmp - C++ Reference (cplusplus.com)

?【代碼演示】

#include <stdio.h>
#include <string.h>int main()
{float arr1[] = { 1.0, 2.0, 3.0, 4.0 };float arr2[] = { 1.0, 3.0 };int ret = memcmp(arr1, arr2, 8); // arr1是否比arr2大,比較8個字節printf("%d\n", ret);return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/42476.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42476.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42476.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python的requests庫使用

安裝 pip install requests方法 requests.get() 發起get請求調用 查詢 requests.post() 發起post請求調用 報錯 requests.put() 發起put請求調用 修改 requests.delete() 發起delete請求調用 刪除 requests.session() 獲取requests的session對象 requests.session().request(…

【Rust】Rust學習 第十四章進一步認識 Cargo 和 Crates.io

本章會討論 Cargo 其他一些更為高級的功能&#xff0c;我們將展示如何&#xff1a; 使用發布配置來自定義構建將庫發布到 crates.io使用工作空間來組織更大的項目從 crates.io 安裝二進制文件使用自定義的命令來擴展 Cargo Cargo 的功能不止本章所介紹的&#xff0c;關于其全…

云積天赫|AIGC+營銷的排頭兵

AIGC生成式人工智能&#xff0c;正逐漸成為人們關注的焦點。AIGC的出現&#xff0c;標志著人工智能已經進入了一個全新的時代。AIGC的出現&#xff0c;也為營銷行業帶來了新的活力。那么企業該怎么利用這次AIGC浪潮&#xff0c;成為AIGC營銷的排頭兵呢&#xff1f;      “…

【JavaScript】使用js實現滑塊驗證碼功能與瀏覽器打印

滑塊驗證碼 效果圖&#xff1a; 實現思路&#xff1a; 根據滑塊的最左側點跟最右側點&#xff0c; 是否在規定的距離內【頁面最左側為原點】&#xff0c;來判斷是否通過 html代碼&#xff1a; <!DOCTYPE html> <html><head><title>滑動圖片驗證碼&…

Python爬蟲常用:谷歌瀏覽器驅動——Chromedriver 插件安裝教程

前言 大家早好、午好、晚好吖 ? ~歡迎光臨本文章 我們在做爬蟲的時候經常要使用谷歌瀏覽器驅動&#xff0c;今天分享下這個Chromedriver 插件的安裝方法。 話不多說&#xff0c;直接開搞&#xff0c;如果有什么疑惑/資料需要的可以點擊文章末尾名片領取源碼 第一步、打開谷…

使用Dockker創建vwas容器時報錯的解決方法

執行命令 docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs沒有詳細看報錯之前找了各種各樣的解決辦法&#xff0c;都無法解決。因此以后在看報錯提示的時候耐心一點看關鍵詞Error 后來才發現啟動vwas時docker報了這個錯&#xff1a; OSError: …

CANoe自動化工程的搭建

基于XMLCAPL建立自動化工程 1、導入ini文件2、新建 Test Environment3、報告類型4、代碼編寫 1、導入ini文件 工程的配置的文件&#xff0c;配置DUT相關信息&#xff0c;具體視工程而編寫內容。 2、新建 Test Environment 1、新建XML測試用例環境 2、導入XML測試用例文件 …

Jmeter常用功能-參數化介紹

JMeter也有像LR中的參數化&#xff0c;本篇就來介紹下JMeter的參數化如何去實現。 參數化&#xff1a;錄制腳本中有登錄操作&#xff0c;需要輸入用戶名和密碼&#xff0c;假如系統不允許相同的用戶名和密碼同時登錄&#xff0c;或者想更好的模擬多個用戶來登錄系統。 這個時…

mac M1安裝opencv方法及類型報錯解決

安裝opencv: pip install opencv-python pip install --user opencv-contrib-python pip install opencv-python 4.5.2.54 numpy 1.25.2 安裝過程中報錯如下&#xff1a; python-類型錯誤&#xff1a;“numpy._DTypeMeta”對象不可下標 TypeError: ‘numpy._DTypeMeta’ obje…

虛擬機/雙系統Ubuntu擴容

虛擬機Ubuntu擴容 1.需要刪除所有的快照 2.擴展虛擬機磁盤大小 虛擬機(M)→設置(s)→硬盤(SCSI)→擴展磁盤容量 3.Ubuntu內調整分區大小 安裝gparted分區工具&#xff1a;sudo apt-get install gparted 啟動gparted并resize分區 4.最后最好建一個快照&#xff0c;不然gg了…

WinPlan經營大腦垂直大模型行業報告

一、引言 在當前高度信息化的時代,企業經營管理決策的重要性已經得到了廣泛的認可。然而,在實際操作中,許多企業仍然在憑經驗、拍腦袋進行經營決策,缺乏數據工具與專職分析團隊,導致決策難、效率低等問題。針對這一問題,近年來,一種名為“WinPlan”的經營決策產品逐漸嶄…

[測試報告] 愛搜Blog 自動化測試報告

目錄 項目背景 項目功能 測試詳情 一、設計測試用例 二、功能測試步驟結果 1. 登錄頁面 2. 個人博客頁面 3. 博客詳情頁 4. 博客編輯頁 三、自動化測試及測試結果 1. 測試環境 2. 登錄測試用例&#xff1a; 3. 個人詳情頁測試用例&#xff1a; 4. 寫博客并發布測試…

Android免打包多渠道統計如何實現

摘要&#xff1a; 實際上只要完成1-2步即可實現多渠道打包&#xff0c;這也意味著&#xff0c;只要每次更新App時給出一個原始包&#xff0c;運營人員就能在后臺自己進行操作管理&#xff0c;簡單快捷到全程無需開發人員參與。 我們都知道&#xff0c;Android 市場被分割成幾十…

Go學習筆記之數據類型

文章目錄 GO數據類型數組array切片slice集合map結構體make和new GO數據類型 在go語言中&#xff0c;定義的全局數據結構不使用不會報錯&#xff0c;定義的局部數據結構必須使用&#xff0c;否則報錯&#xff1b;建議定義的數據類型就要使用&#xff0c;要么不定義。 數組array …

使用Alien對.deb包與.rpm包相互轉換

目錄 1、切換到root 2、更新yum&#xff08;更新比較耗時&#xff0c;不更新沒試行不&#xff0c;自行斟酌是否跳過這一步&#xff09; 3、卸載ibus 4、安裝Alien及其依賴包 5、安裝Alien 6、將.deb轉換成.rpm包 7、安裝RPM包 8、如果報錯 9、將.rpm轉換成.deb包 10、安…

在 Visual Studio 中編譯 Qt 資源文件和 UI 文件教程

在 Visual Studio 中編譯 Qt 資源文件和 UI 文件教程 Qt 是一個跨平臺的 C 庫&#xff0c;用于開發圖形界面和應用程序。在 Qt 開發中&#xff0c;資源文件和 UI 文件是常見的兩種資源&#xff0c;需要將它們編譯成可用的形式&#xff0c;以便在應用程序中使用。本教程將指導您…

MongoDB(三十九)

目錄 一、概述 &#xff08;一&#xff09;相關概念 &#xff08;二&#xff09;特性 二、應用場景 三、安裝 &#xff08;一&#xff09;編譯安裝 &#xff08;二&#xff09;yum安裝 1、首先制作repo源 2、軟件包名&#xff1a;mongodb-org 3、啟動服務&#xff1a…

Java工程師修煉手冊:Java數據結構面試題

Java數據結構面試題一直都是面試官喜歡問到的問題&#xff0c;在我們去面試Java的相關崗位時&#xff0c;肯定會被提問到&#xff0c;所以我們就需要提前做好準備&#xff0c;輕松的去應對&#xff1a; 1. 數據結構定義 數據結構是計算機存儲、組織數據的方式。數據結構是指相…

asp.net core讀取request內容

在Startup.cs中定義Middleware&#xff0c;設置緩存Http請求的Body數據。代碼如下。自定義Middleware請放到Configure方法的最前面。 app.Use(next > new RequestDelegate(async context > {context.Request.EnableBuffering();await next(context);})); GET請求 HttpC…

詳解23種設計模式優缺點以及解決方案

1. 單例模式&#xff08;Singleton Pattern&#xff09;&#xff1a; 優點&#xff1a;確保一個類只有一個實例&#xff0c;提供全局訪問點&#xff0c;節省資源。缺點&#xff1a;可能引入全局狀態&#xff0c;難以擴展和測試。解決方法&#xff1a;使用依賴注入來替代直接訪…