C语言学习之字符函数和字符串函数

        在C语言前面的内容中,我们学习到的库函数主要还是针对输入输出以及数字相关的内容,今天我们来学习一下有关于字符相关的函数,来对字符和字符串进行操作。

目录

字符分类函数

字符转换函数

以下的函数到strtok函数使用均需要包含头文件

strlen函数的使用和模拟实现

       模拟实现

strcpy函数的使用和模拟实现

模拟实现

strcat函数的使用和模拟实现

模拟实现:

strcmp函数的使用和模拟实现 

模拟实现: 

strncpy函数的使用

strncat函数的使用

strncmp函数的使用

strstr函数的使用和模拟实现

模拟实现

strtok函数的使用

strerror函数

perrior函数

strdup函数

1. 函数定义与功能

2. 工作流程

3. 手动实现(兼容性方案)

4. 使用示例

5. 与手动malloc+strcpy的对比

使用strdup

6. 注意事项

7. 相关函数对比

8. 常见问题

Q1: 为什么strdup不属于C89/C99标准?

Q2: 如何检测strdup是否可用?

Q3: strdup与strcpy的性能差异?

ASCII值字符表

一、标准ASCII字符表(0-127)

二、控制字符和转义字符表(0-31 和 127)

    C语言官方文档ASCII 图表


字符分类函数

        C语言中有一系列自带的函数专门处理字符分类,即处理一个字符属于什么类型的。使用这些函数需要包含头文件<ctype.h>

以下就是<ctype.h>头文件中所有函数的函数名及其返回为真的条件

