前言:
爲了方便查看博客,特意申請了一個公衆號,附上二維碼,有興趣的朋友可以關注,和我一起討論學習,一起享受技術,一起成長。
1. memcpy
內存複製函數。
1.1 頭文件
#include <string.h>
1.2 描述
void *memcpy(void *str1, const void *str2, size_t n) ;
從存儲區 str2 複製 n 個字符到存儲區 str1。
1.3 參數
str1: 指向用於存儲複製內容的目標數組,類型強制轉換爲 void* 指針;
str2: 指向要複製的數據源,類型強制轉換爲 void* 指針;
n: 要被複制的字節數。
1.4 返回值
該函數返回一個指向目標存儲區 str1 的指針。
1.5 注意
(1)複製的內容不同。strcpy 只能複製字符串,而 memcpy 可以複製任意內容,例如字符數組、整型、結構體、類等;
(2)複製的方法不同。strcpy 不需要指定長度,它遇到被複制字符的串結束符 “\0” 才結束,所以容易溢出。memcpy 則是根據其第 3 個參數決定複製的長度;
(3)用途不同。通常在複製字符串時用 strcpy,而需要複製其他類型數據時則一般用 memcpy。
(4)str1指針要分配足夠的空間,也即大於等於 n 字節的空間。如果沒有分配空間,會出現斷錯誤,造成空間溢出;
(5)str1 和 str2 所指的內存空間不能重疊(如果發生了重疊,使用 memmove() 會更加安全);
(6)memcpy 是按照字節進行拷貝。多字節數據的拷貝如下:
測試:
void memcpy_test(void)
{
char src_buf[5] = {1, 2, 3, 4, 5};
char dst_buf[10] = {0};
int test_buf1[5] = {99, 8522, 987, 3467, 11027};
int test_buf2[5] = {0};
memcpy(dst_buf, src_buf, sizeof(src_buf));
for (int i = 0; i < sizeof(src_buf); i++)
{
printf("dst_buf: %d \r\n", dst_buf[i]);
}
memcpy(test_buf2, test_buf1, sizeof(test_buf1)); //sizeof(test_buf1) = 20字節
for (int i = 0; i < 5; i++)
{
printf("test_buf2: %d \r\n", test_buf2[i]);
}
}
結果輸出:
1.6 原型實現
void *Memcpy(void *dest, const void *src, unsigned int count)
{
char *pdest = (char *)dest;
char *psrc = (char *)src;
if (pdest == NULL || psrc == NULL)
{
return NULL;
}
if (pdest == psrc)
{
return;
}
if (pdest > psrc)
{
while (count--)
{
*(pdest + count) = *(psrc + count);
}
}
else
{
while (count--)
{
*pdest++ = *psrc++;
}
}
return pdest;
}
2. memset
將某一塊內存中的內容全部設置爲指定的值,它是對較大的結構體或數組進行清零操作的一種快捷方法。
2.1 頭文件
#include <string.h>
2.2 描述
void *memset(void *str, int c, size_t n) ;
複製字符 c(一個無符號字符)到參數 str 所指向的字符串的前 n 個字符。
2.3 參數
str: 指向要填充的內存塊
c: 要被設置的值。該值以 int 形式傳遞,但是函數在填充內存塊時是使用該值的無符號字符形式;
n: 要被設置爲該值的字節數。
2.4 返回值
該函數返回一個指向存儲區 str 的指針。
2.5 注意
(1)memset 函數按字節對內存塊進行初始化,所以不能用它將 int 數組初始化爲 0 和 -1 之外的其他值(除非該值高字節和低字節相同);
(2)memset(void *str, int c, size_t n) 中 c 實際範圍應該在 0——255,因爲該函數只能取 c 的後八位賦值給你所輸入的範圍的每個字節。無論 c 多大隻有後八位二進制有效,而後八位二進制的範圍在(0~255)。而對字符數組操作時則取後八位賦值給字符數組,其八位值作爲 ASCII 碼。
測試:
void memset_test(void)
{
char test_buf1[5];
int test_buf2[5];
memset(test_buf1, 1, sizeof(test_buf1));
memset(test_buf2, 1, sizeof(test_buf2));
for (int i = 0; i < sizeof(test_buf1); i++)
{
printf("tset_buf1 is %x \r\n", test_buf1[i]);
}
for (int i = 0; i < 5; i++)
{
printf("tset_buf2 is %x \r\n", test_buf2[i]);
}
}
結果輸出:
2.6 原型實現
void *MyMemset(void *dest, int n, unsigned int len)
{
if (dest == NULL)
return NULL;
char *pdest = (char *)dest;
while (len--)
{
*pdest++ = n;
}
return dest;
}
3. memcmp
把存儲區 str1 和存儲區 str2 的前 n 個字節進行比較,按字節比較。
3.1 頭文件
#include <string.h>
3.2 描述
int memcmp(const void *str1, const void *str2, size_t n) ;
存儲區 str1 和存儲區 str2 的前 n 個字節進行比較。
3.3 參數
str1: 指向內存塊的指針;
str2: 指向內存塊的指針;
n: 要被比較的字節數。
3.4 返回值
如果返回值 < 0,則表示 str1 小於 str2;
如果返回值 > 0,則表示 str2 小於 str1;
如果返回值 = 0,則表示 str1 等於 str2。
3.5 注意
(1)該函數是按字節比較的,比較 str1 和 str2 的第一個字節的 ascII 碼值。
測試:
void memcmp_test(void)
{
char str1[] = "abcd";
char str2[] = "abcd";
int ret = 0;
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
memcpy(str1, "abce", 4);
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
memcpy(str1, "abcc", 4);
ret = My_Memcmp(str1, str2, 4);
printf("ret val is %d \r\n", ret);
}
結果輸出:
3.6 原型實現
int My_Memcmp(void *str1, void *str2, int count)
{
char *ptr1 = (char *)str1;
char *ptr2 = (char *)str2;
if (!count)
{
return 0;
}
while (count--)
{
if (*ptr1 == *ptr2)
{
ptr1 = ptr1 + 1;
ptr2 = ptr2 + 1;
}
else
{
return *ptr1 - *ptr2;
}
}
return *ptr1 - *ptr2;
}
4. strlen
strlen 所作的是一個計數器的工作,它從內存的某個位置(可以是字符串開頭,中間某個位置,甚至是某個不確定的內存區域)開始掃描,直到碰到第一個字符串結束符 ‘\0’ 爲止,然後返回字符計數器值(長度不包含 '\0 ')。
4.1 頭文件
#include <string.h>
4.2 描述
size_t strlen(const char *str) ;
計算字符串 str 的長度,直到空結束字符,但不包括空結束字符。
4.3 參數
str: 要計算長度的字符串。
4.4 返回值
該函數返回字符串的長度。
4.5 注意
(1)注意 strlen 和 sizeof 的區別。
測試:
void strlen_test(void)
{
char *ptr = "asdfghjl";
printf("strlen length is %d \r\n", strlen(ptr));
}
結果輸出:
輸出字符串長度不包括 ‘\0’。
4.6 原型實現
unsigned int My_Strlen(const char *str)
{
int str_len = 0;
while (*str++ != '\0')
{
str_len++;
}
return str_len;
}
5. strcpy
strcpy 把含有 ‘\0’ 結束符的字符串複製到另一個地址空間,返回值的類型爲 char*。
5.1 頭文件
#include <string.h>
5.2 描述
char *strcpy(char *dest, const char *src);
把 src 所指向的字符串複製到 dest。
5.3 參數
dest: 指向用於存儲複製內容的目標數組。;
src: 要複製的字符串。
5.4 返回值
該函數返回一個指向最終的目標字符串 dest 的指針。
5.5 注意
(1)若目標數組 dest 不夠大,而源字符串的長度又太長,可能會造成緩衝溢出的情況;
(2)src 和 dest 所指內存區域不可以重疊。
測試:
void strcpy_test(void)
{
char test_buf1[6] = "ABCDE";
char test_buf2[100] = {0};
strcpy(test_buf2, test_buf1);
printf("strcpy test %s \r\n", test_buf2);
}
5.6 原型實現
char *My_Strcpy(char *dest, const char *src)
{
char *tmp = (char *)dest;
if ((dest == NULL) || (src == NULL))
{
return 0;
}
while (*src != '\0')
{
*tmp++ = *src++;
}
*tmp = '\0';
return tmp;
}
6. strcmp
比較兩個字符串並根據比較結果返回整數。基本形式爲 strcmp(str1, str2),若 str1 = str2,則返回零;若 str1 < str2,則返回負數;若 str1 > str2,則返回正數。
6.1 頭文件
#include <string.h>
6.2 描述
int strcmp(const char *str1, const char *str2);
把 str1 所指向的字符串和 str2 所指向的字符串進行比較。
6.3 參數
str1: 要進行比較的第一個字符串;
str2: 要進行比較的第二個字符串。
6.4 返回值
如果返回值小於 0,則表示 str1 小於 str2;
如果返回值大於 0,則表示 str1 大於 str2;
如果返回值等於 0,則表示 str1 等於 str2。
6.5 注意
(1)兩個字符串自左向右逐個字符相比(按 ASCII 值大小相比較),直到出現不同的字符或遇 ‘\0’ 爲止;
(2)只能比較字符串,即可用於比較兩個字符串常量或比較數組,不能比較數字等其他形式的參數。
測試:
void strcmp_test(void)
{
int ret = 0;
// char test_buf1[6] = "ABCBE";
// char test_buf2[3] = "AB";
char test_buf1[6] = {1, 2, 3, 4, 5, 6};
char test_buf2[6] = {1, 2, 3, 7, 5, 6};
ret = My_Strcmp(test_buf1, test_buf2);
printf("ret val is %d \r\n", ret);
}
6.6 原型實現
int My_Strcmp(const char *str1, const char *str2)
{
if (str1 == NULL || str2 == NULL)
{
return NULL;
}
while (*str1 == *str2)
{
str1++;
str2++;
if (*str1 == '\0' || *str2 == '\0')
{
break;
}
}
return *str1 - *str2;
}
7. sprintf
字符串格式化命令。函數聲明爲 int sprintf(char *string, char *format [,argument,…]);,主要功能是把格式化的數據寫入某個字符串中,即發送格式化輸出到 string 所指向的字符串。
7.1 頭文件
#include <stdio.h>
7.2 描述
int sprintf(char *str, const char *format, ...);
發送格式化輸出到 str 所指向的字符串。
7.3 參數
str: 指向一個字符數組的指針,該數組存儲了 C 字符串;
format : 字符串,包含了要被寫入到字符串 str 的文本。它可以包含嵌入的 format 標籤,format 標籤可被隨後的附加參數中指定的值替換,並按需求進行格式化。format 標籤屬性是 %[flags] [width] [.precision] [length]specifier,具體介紹如下:
flags(標識):
width(寬度):
.precision(精度):
length(長度):
specifier(說明符):
附加參數 – 根據不同的 format 字符串,函數可能需要一系列的附加參數,每個參數包含了一個要被插入的值,替換了 format 參數中指定的每個 % 標籤。參數的個數應與 % 標籤的個數相同。
7.4 返回值
如果成功,則返回寫入的字符總數,不包括字符串追加在字符串末尾的空字符。如果失敗,則返回一個負數。
7.5 注意
(1)格式化數字字符串 sprintf 最常見的應用之一是把整數打印到字符串中;
(2)sprintf 的返回值是字符數組中字符的個數,即字符串的長度,不用在調用 strlen(str) 求字符串的長度;
測試:
void sprintf_test(void)
{
char buf[100] = {0};
double data = 3.141593;
uint16_t len = 0;
uint8_t test_buff[5] = {55, 22, 66, 77, 99};
len = sprintf((char *)buf, "%f", data);
printf("----------------------------------------------------------------- \r\n");
printf("buf is %s \r\n", buf);
printf("sprintf return len is %d \r\n", len);
len = 0;
for (uint16_t i = 0; i < sizeof(test_buff); i++)
{
len += sprintf((char *)buf + i * 2, "%d", test_buff[i]); //數組數據轉換爲字符串
}
printf("test_buff val is %s \r\n", buf);
printf("sprintf return len is %d \r\n", len);
printf("----------------------------------------------------------------- \r\n");
}
結果輸出: