IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> [C笔记]常用字符串函数与内存操作函数 -> 正文阅读

[C++知识库][C笔记]常用字符串函数与内存操作函数

-C语言中对字符和字符串的处理很常见,但C语言本身没有字符串类型,字符串通常放在常量字符串中或者字符数组中。
-字符串常量适用于那些对它不做修改的字符串函数。
-字符串函数头文件<string.h>

求字符串长度

strlen

返回字符串中 ‘\0’ 之前出现的字符个数(不包含 ‘\0’ )

声明:

size_t strlen ( const char * str );

size_t 即为 无符号整型 unsigned int
sizeof的返回值也是size_t

typedef unsigned int    size_t

参数指向的字符串必须以 ‘\0’ 结束,否则结果将会是无意义的,因为函数只会在遇到’\0‘时停下并返回。

模拟实现strlen

方式:
1.计数器

int my_strlen(const char* str) {
	int count = 0;
	while (*str) {//'\0'的值为0
		count++;
		str++;
	}
	return count;
}

2.指针减指针

int my_strlen(const char* str) {
	char* p = str;
	while (*p) {//'\0'的值为0
		p++;
	}
	return p-str;
}

3.递归(不创建临时变量求字符串长度)

int my_strlen(const char* str) {
	if (*str == '\0') {
		return 0;
	}
	else{
		return 1 + my_strlen(str + 1);
		//count++和指针++都在这一步实现了
	}
}

靠\0自动限制长度的字符串操作函数

strcpy