函数名返回真值的条件(参数满足)
isalnum(int c)c 是字母(a-z/A-Z)或数字(0-9
isalpha(int c)c 是字母(a-z/A-Z
iscntrl(int c)c 是控制字符(ASCII 0-31 或 127)
isdigit(int c)c 是十进制数字(0-9
isgraph(int c)c 是除空格外的可打印字符(ASCII 33-126)
islower(int c)c 是小写字母(a-z
isprint(int c)c 是可打印字符(包括空格,ASCII 32-126)
ispunct(int c)c 是标点符号(即非空格、非字母数字的可打印字符,如 !@#$%^&*() 等)
isspace(int c)c 是空白字符(空格 ' '、换行符 '\n'、制表符 '\t'、回车 '\r'、垂直制表符 '\v'、换页符 '\f'
isupper(int c)c 是大写字母(A-Z
isxdigit(int c)c 是十六进制数字(0-9/a-f/A-F

注意:

所有函数的参数 c 应为 unsigned char 类型或 EOF(通常定义为 -1)。

直接传递 char 类型可能导致符号扩展问题(如 char 为负值时行为未定义)。

        使用的方法十分简单,如下:

int islower(int c);

        练习:挑出一段字符中所有的数字:

#include<stdio.h>
#include<ctype.h>
int main()
{
	char ch[] = "ABCcdfe123###26 qwe21w";
	int i = 0;
	while (ch [i] != '\0')
	{
		if (isdigit(ch[i]) !=0)
		{
			printf("%c ",ch[i]);
		}
		i++;
	}
	return 0;
}

        练习:将一段字符中所有的大写字母转化为小写字母:

#include<stdio.h>
#include<ctype.h>
int main()
{
	char ch[] = "ABCcdfe123###26 qwe21w";
	int i = 0;
	while (ch[i] != '\0')
	{
		if (islower(ch[i]) != 0)
		{
			ch[i] = ch[i] - 32;
			
		}
		printf("%c", ch[i]);
		i++;
	}
	return 0;
}

字符转换函数

        C语言有两个字符转换函数

int tolower(int c)//将参数传入进去的大写字母转化为小写
int toupper(int c)//将参数传入进去的小写字母转化为大写

以下的函数到strtok函数使用均需要包含头文件<string.h>

strlen函数的使用和模拟实现

        使用的基本语法:

size_t strlen(const char* str);

        字符串以’\0‘为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包括’\0‘)

        但是参数str指向的字符串必须以'\0'为结束标识

        注意函数的返回值为size_t,为无符号类型(注意!!!)

       模拟实现

        方案1(计数器):

int my_strlen(const char* str)
{
	int count = 0;
	assert(str!=NULL);
	while (*str)
	{
		count++:
		str++;
	}
	return count;
}

        方案2(递归):

int my_strlen(const char* str)
{
	assert(str);
	if (*str=='\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}

        方案3(指针—指针):

int my_strlen(const char* str)
{
	assert(str);
	char* p = str;
	while (*p != '\0')
	{
		p++;
	}
	return p - str;
}

strcpy函数的使用和模拟实现

        strcpy函数是字符串拷贝函数。

在实际应用中如果我们要进行字符的赋值是不能这么做的

char st1[]="hello world";
char st2[] = "abcdfe";
st1[] = st2[];//这么写是绝对错误的

        因为数组本质上是指针,是常量值,指针是不能改变的。

        因此我们用strcpy函数来实现字符串的复制拷贝

        基本语法如下:

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

其中destination为目标字符串,source为源头字符串

        源字符串必须以’\0‘结束,并且会将源字符串的'\0'拷贝至目标空间

        目标空间必须足够大,并且必须可以修改

模拟实现

        主函数:

int main()
{
	char ch1 [] = "ABCDEFGHIKJHG";
	char ch2 [] = "hello world";
	my_strcpy(ch1, ch2);
	printf("%s\n", ch1);
	return 0;
}

        方案1:

void my_strcpy(char *dest ,char *soc)
{
	char* ret = *soc;
	assert(dest!=NULL && soc!=NULL);
	while (*soc!='\0')
	{
		*dest = *soc;
		soc++;
		dest++;
		//可以简写为*dest++ = *soc++;
	}
	*dest = *soc;//拷贝'\0'
}

        方案2: 

char* my_strcpy(char *dest ,char *soc)
{
	char* ret = *soc;
	assert(dest!=NULL && soc!=NULL);
	while (*soc!='\0')
	{
		*dest = *soc;
		soc++;
		dest++;
		//可以简写为*dest++ = *soc++;
	}
	*dest = *soc;//拷贝'\0'
	return ret;//返回原字符串的地址
}

strcat函数的使用和模拟实现

        strcat函数的作用就是将两个字符串函数拼接(追加)起来。

        语法上与strcpy很类似:

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

其中destination为目标字符串,source为源头字符串 

        源字符串和目标字符串都必须有’\0‘结束,如果目标字符串没有'\0'不知道从哪里开始 

        目标空间必须足够大,并且必须可以修改

应用举例:

int main()
{
	char ch1 [200] = "hello";
	char ch2 [] = "  world";
	strcat(ch1, ch2);
	printf("%s\n", ch1);
	return 0;
}

 

模拟实现:

char* my_strcat(char* dest, const char* soc)
{
	assert(dest!=NULL && soc!=NULL);
	char* ret = dest;
	//找到*dest指向的'\0'
	while (*dest!='\0')
	{
		dest++;
	}
	//然后数据拷贝
	while (*dest++ = *soc++)
	{
		;//空语句
	}
	return ret;//返回原字符串的地址
}

 但是请注意,这么写运行不通过:

char* my_strcat(char* dest, const char* soc)
{
	assert(dest!=NULL && soc!=NULL);
	char* ret = dest;
	//找到*dest指向的'\0'
	while (*dest!='\0')
	{
		dest++;
	}
	//然后数据拷贝
	while (*dest++ = *soc++)
	{
		;//空语句
	}
	return ret;//返回原字符串的地址
}
int main()
{
	char ch1 [200] = "hello";
	char ch2 [] = "  world";
	my_strcat(ch1, ch1);
	printf("%s\n", ch1);
	return 0;
}

因为自己给自己追加会消除掉'\0',就会造成死循环,因此程序就会崩溃。 

strcmp函数的使用和模拟实现 

        strcmp函数是用来比较字符串大小的函数。

比较标准:比较两个字符串中对应位置上字符ASCII码值的大小(一旦有一个字符比较出来了大小就停止不再进行下一个字符的比较大小,如果二者相等就继续比较下一个位置的字符)

如果第一个字符串大于第二个字符串,则返回一个大于0的数字

如果第一个字符串等于第二个字符串,则返回0

如果第一个字符串小于第二个字符串,则返回一个小于0的数字

对比辨析:

模拟实现: 

int my_strcmp(const char* str1, const char* str2)
{
	int ret = 0;
	assert(str1!=NULL && str2!=NULL);
	while (*str1==*str2 )
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

 以上的三个字符函数是长度受限制的字符串函数,接下来我们要学习长度不受限制的字符串函数

strncpy函数的使用

        strncpy函数语法如下:

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

         

int main()
{
	char arr1[200] = "abcdfghijk";
	char arr2[20] = "  ";
	strncpy(arr2, arr1,5);
	printf("%s\n",arr2);
	return 0;
}

当不够的时候,自动补齐“\0”。

strncat函数的使用

        strncat函数的语法结构如下:

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

int main()
{
	char arr1[200] = "abcdfghijkxxxxxxxxxxxxxx";
	char arr2[20] = "xxx";
	strncat(arr2, arr1,5);
	printf("%s\n",arr2);
	return 0;
}

strncmp函数的使用

strcnmp函数的语法如下:

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

strstr函数的使用和模拟实现

           strstr函数主要是用于在一个字符串中找另外一个子字符串是否存在的函数。

           strstr函数的语法类型:

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

         如果找到了,返回起始地址;如果没找到返回NULL。

应用:str1中找str2第一次出现的位置,如果找到了返回起始位置的地址。

int main()
{
	char arr1[100] = "sgudusbvdugdabngfduedudhuidguadbefgbeudhidugauidufuegeuiw";
	char arr2[100] = "ab";
	const char *ret=strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到");
	}
	else
	{
		printf("%s\n",ret);
	}
	
	return 0;
}

模拟实现

方案1:

const char* my__strstr(const char* str1, const char* str2)
{
	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cur = str1;
	while (*cur)
	{
		s1=cur;
		s2=str2;
		while (*s1 && *s2 && !(*s1 - *s2))
		{
			s1++;
			s2++;
		}
		if (!*s2)
		{
			return cur;
		}
		cur++;
	}
}

方案2:

char* my_strstr(const char* str1, const char* str2)
{
	char* cp = (char*)str1;
	char* s1, * s2;
	if (!*str2)
	{
		return (char*)str1;
	}
	while (*cp)
	{
		s1 = cp;
		s2 = (char*)str2;
		while (*s1 && *s2 && !(*s1 - *s2))
		{
			s1++;
			s2++;
		}
		if (!*s2)
		{
			return cp;
		}
		cp++;
	}
	return NULL;
}

strtok函数的使用

        strtok函数是用来提取被分隔符分隔的字符串的函数。

        如邮箱“loutianlizi@qq.com”

        经过strtok函数后产生三段字符串“loutianlizi”,“qq”,“com”

        strtok函数的语法形式为:

char*strtok(char*str,const char *sep);

        sep参数指向一个字符串,定义了用作分隔符发字符集合

        第一个参数指定一个字符串,它包含了0个或者多个sep参数中一个或者多个分隔符分隔的标记

        strtok函数找到str中下一个标记,并将其用\0结尾,返回一个指向这个标记的指针(注:strtok函数会改变该被操作的字符串,所以被strtok切割的字符串一般都是临时拷贝内容且可以修改)

        strtok第一个参数不为NULL,函数将找到str第一个标记,strtok函数将它保存在字符串中的位置; strtok第一个参数为NULL,函数将在同意字符串中被保存的位置开始,查找下一个标记

        如果不存在更多标记则返回NULL指针

int main()
{
	char arr[] = "hello world";
	char* sep = " . ";
	char* str = NULL;
	for (str==strtok(arr,sep);str!=NULL;str=strtok(NULL,sep))
	{
		printf("%s\n",str);
	}
	return 0;
}

strerror函数

        strerror函数的语法如下:

char*strerror(int errnum);

        strerror可以将参数部分错误码对应的错误信息的字符串地址返回回来。

        在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局的变量 errno 来记录程序的当前错误码,只不过程序启动的时候errno是 0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应的错误码,存放在 errno 中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

perrior函数

        perroir函数功能同上,直接打印错误信息。打印完参数部分后,再打印“:”和空格,接着打印错误信息。

        今天的内容就到这里了,谢谢各位读者朋友,求一个赞谢谢。

以下的内容仅供查询参考。

strdup函数

1. 函数定义与功能

定义

strdup函数是通过封装内存分配和字符串复制操作,简化了字符串的动态复制流程的函数。但其使用时需注意内存管理和平台兼容性。strdup是C标准库(非C89/C99标准,属于POSIX标准,C23引入)中的一个字符串处理函数,用于复制字符串并动态分配内存

在需要动态创建字符串副本的场景中,合理使用strdup可提升代码的简洁性和可维护性。
其原型为:

#include <string.h>  // 需要包含的头文件

char* strdup(const char* s);

功能

  • 输入:接收一个以\0结尾的源字符串指针 s

  • 输出:返回一个指向新分配内存的指针,该内存包含源字符串的副本。

  • 内存管理:内部调用 malloc 分配内存,调用者需负责后续的 free 释放。

2. 工作流程

  1. 验证输入:若 s 为 NULL,行为未定义(可能导致崩溃)。

  2. 计算长度:通过 strlen(s) 获取源字符串长度 len

  3. 分配内存:调用 malloc(len + 1) 分配足够容纳字符串及其终止符\0的内存。

  4. 复制内容:使用 strcpy 或 memcpy 将源字符串复制到新内存。

  5. 返回指针:返回新内存的指针,若分配失败则返回 NULL

3. 手动实现(兼容性方案)

对于不支持 strdup 的环境(如旧版C标准),可自定义实现:

#include <stdlib.h>
#include <string.h>

char* my_strdup(const char* s) {
    if (s == NULL) return NULL;
    size_t len = strlen(s) + 1;
    char* copy = (char*)malloc(len);
    if (copy != NULL) {
        memcpy(copy, s, len);  // 比strcpy更高效
    }
    return copy;
}

4. 使用示例

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main() {
    const char* original = "Hello, World!";
    
    // 使用strdup复制字符串
    char* copy = strdup(original);
    if (copy == NULL) {
        perror("strdup失败");
        return 1;
    }
    
    printf("原字符串:%s\n", original);
    printf("副本字符串:%s\n", copy);
    
    free(copy);  // 必须释放内存
    return 0;
}

输出: 

原字符串:Hello, World!
副本字符串:Hello, World!

5. 与手动malloc+strcpy的对比

使用strdup
char* copy = strdup(original);
if (copy) { /* ... */ }
free(copy);

手动实现

size_t len = strlen(original) + 1;
char* copy = (char*)malloc(len);
if (copy) {
    strcpy(copy, original);
    /* ... */
}
free(copy);

优势

  • 简化代码,减少错误(如忘记+1strcpy)。

  • 提高可读性。

6. 注意事项

注意事项说明
内存泄漏必须手动free返回的指针,否则内存泄漏。
输入验证若传入NULL,可能导致崩溃(未定义行为)。
跨平台兼容性Windows下需使用_strdup(需定义_CRT_NONSTDC_NO_DEPRECATE)。
错误处理必须检查返回值是否为NULL(内存分配失败时)。
性能相比直接操作内存,多一次strlen调用(但通常可忽略)。

7. 相关函数对比

函数功能内存管理标准化
strdup复制字符串并分配内存需手动freePOSIX/C23
strndup复制前n个字符并分配内存需手动freePOSIX
strcpy复制字符串到已有内存目标内存需预先分配C89
snprintf格式化复制到缓冲区目标内存需预先分配C99

8. 常见问题

Q1: 为什么strdup不属于C89/C99标准?
  • 历史原因strdup最初是POSIX标准的扩展函数,C23才将其纳入标准库。

  • 替代方案:使用malloc+strcpy组合。

Q2: 如何检测strdup是否可用?
  • 编译器检查

    #ifdef _POSIX_C_SOURCE
        // 使用strdup
    #else
        // 使用自定义实现
    #endif
    Q3: strdupstrcpy的性能差异?
  • strdup多一次strlen调用和内存分配,适用于动态需求;strcpy更高效,适用于静态内存操作。

ASCII值字符表

一、标准ASCII字符表(0-127)

十进制十六进制字符/符号描述
0-310x00-0x1F控制字符不可打印,具体功能见下方表格
320x20空格(Space)
330x21!感叹号
340x22"双引号
350x23#井号
360x24$美元符号
370x25%百分号
380x26&与符号
390x27'单引号
400x28(左圆括号
410x29)右圆括号
420x2A*星号
430x2B+加号
440x2C,逗号
450x2D-减号/连字符
460x2E.句号
470x2F/斜杠
48-570x30-0x390-9数字字符
580x3A:冒号
590x3B;分号
600x3C<小于号
610x3D=等号
620x3E>大于号
630x3F?问号
640x40@at符号
65-900x41-0x5AA-Z大写字母
910x5B[左方括号
920x5C\反斜杠
930x5D]右方括号
940x5E^脱字符
950x5F_下划线
960x60```反引号
97-1220x61-0x7Aa-z小写字母
1230x7B{左花括号
1240x7C``竖线
1250x7D}右花括号
1260x7E~波浪号
1270x7FDEL删除字符(不可打印)

二、控制字符和转义字符表(0-31 和 127)

十进制十六进制转义序列符号功能
00x00\0NUL空字符(字符串结束符)
70x07\aBEL响铃(终端提示音)
80x08\bBS退格(Backspace)
90x09\tHT水平制表符(Tab)
100x0A\nLF换行(Line Feed)
110x0B\vVT垂直制表符
120x0C\fFF换页符(Form Feed)
130x0D\rCR回车(Carriage Return)
270x1B\eESC退出键(终端控制序列起始)
1270x7F-DEL删除字符(历史用途)

    C语言官方文档ASCII 图表

 

下表包含所有 128 个 ASCII 十进制 (dec)、八进制 (oct)、十六进制 (hex) 和字符 (ch) 代码。

decocthexchdecocthexchdecocthexchdecocthexch
0000NUL(空)324020(空格)6410040@9614060`
1101SOH(标头的开头)334121!6510141A9714161a
2202STX(文本开头)344222"6610242B9814262b
3303ETX(文末)354323#6710343C9914363c
4404EOT(传输结束)364424$6810444D10014464d
5505ENQ(查询)374525%6910545E10114565e
6606ACK(确认)384626&7010646F10214666f
7707BEL(铃铛)394727'7110747G10314767g
81008BS(退格键)405028(7211048H10415068h
91109HT(水平选项卡)415129)7311149I10515169i
10120aLF(换行 - 新行)42522a*741124aJ1061526aj
11130bVT(垂直选项卡)43532b+751134bK1071536bk
12140cFF(表单馈送 - 新页面)44542c,761144cL1081546cl
13150dCR(回车)45552d-771154dM1091556dm
14160eSO(移出)46562e.781164eN1101566en
15170fSI(Shift 键)47572f/791174fO1111576fo
162010DLE(数据链路转义)48603008012050P11216070p
172111DC1(设备控制 1)49613118112151Q11316171q
182212DC2(设备控制 2)50623228212252R11416272r
192313DC3(设备控制 3)51633338312353S11516373s
202414DC4(设备控制 4)52643448412454T11616474t
212515NAK(否定承认)53653558512555U11716575u
222616SYN(同步空闲)54663668612656V11816676v
232717ETB(传输块末端)55673778712757W11916777w
243018CAN(取消)56703888813058X12017078x
253119EM(媒介结束)57713998913159Y12117179y
26321aSUB(替补)58723a:901325aZ1221727az
27331bESC(逃脱)59733b;911335b[1231737b{
28341cFS(文件分隔符)60743c<921345c\1241747c|
29351dGS(组分隔符)61753d=931355d]1251757d}
30361eRS(记录分隔符)62763e>941365e^1261767e~
31371fUS(单位分隔符)63773f?951375f_1271777fDEL(删除
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值