
------每日一图"地球情绪"
文章目录
目录
[strcpy( )](#strcpy( ))
[strncpy( )](#strncpy( ))
[strcat( )](#strcat( ))
[strncat( )](#strncat( ))
[strcmp( )](#strcmp( ))
[strncmp( )](#strncmp( ))
[strstr( )](#strstr( ))
[strtok( )](#strtok( ))
前言
即使我们已经进入了C++时代,但 string.h 中的一些函数仍然非常重要,尤其是当我们需要处理操作系统底层代码或与C语言交互时,尤为明显。
在string.h中,除了与字符串操作相关的函数(如strlen, strcpy, strcat, strcmp, strstr, strchr等)之外,还有一些内存操作函数,这些函数在C++中仍然非常有用,因为它们可以操作任意类型的内存块,而不仅仅是字符串。
本文总结string.h头文件中,经常用到的函数,主要包括:字符串操作函数与内存操作函数。
一、字符串操作函数
strlen
函数原型:
cpp
#include <string.h>
size_t strlen(const char *str);
函数作用: 计算传入字符串的长度,即字符的个数,不包括字符串末尾的空字符\0(包括\n)。
**函数参数:**str,需要计算的字符串。
**函数返回值:**该字符串的长度。
使用注意:如果传入的字符串指针不是以 '\0' 结尾,函数的行为是未定义的,可能会一直访问内存直到遇到 '\0',导致程序错误。
使用示例:
cpp
void test_strlen()
{
const char* str = "hello strlen";
cout << strlen(str) << endl;
char str1[] = "hello world\n";
cout << strlen(str1) << endl;
}
模拟实现:
cpp
size_t my_strlen(const char* str = NULL)
{
if (!str)return 0;
size_t ret = 0;
while (*str != '\0')
{
++ret;
++str;
}
return ret;
}
strcpy与strncpy
strcpy( )
函数原型:
cpp
#include <string.h>
char *strcpy(char *dest, const char *src);
函数作用: 将源字符串src (包括结束符\0)复制到目标字符串dest中。
函数参数:dest 目标字符串;src源字符串。
函数返回值: 指向目标字符串dest的指针。
使用注意:使用strcpy( ) 时,需要注意目标数组dest 的空间大小是否足够大,否则**如果源字符串长度过长,可能会导致缓冲区溢出。源字符串与目标内存区域不能重叠,**如果重叠,行为未定义。
使用示例:
cpp
void test_strcpy()
{
const char* str1 = "hello strcpy\n";
char buffer[24];
cout << strcpy(buffer, str1);
cout << buffer;
}
模拟实现:
cpp
char* my_strcpy(char* dest, const char* src)
{
if (src == NULL || dest == NULL)return NULL;
char* temp = dest;
// 赋值表达式的结果就是所赋值的字符,即赋值后左边的值,也就是原来的*src的值
while ((*dest++ = *src++)) { ; }
return temp;
}
strncpy( )
函数原型:
cpp
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
函数作用: 从源字符串src 复制最多n 个字符到目标字符dest 中。strcpy的升级版,指定拷贝字节数量,可预防strcpy可能导致的缓冲区溢出的缺陷,。
函数参数: dest ,目标字符串;src ,源字符串(被复制字符串);n,要复制的最大字符数。
函数返回值: 指向目标字符串dest的指针。
使用注意:与 strcpy相比,它增加了安全性,但不保证目标字符串以 '\0' 结尾 (例如当 n 小于或等于 src的长度时)。使用者需要自己确保目标字符串的正确终止。
使用示例:
cpp
void test_strncpy()
{
const char* str1 = "hello strcpy\n";
char buffer[24];
cout << strcpy(buffer, str1);
cout << strncpy(buffer,str1,strlen(str1)+1);//将\0也拷贝了
}
模拟实现:
cpp
char* my_strncpy(char* dest, const char* src, size_t n)
{
if (!dest || !src || n == 0)return dest;
char* ret = dest;
for (size_t i = 0; i < n; ++i)
{
*dest++ = *src;
if (*src != '\0') src++;
}
return ret;
}
strcat与strncat
strcat( )
函数原型:
cpp
#include <string.h>
char *strcat(char *dest, const char *src);
函数作用: 将源字符串src 追加到目标字符串dest的末尾。
函数参数: dest ,目标字符串;src,要追加的源字符串。
函数返回值: 指向目标字符串dest的指针。
使用注意:C标准库中dest与src同源时会发生未定义行为。 目标数组必须足够大,以容纳拼接后的两个字符串(包括 '\0')
使用示例:
cpp
void test_strcat()
{
char buffer[64] = "hello world";
const char* str = "hello strcat\n";
if (!strcat(buffer, buffer))cout << "NO" << endl;
cout << buffer;
}
模拟实现:
cpp
char* my_strcat(char* dest, const char* src)
{
if (src == NULL || dest == NULL)return NULL;
//C标准库中dest与src同源时会发生未定义行为
if (dest == src)return NULL;
char* temp = dest;
while (*dest != '\0')dest++;
while ((*dest++ = *src++)) { ; }
return temp;
}
strncat( )
函数原型:
cpp
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
函数作用: 将源字符串src 的最多前n个字符追加到目标字符串dest的末尾。实际复制的字节数 = min(sizeof (src), n),也就是说当n大于src的字节数时,只会拷贝strlen( src)个字节数。
函数参数: dest ,目标字符串;src ,要追加的源字符串;n,要追加的最大字符数。
函数返回值: 指向目标字符串dest的指针。
使用注意:比 strcat 更安全,因为它限制了追加的字符数。它会保证结果字符串以 '\0' 结尾。
使用示例:
cpp
void test_strncat()
{
char buffer[64] = "hello world";
const char* str = ",hello strcat\n";
strncat(buffer, str, 100);
cout << buffer;
}
模拟实现:
cpp
char* my_strncat(char* dest, const char* src, size_t n)
{
if (!dest || !src || n == 0)return dest;
char* ret = dest;
while (*dest)++dest;
for (size_t i = 0; i < n && src; ++i)
{
*dest++ = *src++;
}
*dest='\0';
return ret;
}
strcmp与strncmp
strcmp( )
函数原型:
cpp
#include <string.h>
int strcmp(const char *str1, const char *str2);
函数作用: 比较两个字符串**str1** 和**str2**。
函数参数: str1 ,第一个要比较的字符串;str2,第二个要比较的字符串。
函数返回值:
小于0,如果**
str1** 小于**str2**等于0,如果**
str1** 等于**str2**大于0,如果**
str1** 大于**str2**
使用注意:比较是基于字符的 ASCII 值进行的。
使用示范:
cpp
void test_strcmp()
{
const char* str1 = "abcdef";
const char* str2 = "agb";
cout << strcmp(str1, str2) << endl;
}
模拟实现:
cpp
//更符合标准库风格的实现方式
int my_strcmp(const char* str1, const char* str2)
{
if (str1 == NULL && str2 == NULL)return 0;
if (str1 == NULL)return -1;
if (str2 == NULL)return 1;
while (*str1 == *str2)
{
if (*str1 == '\0')return 0;
++str1;
++str2;
}
return (*str1 - *str2);
}
strncmp( )
函数原型:
cpp
#include <string.h>
int strncmp(const char *str1, const char *str2, size_t n);
函数作用: 比较两个字符串**str1** 和**str2** 的最多前n个字符。
函数参数: str1 ,第一个要比较的字符串;str2 ,第二个要比较的字符串;n,要比较的最大字符数。
函数返回值:
小于0,如果
str1小于str2等于0,如果
str1等于str2大于0,如果
str1大于str2
使用注意:如果在比较完 n 个字符前遇到任一个字符串的 '\0',比较也会停止。
使用示例:
cpp
void test_strncmp()
{
const char* str1 = "abcdef";
const char* str2 = "abc";
cout << strncmp(str1, str2, strlen(str1)) << endl;
}
模拟实现:
cpp
int my_strncmp(const char* dest, const char* src, size_t n)
{
if (!dest || !src)
{
// 更简洁的空指针处理
if (!dest && !src) return 0;
return (!dest) ? -1 : 1;
}
size_t i = 0;
//这样写,当*dest!=*src时,src还会++,导致越界
//while (i < n && *dest++ == *src++)
while (i < n && *dest == *src)
{
++i;
dest++;
src++;
}
if (i == n)return 0;
return (*(unsigned char*)dest - *(unsigned char*)src);
}
strstr( )
函数原型:
cpp
#include <string.h>
char *strstr(const char *str1, const char *str2);
函数作用: 在字符串str1中查找子字符串**str2**的第一次出现。
函数参数: str1,要搜索的主字符串;str2,要查找的子字符串。
函数返回值: 指向子字符串第一次出现的指针,如果未找到则返回NULL。
使用注意:搜索包括终止空字符在内的所有字符,但通常用于查找非空子串。
使用示例:
cpp
void test_strstr()
{
char str[] = "This is a simple string";
char* pch;
pch = strstr(str, "simple");
strncpy(pch, "sample",6);
printf("%s\n", str);
printf("%s\n", pch);
}
模拟实现:
cpp
char* my_strstr(const char* str1, const char* str2)
{
if (!str1 || !str2)return NULL;
char* temp = (char*)str1;
while (*temp)
{
//在里面定义防止误触while循环
char* s1 = temp;
char* s2 = (char*)str2;
char* ret = s1;
while (*s1 && *s2 && !(*s1 - *s2))
{
++s1;
++s2;
}
if (!*s2)return ret;
++temp;
}
return NULL;
}
strtok( )
函数原型:
cpp
#include<string.h>
char *strtok(char *str, const char *sep)
函数作用 :将字符串 str 分割成一系列以\0结束的子串。在第一次调用时,str 指定要分割的字符串,后续调用应传入 NULL,函数会继续从上次的位置进行分割。
函数参数 :str :要分割的字符串,在第一次调用时指定,后续调用应为 NULL 。sep:包含分隔符的字符串。
函数返回值 :返回指向下一个找到的令牌的指针。如果没有更多令牌,则返回 NULL。
使用注意:此函数会修改原始字符串,它在找到的分隔符处写入 '\0'。
使用示例:
cpp
void test_strtok()
{
char temp[] = "hello world,hello strtok.hello XXX\n";
char s[] = "[=";
char* ret = strtok(temp, s);
while (ret)
{
cout << ret << endl;
ret = strtok(NULL, s);
}
}
二、内存操作函数
这些内存操作函数在C++中仍然非常有用,因为它们可以操作任意类型的内存块,而不仅仅是字符串。
memset
函数原型:
cpp
#include <string.h>
void *memset(void *ptr, int value, size_t num);
函数作用: 将内存区域**ptr** 的前n个字节设置为指定的值value。
函数参数: ptr,要设置的内存区域;value,要设置的值;n,要设置的字节数。
函数返回值: 指向内存区域ptr的指针。
注意事项:常用于内存初始化(如清零)或设置特定模式。按字节进行设置。
使用示例:
cpp
void test_memset()
{
char buffer[64];
//方便strcpy或其他操作之后直接打印字符串
memset(buffer, '\0', sizeof(buffer));
const char* str = "hello memset\n";
strncpy(buffer, str, strlen(str));
cout << buffer;
}
memcpy与memmove
memcpy
函数原型:
cpp
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
函数作用: 从源内存地址src复制n个字节到目标内存地址dest。
函数参数: dest,目标内存地址;src,源内存地址;n,要复制的字节数。
函数返回值: 指向目标内存地址dest的指针。
注意事项:不处理内存重叠。如果源和目标内存区域有重叠,应使用 memmove 函数。它不关心数据的类型(void*),只进行纯粹的字节拷贝。
使用示例:
cpp
struct Point
{
int _x;
int _y;
Point(int x = 0, int y = 0)
:_x(x),_y(y)
{ }
};
void test_memcpy()
{
const char* str = "hello memcpy\n";
char buffer[24];
cout << (char*)memcpy(buffer, str, strlen(str) + 1);
Point p1(12, 12);
Point p2;
memcpy(&p2, &p1, sizeof(p2));
cout << p2._x << ' ' << p2._y << endl;
}
memmove
函数原型:
cpp
#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
函数作用: 从源内存地址src复制n个字节到目标内存地址dest,正确处理内存重叠区域。
函数参数: dest,目标内存地址;src,源内存地址;n,要复制的字节数。
函数返回值: 指向目标内存地址dest的指针。
注意事项:与 memcpy 功能类似,但能够正确处理内存区域重叠的情况,可能比 memcpy 效率稍低。
使用示例:
cpp
//它能除了内存重叠的情况:自己复制自己
void test_memmove()
{
char str[24] = "aaaabbbb";
cout << str << endl;
memmove(str + 4, str, 4);
cout << str << endl;
}
memcmp
函数原型:
cpp
#include <string.h>
int memcmp(const void *ptr1, const void *ptr2, size_t n);
函数作用: 比较两个内存区域ptr1和ptr2的前n个字节。
函数参数: ptr1,第一个内存区域;ptr2,第二个内存区域;n,要比较的字节数。
函数返回值:
小于0,如果
ptr1小于ptr2等于0,如果
ptr1等于ptr2大于0,如果
ptr1大于ptr2
注意事项:比较是按字节进行的,不关心数据的类型,也不在乎是否有 '\0' 字符。
使用示例:
cpp
struct Point
{
int _x;
int _y;
Point(int x = 0, int y = 0)
:_x(x),_y(y)
{ }
};
void test_memcmp()
{
const char* str1 = "hello a";
const char* str2 = "hello b";
cout << memcmp(str1, str2, strlen(str1)) << endl;
Point p1(1, 1);
Point p2(1, 1);
cout << memcmp(&p1, &p2, sizeof(p1));
}
memchr
函数原型:
cpp
#include <string.h>
void *memchr(const void *ptr, int value, size_t n);
函数作用: 在内存区域ptr的前n个字节中搜索第一次出现值value的位置。
函数参数: ptr,要搜索的内存区域;value,要搜索的值;n,要搜索的字节数。
函数返回值: 指向第一次出现值value的指针,如果未找到则返回NULL。
使用示例:
cpp
void test_memchr()
{
char str[] = "hello memchr";
cout << sizeof(str);
//注意是字符型,因为memchr的形参二是int型
char* pos = (char*)memchr(str, 'o', strlen(str));
cout << pos << endl;
}
总结
本文总结了C语言string.h头文件中的常用字符串和内存操作函数。
字符串函数包括:strlen计算长度,strcpy/strncpy复制字符串,strcat/strncat连接字符串,strcmp/strncmp比较字符串,strstr查找子串,strtok分割字符串。
内存操作函数包括:memset内存设置,memcpy/memmove内存复制,memcmp内存比较,memchr内存查找。
这些函数在C++中仍然重要,特别是处理底层代码或与C交互时。本文详细说明了每个函数的原型、作用、参数、返回值和使用注意事项,并提供了模拟实现代码示例。
整理不易,读完点赞,手留余香~