将源(source)字符串中’\0’之前的字符全部拷贝到目标(destination)字符串,并返回指向目标字符串的指针。
假设空间足够,就会替换目标字符串对应位置的内容('\0’也会一起拷贝并替换进去)

-会将源字符串中的 ‘\0’ 拷贝到目标空间。
-源字符串必须以 ‘\0’ 结束,原因同前文。
-目标空间必须足够大,以容纳源字符串的内容。
-目标空间必须可修改,。

声明:

char* strcpy ( char * destination, const char * source );

source:指向源字符串的指针
destination:指向目标字符串的指针
返回的char是指向目标字符串的指针,也就是目标空间的起始地址
返回char
是为了实现链式访问

模拟实现strcpy

char* my_strcpy(char* dest, const char* src) {//原空间不会被修改,故可为src加const
	assert(dest && src);//要确保dest与src不为空
	char* ret = dest;//由于后续dest会被改变,使用一个变量存放dest最初的值以供最后返回
	while (*dest++ = *src++) {//在这里一次性实现拷贝与遍历与检测\0
		;
	}
	return ret;
}

strcat

字符串连接(或称追加):
将源字符串的内容添加到目标字符串后面。
目标字符串中的‘\0’被源字符串的第一个字符覆盖,并且在目标字符串中由两者连接形成的新字符串的末尾会包含’\0’。
也就是说,strcat不会覆盖目标字符串中’\0’以外的内容。

-源字符串必须以 ‘\0’ 结束,原因同前文。
-目标空间必须足够大,以容纳源字符串的内容。
-目标空间必须可修改。

声明:

char* strcat(char *destination, const char *source)

source:指向源字符串的指针
destination:指向目标字符串的指针
返回的char*是指向目标字符串的指针,也就是目标空间的起始地址

模拟实现strcat

思路:
1.找到目标字符串的末尾,即’\0’
2.从源字符串的’\0’位置起,将源字符串中直到’\0’为止的所有字符(包括’\0’)追加到目标字符串的末尾

char* my_strcat(char* dest, const char* src) {
	assert(dest && src);
	char* ret = dest;//同my_strcpy,使用一个变量存放dest最初的值以供最后返回
	while (*dest) {//'\0' 的ascii码值为0,可直接判断
		dest++;
	}
	while (*dest++ = *src++) {//同my_strcpy中的实现方式,一次性实现拷贝与遍历与检测'\0'
		;
	}
	return ret;
}

strcmp

从字符串头部开始逐字符(也就是逐字节)比较两个字符串,直到发现不同的字符或者遇到’\0’,返回代表两字符串第一个不相同的字符的ASCII码值的大小关系的整型:

用strcmp比较str1和str2两个字符串:
如果返回值小于 0,则表示 str1小于 str2。
如果返回值大于 0,则表示 str1 大于 str2。
如果返回值等于 0,则表示 str1 等于 str2。

所以strcmp返回的是字符串的内容的比较结果,而不是长度的
另外需要注意,规定的不相同时的返回值只是">0<0",而不是具体的某个数

声明:

int strcmp ( const char * str1, const char * str2 );

模拟实现strcmp

int my_strcat(const char* s1, const char* s2) {
	assert(s1 && s2);
	while (*s1 == *s2) {
		if (*s1 == '\0') {
			return 0;
		}
		s1++; 
		s2++;
	}
	return *s1 - *s2;
}

靠指定字符数限制长度的字符串操作函数

strncpy

将源字符串的前 num 个字符复制到目标字符串。 如果在复制 num 个字符之前找到源 C 字符串的结尾(由’\0’表示),也就是源字符串长度小于num时,则开始用0(也就是’\0’)填充目标字符串,直到总共写入了 num 个字符。

-源字符串必须以 ‘\0’ 结束,原因同前文。
-目标空间必须足够大,以容纳源字符串的内容。
-目标空间必须可修改。

声明:

char * strncpy ( char * destination, const char * source, size_t num );

num:指定的字符数,需注意类型为size_t,即无符号整型

strncat

将源字符串的内容添加到目标字符串后面。
目标字符串中的‘\0’被源字符串的第一个字符覆盖,并且在目标字符串中由两者连接形成的新字符串的末尾会包含’\0’。
如果源字符串长度小于给定的num,则只复制直到’\0’为止的内容。

-源字符串必须以 ‘\0’ 结束,原因同前文。
-目标空间必须足够大,以容纳源字符串的内容。
-目标空间必须可修改。

声明:

char * strncat ( char * destination, const char * source, size_t num );

num:指定的字符数

strncmp

比较到出现不同字符或者一个字符串结束或者num个字符全部比较完:
“将 C 字符串 str1 的最多 num 个字符与 C 字符串 str2 的字符进行比较。
此函数从每个字符串的第一个字符开始比较。 如果它们彼此相等,则继续比较后续字符对,直到字符不同,或到达’\0’,或直到两个字符串中的 num 个字符匹配,以先发生者为准。”

函数返回的值与比较结果的关系同strcmp:

用strcmp比较str1和str2两个字符串:
如果返回值小于 0,则表示 str1小于 str2。
如果返回值大于 0,则表示 str1 大于 str2。
如果返回值等于 0,则表示 str1 等于 str2。

声明:

int strncmp ( const char * str1, const char * str2, size_t num );

字符串查找

strstr

用于在一个字符串中查找另一个字符串第一次出现的位置:
“返回指向str1中第一次出现str2的位置的指针,如果未在str1中找到str2,则返回空指针。
参与比较的内容不包括’\0’,但会在遇到’\0’时停止”
若str2为空字符串,即在str1中查找’\0’,库函数strstr返回的是str1,而不是str1的结束地址

声明:

const char * strstr ( const char * str1, const char * str2 );
      char * strstr ( const char * str2, const char * str1);
      char * strstr (       char * str1, const char * str2 );

模拟实现strstr

有一次匹配与多次匹配两种情况:

*	一次匹配			多次匹配
s1	abcdef			abbbcdef
s2	bcd				bbc

多次匹配需要使用变量记住每次匹配开始时的位置

每次匹配的停止条件:
1.匹配到不同字符时
2.在其中一个字符串中遇到了’\0’时

char* my_strstr(const char* str1, const char* str2) {
	assert(str1 && str2);
	char* s1;
	char* s2;
	char* cp = str1;
	if (*str2 == '\0') {
		return str1;//若str2为空字符串,即在str1中查找'\0',返回str1
	}
	while (*cp){//循环,直到cp指向'\0'
		s1 = cp;//cp记录每次匹配开始时s1的位置
		s2 = str2;//s2复位
		while (*s2!='\0'&& *s1 == *s2) {
			s1++;
			s2++;
		}
		if (*s2 == '\0') {
			return cp;//匹配成功,返回str1中相同字符串的起点
		}
		cp++; //每当第一个字符匹配失败(*s1 != *s2)则cp++
	}
	return NULL;//全部匹配失败,返回空指针
}
cp记录每次匹配开始时s1的位置,每当第一个字符匹配失败(*s1!=*s2)则cp++,
,而每次匹配当第2个及之后的字符匹配失败时s2(已+i)回到str2,s1(已+i)回到cp,cp++ ,
,当*s2=='\0'则匹配成功,
,当*s1=='\0'而*s2!='\0'则全部匹配失败,
,循环,直到cp指向'\0' 

库函数也是这样实现的,但对于字符串查找,KMP算法更高效

strtok

用于分割字符串

声明:

char * strtok ( char * str, const char * sep );
参数str是待分割的字符串,包含0个或者多个由sep字符串中的一个或者多个分隔符分割的片段。
参数sep是分隔符集合字符串,定义了用作分隔符的字符集合(是集合,所以无所谓先后顺序)
	如"114514@1919810.com"这个邮箱地址字符串的分隔符是"@."

-若参数str不为NULL,函数将找到str中第一个片段,strtok函数将保存它在字符串中的位置(通过静态变量或全局变量实现)。
-若参数str为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个片段。
-对于找到的片段,strtok函数会将这个片段尾部的那个分隔符改为’\0’,然后返回指向这个片段的指针(因此strtok函数会改变被操作的字符串,故使用strtok函数切分的字符串一般都是可修改的临时拷贝的内容)。
-如果字符串中不存在更多片段,则返回 NULL 指针。

使用例

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

int main(){
    char str[] = "- This, a sample string.";
    char* pch;
    pch = strtok(str, " ,.-");//对某个字符串初次调用,第一个参数需传待分割字符串的地址
    while (pch != NULL){
        printf("%s\n", pch);
        pch = strtok(NULL, " ,.-");//而对同一字符串的后续调用,第一个参数只需要传空指针
    }
    return 0;
}

输出:
在这里插入图片描述

错误信息报告

strerror

返回错误码所对应的错误信息。

声明:

依赖头文件string.h

char * strerror ( int errnum );

errnum:错误码
返回的char*:传入的错误码所对应的错误信息的字符串的地址

使用例

c语言库函数调用(执行)失败时会把错误码存到c语言内置的errno变量中,可以使用strerror解读此错误码:

printf("%s\n",strerror(errno));
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

int main(){
    for (int n = -1; n < 50; n++) {
        printf("%d.%s\n", n,strerror(n));
    }
    return 0;
}

输出:
在这里插入图片描述

perror

依赖头文件stdio.h
在传入的字符串后增加冒号和空格,再加上本次函数调用时errno变量中错误码所对应的错误信息字符串

声明:

void perror ( const char * str );

使用例

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main(){
    for (int n = -1; n < 5; n++) {
        int errno = n;//就结果而言,这里的赋值并不影响内置的errno变量
        perror("测试报错");
    }
    return 0;
}

输出:
在这里插入图片描述

内存操作函数

memcpy

拷贝指定字节长度的内存中的内容,不限于字符串

声明:

void* memcpy ( void* dest, const void* src, size_t num );

-函数memcpy从src的位置开始向后,复制num个字节的数据到dest的内存位置。
-src和dest的空间不能有重叠,否则复制的结果会是未定义的。(对于重叠的空间,应使用memmove)(另,vs下的memcpy可以做到对重叠空间的操作,但不应指望所有编译器都支持这样做)
-这个函数不关心是否遇到’\0’ 。

模拟实现memcpy

void* my_memcpy(void* dest, const void*src,size_t num) {
	assert(dest && src);
	void* ret = dest;//void*可以直接被赋值
	while (num--){
		*(char*)dest = *(char*)src;
		//void*不能直接运算,故强制类型转换为大小为1比特的char的指针再解引用
		dest = (char*)dest + 1;//指针+1即为向后1个所指的类型的长度
		src = (char*)src + 1;//用这种写法进行指针++在所有编译器下都能运行
	}
	return ret;
}

memmove

包含了memcpy的功能,且可对重叠的空间进行操作

声明:

void * memmove ( void * dest, const void * src, size_t num );

返回值和参数与memcpy的相同

模拟实现memmove

void* my_memmove(void* dest, const void* src,size_t num) {
	assert(dest && src);
	void* ret = dest;//void*可以直接被赋值	
	if (dest < src) {
		//先拷低地址,以避免覆盖,实现方式同memcpy
		while (num--){
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else {
		//先拷高地址,以避免覆盖
		//src与dest不重合的情况无所谓先从哪边开始,为方便写条件也放这里处理
		while (num--){
			*((char*)dest + num) =*((char*)src + num);
			//第一次进来这里的num已经-1,加上src便是指向src最后一个元素的指针,对dest同理
		}
	}
	return ret;
}

memcmp

指定两段相同长度的内存空间,逐字节进行比较

声明:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

num是要比较的长度,单位为字节
返回值同strcmp,返回的是代表两段空间中第一个不相同的字符的ASCII码值的大小关系的整型:

如果返回值小于 0,则表示 ptr1 小于 ptr2。
如果返回值大于 0,则表示 ptr1 大于 ptr2。
如果返回值等于 0,则表示 ptr1 等于 ptr2。

memset

将指定范围的空间里的内容改为指定的字符

声明:

void * memset ( void * ptr, int value, size_t num );

ptr: 指向要填充的内存块的指针。
value: 要设置的值。 该值作为 int 传递,但该函数使用该值的无符号字符转换填充内存块。
num: 要设置为value的字节数。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 10:25:04  更:2022-08-06 10:28:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:25:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码