目錄
前言
字串函式與記憶體函式總匯(講解的)
字串函式介紹
strlen(字串長度計算函式)
strcpy/strncpy(字串拷貝函式)
strcat/strncat(字串拼接函式)
strcmp/strncmp(字串比較函式)
strstr(字串查找函式)
strtok(字串切分函式)
strerror(回傳錯誤原因的描述字串函式)
記憶體函式介紹
memcpy(記憶體拷貝函式)
memmove(記憶體移動函式)
memcmp(記憶體比較函式)
前言
本章主要講解:
- 字符和字串的庫函式的使用和注意事項
字串函式與記憶體函式總匯(講解的)
- 求字串長度:strlen
- 長度不受限制的字串函式:strcpy strcat strcmp
- 長度受限制的字串函式:strncpy strncat strncmp
- 字串查找:strstr strtok
- 錯誤資訊報告:strerror
- 記憶體操作函式:memcpy memmove memset memcmp
寫在前面的話:
C語言本身是沒有字串型別的
字串通常放在常量字串(不做修改)中或者字符陣列(可以修改)中來處理
字串函式介紹
strlen(字串長度計算函式)
- 定義:
size_t strlen ( const char * str );
- 注意:
- 字串以 '\0' 作為結束標志,strlen函式回傳的是在字串中 '\0' 前面出現的字符個數(不包 含 '\0' )
- 引數為字符指標,且是指向的字串必須要以 '\0' 結束的指標(否則會出錯)
- size_t 的定義為 typedef unsigned int size_t;即 strlen 函式回傳型別為無符號整型
- 易錯題:
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "abc";
if (strlen(str2) - strlen(str1) > 0)
{
printf("呵呵\n");
}
else
{
printf("哈哈\n");
}
return 0;
}
//輸出結果:呵呵
//解釋:對于strlen函式回傳的是無符號整形,無符號數的加級訓是無符號數(永遠大于0)
- 函式演示:
#include<stdio.h>
#include<string.h>
int main()
{
char ch[] = "abcdef";
printf("%d\n", strlen(ch));
return 0;
}
//輸出結果:6
- 模擬實作:
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
//對于傳入的指標我們并希望對它指向的物件的內容進行修改,我們可以用const進行修飾
{
assert(str);//傳入指標不能為NULL,記得包含頭檔案<assert.h>
size_t len = 0;
while (*str++)//*的優先級高于后置++,如果為'\0'則不進入回圈('\0'的ASCII碼值為0)
{//進入后指標str++
len++;
}
return len;
}
strcpy/strncpy(字串拷貝函式)
- 定義:
char *strcpy( char *strDestination//目標字串//,
const char *strSource//來源字串);
char *strncpy( char *strDest, const char *strSource, size_t count//拷取長度(位元組為單位));
- 區別:
strcpy:一直拷貝到'\0'(包括'\0')(并不安全)
strncpy:引數count用來控制拷貝字串的長度(如果超過了源字串的長度則會在拷完后再補0)(相對安全)
- 注意:
- 源字串必須以 '\0' 結束
- 目標空間必須足夠大(以確保能存放源字串),且必須可修改
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello world!";
char str2[20] = { 0 };
char str3[20] = { 0 };
strcpy(str2, str1);
strncpy(str3, str1, 2);
printf("%s\n", str2);
printf("%s\n", str3);
return 0;
}
//輸出結果:
//Hello world!
//He
- 模擬實作:
char* my_strcpy(char* des, const char* src)
{
assert(des&&);//指標不為NULL
char* ret = des;//記錄字串首元素地址
while (*des++ = *src++)//先*指標進行賦值,賦值后對des指向的內容進行判斷,再進行指標++
{
;
}
return ret;//回傳字串首元素地址
}
#include<assert.h>
char* my_strncpy(char* des, const char* src, int n)
{
assert(des && src);
char* ret = des, *cp = (char*)src;
int count = 0;
while (*cp++)//計算源字串長度
{
count++;
}
for (int i = 0; i < n; i++)
{
if (i < count)
{
*des++ = *src++;
}
else//拷貝個數大于長度補0
{
*des++ = '0';
}
}*des = '\0';//末尾加上結束符
return ret;
}
strcat/strncat(字串拼接函式)
- 定義:
char *strcat( char *strDestination, const char *strSource );
char *strncat( char *strDest, const char *strSource, size_t count );
- 區別:
strcat:將源字串拼接到目標字串后面(從目標字串的'\0'位置開始拼接,一直拼接到源字串'\0'處,包括'\0')
strncat:引數
count用來控制拼接字符個數(大于源字串個數也只拼接到源字串的'\0')
- 注意:
- 源字串必須以 '\0' 結束
- 目標空間必須足夠大(以確保能存放拼接的字符) ,且必須可變
函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[100] = "hello ";
char str2[100] = "hello ";
char str3[] = "world!";
strcat(str1, str3);
strncat(str2, str3, 4);
printf("%s\n", str1);
printf("%s\n", str2);
return 0;
}
//輸出結果:
//hello world!
//hello worl
- 模擬實作:
char* my_strcat(char* des, const char* str)
{
assert(des && str);
char* ret = des;
//找到des最后一個字符
while (*des)
{
des++;
}
//將str中字串拷貝至des末尾
while (*des++ = *str++)
{
;
}
return 0;
}
char* my_strncat(char* des, const char* str, size_t n)
{
assert(des && str);
char* ret = des;
while (*des)
{
des++;
}
for (size_t i = 0; i < n && *str; i++)
{
*des++ = *str++;
}
*des = '\0';//末尾追加結束符
return ret;
}
strcmp/strncmp(字串比較函式)
- 定義:
int strcmp( const char *string1, const char *string2 );
int strncmp( const char *string1, const char *string2, size_t count );
- 區別:
strcmp:比較每個字符對應的ASCII碼值大小
strncmp:引數count控制了比較字符數量(比較字符個數不超過兩字串個數(含'\0')較少者)
- 標準規定:
第一個字串大于第二個字串,則回傳大于0的數字
第一個字串等于第二個字串,則回傳0
第一個字串小于第二個字串,則回傳小于0的數字
- 注意:
比較到出現另個字符不一樣或者一個字串結束或者count個字符全部比較完
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcd";
char str2[] = "abcd";
char str3[] = "accd";
printf("%d\t", strcmp(str1, str2));
printf("%d\t", strcmp(str1, str3));
printf("%d\t", strncmp(str1, str2,2));
printf("%d\t", strncmp(str1, str3,2));
return 0;
}
//輸出結果:0 -1 0 -1
- 模擬實作:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
//字符相等就比較下一個,中間如果存在兩字符不相等則結束遍歷
while (*str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
}
//結果回傳兩者ASCII碼差值
return (*str1 - *str2);
}
int my_strncmp(const char* str1, const char* str2, size_t n)
{
assert(*str1 && *str2);
for (size_t i = 0; i < n - 1 && *str1 && *str2; i++)
{
//不同時直接結束,再回傳此刻兩者的差值
if (*str1 != *str2)
break;
str1++;
str2++;
}
//或結束遍歷后(n-1次)直接比較最后一位字符
return (*str1 - *str2);
}
strstr(字串查找函式)
- 定義:
char *strstr( const char *string, const char *strCharSet );
注:如果字串
strCharSet在string出現,則回傳string中第一次出現該字串的首地址,否則回傳NULL
- 模擬實作:
- 首先在str1中找到與str2首字符相同的字符,后對str2后面的字符進行逐個比較
- 如果在后續逐個比較程序中出現了不同的字符,這時候就需要str1回傳到之前剛開始對字符比較的地方的后一個位置再進行比較,且str2需要回傳到首字符
- 如果在后續逐個比較程序中,str2指向的字符為\0這就代表在str1中找到了str2這個字串,則回傳str2首字符對應于str1所在的地址
- 還有則是遍歷后str1指向的字符為\0(在沒滿足str2指向的字符為\0時),這就表示在str1中找不到str2這個字串,則回傳NULL
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
if (*str2 == '\0')
return (char*)str1;//如果str2指向的物件如:char ch[]="";(即內容為'\0')直接回傳str1
const char* s1;
const char* s2;
const char* p = str1;
//str1指向的物件內容不為'\0'則進入回圈
while (*p)
{
//進入回圈調整s2,s1位置
s1 = p;
s2 = str2;
//相同則進行遍歷
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
//s2遍歷到結束符即找到了,回傳此次開始的地址
if (*s2 == '\0')
{
return (char*)p;
}
//(上面條件不滿足)s1到了'\0'則說明開始位置p都到了結束符,那么已經找不到了
if (*s1 == '\0')
{
return NULL;
}
//此次查找沒有找到,則使開始位置p指向下一個位置
p++;
}
return NULL;
}
strtok(字串切分函式)
- 定義:
char *strtok( char *str//被切分物件,
const char *sep//分隔符號集合);
- 注意:
- 第一個引數指定一個字串,它包含了0個或者多個由
sep字串中一個或者多個分隔符分割的標記strtok函式找到str中的首個分隔符,并將其用'\0'替代,且回傳分隔符前一個字串首元素地址- strtok函式的第一個引數不為 NULL ,函式將找到strToken中首個分隔符,strtok函式會記憶該分隔符后一個字符的位置
- strtok函式的第一個引數為NULL,函式將在同一個字串中被記憶的位置開始,查找下一個分隔符
- 如果字串中不存在更多的標記,則回傳 NULL 指標
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "- This, a sample string.";
char* pch;
pch = strtok(str, " ,.-");
while (pch != NULL)
{
printf("%s\t", pch);
pch = strtok(NULL, " ,.-");
}
return 0;
}
//輸出結果:This a sample string
#include <stdio.h>
#include <string.h>
int main()
{
char* p = "abcde@163.com";
const char* sep = ".@";
char arr[30];
char* str = NULL;
strcpy(arr, p);//將資料拷貝一份,處理arr陣列的內容
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\t", str);
}
}
//輸出結果:abcde 163 com
- 模擬實作:
char* strtok (char* str, const char* delim)
{
// 生成替換字符表
char table[256] = {0};
while (*delim != '\0')
{
table[*delim] = 1;
delim++;
}
// 使用 static 型別指標保存上一次函式呼叫時的字串地址
static char* pstr = NULL;
if (str != NULL)
{
pstr = str;
}
// 保證 pstr 指向以非替換字符為首的子字串
while (*pstr != '\0' && table[*pstr] == 1)
{
pstr++;
}
// ret 保存回傳子字串的首地址
char* rst = (*pstr != '\0') ? pstr : NULL;
while (*pstr != '\0')
{
if (table[*pstr] == 1)
{
// 切割得到子字串,且 pstr 最后指向子字串的下一字符
*pstr++ = '\0';
break;
}
else
{
pstr++;
}
}
return rst;
}
strerror(回傳錯誤原因的描述字串函式)
- 定義:
char *strerror( int errnum );
- 區別:
strerror:從內部陣列中搜索錯誤號
errnum,并回傳一個指向錯誤訊息字串的指標perror:列印+strerror(直接打印出來)
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *stream;
if ((stream=fopen("crt_fopen.c", "r")) == NULL) //以讀取的方式打開檔案
{
perror("perror says open failed");
printf("strerror says open failed: %s\n", strerror(errno));
}
else
{
printf("open succeeded on input file\n");
fclose(stream);
}
return 0;
}
注:通過fopen()函式打開指定的檔案,如果打開該檔案失敗,則fopen()函式的回傳值是NULL,此時可以通過perror()函式或者strerror()函式顯示錯誤資訊
記憶體函式介紹
- 記憶體函式與字串函式的區別:
字串函式:只能對字串進行操作
記憶體函式:能夠操作任何資料型別(對物件儲存的內容進行操作)
memcpy(記憶體拷貝函式)
- 定義:
void *memcpy( void *dest, const void *src, size_t count //以位元組為單位);
- 注意:
- 函式memcpy從source的位置開始向后復制num個位元組的資料到destination的記憶體位置
- 這個函式在遇到 '\0' 的時候并不會停下來
- 如果source和destination有任何的重疊,復制的結果都是未定義的
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6 };
int arr2[5] = { 0 };
memcpy(arr2, arr1, 16);
int i = 0;
int size = sizeof(arr2) / sizeof(arr2[0]);
for (i = 0; i < size; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
//輸出結果:1 2 3 4 0
- 模擬實作:
void* my_memcpy(void* des, const void* src, size_t n)
{
assert(des && src);
void* ret = des;//記錄初始位置
while (n--)//回圈拷貝
{
*(char*)des = *(char*)src;
(char*)des = (char*)des + 1;
(char*)src = (char*)src + 1;
}
return ret;
}
memmove(記憶體移動函式)
- 定義:
void *memmove( void *dest, const void *src, size_t count );
注:
memmove函式相比于memcpy函式能夠很好的實作重復地址拷貝(如果source和destination有任何的重疊也能達到想要的結果)
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6 };
int arr2[3] = { 0 };
memmove(arr1 + 2, arr1, 16);
int i = 0;
int size = sizeof(arr1) / sizeof(arr1[0]);
for (i = 0; i < size; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
//輸出結果:1 2 1 2 3 4
- 模擬實作:
void* my_memmove(void* des, const void* src, size_t n)
{
assert(des && src);
void* ret = des;
if (des < src)
{
//如果目標在源地前面則從前到后拷貝
while (n--)
{
*(char*)des = *(char*)src;
(char*)des = (char*)des + 1;
(char*)src = (char*)src + 1;
}
}
else
{
//目標在源地后面則從后到前拷貝
while (n--)
{
*((char*)des + n) = *((char*)src + n);
}
}
return des;
}
memcmp(記憶體比較函式)
- 定義:
int memcmp( const void *buf1, const void *buf2, size_t count );
注:比較從ptr1和ptr2指標開始的num個位元組
- 函式演示:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,3,4,6 };
printf("%d ", memcmp(arr1, arr2, 16));
return 0;
}
//輸出結果:0
- 模擬實作:
int my_memcmp(const void* src1, const void* src2, size_t n)
{
assert(src1 && src2);
while (--n)
{
//遇到不同直接退出
if (*(char*)src1 != *(char*)src2)
{
break;
}
(char*)src1 = (char*)src1 + 1;
(char*)src2 = (char*)src2 + 1;
}
return *(char*)src1 - *(char*)src2;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301998.html
標籤:其他
上一篇:都中秋了還在苦苦加班排序?這篇文章讓你一次性搞定排序問題,手把手教你實作一個通用的排序函式
下一篇:Java學習 -- 多型性
