字符串中的一些函数
strlen函数计算字符串长度
size_t strlen(const char*str);
strlen是求字符串长度的,只针对字符串长度,是一种库函数,使用时必须引用头文件,求字符串长度时读取到空字符时才会停下
sizeof计算变量、数组类型的,大小单位是字节,是一种操作符
字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')
参数指向的字符串必须要以'\0'结束
注意函数的返回值为size_t,是无符号整型
strcpy函数拷贝替换数组元素
char*strcpy(char*destination,const char*source);
源字符串必须以'\0'结束
能够将源字符串的'\0'拷贝到目标空间
目标空间必须可变(即不能是字符串常量)
错误示范:
char arr1[]="abcdefg";//编译器会自动在末尾添加空字符
char arr2[]={'a','b','c','d','e','f','g'};//根本就没有给空字符留位置
用自己写的函数表达strcpy函数就是:
#include<stdio.h>
#include<assert.h>
char*my_strcpy(char* dest,const char* src)
{
assert(dest);
assert(src);
char* ret = dest;//拷贝src指向的字符串到dest指向的空间中,包含'\0'
while (*dest++ = *src++);//返回目的空间的起始地址
*dest = *src;
return ret;
}
int main(void)
{
char arr1[30] = "I am a Chinese.";
char arr2[30] = "He is an American.";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
strcat函数追加数组元素
char*strcat(char*destination,const char*source);
源字符串必须以'\0'结束
目标空间必须足够大,才能容下源字符串的内容
目标空间必须可修改(即不能是字符串常量)
源字符串不能自己给自己添加
#include<stdio.h>
in main(void)
{
char dest[30]="Our wold ";
char src[]="is beautiful.";
strcat(dest,src);
printf("%s\n", dest);
return 0;
}
错误示范:
char dest[]="Our wold ";//空间不足,会出现栈损坏,程序奔溃
char src[]="is beautiful.";
用自己编写的的函数表达strcat函数:
char* my_strcat(char* dest,const char* src)
{
char* ret=dest;
assert(dest);
assert(src);
while(*dest)
{
dest++;
}
while(*dest++=*src++);
return ret;
}
strcmp函数字符串的比较
int strcmp(const char*str1,const char*str2);
比较的是ASC码值
例如:strcmp(str1,str2)
如果str1<str2,则返回一个小于零的数
如果str1>str2,则返回一个大于零的数
如果str1==str2,则返回一个0
切记:返回的值是不可确定的,只能知道正负与0,所以编辑代码时不能通过直接判断是否等于确切的数值来判断大小
字符串中另外一些函数
情情提要:strcpy函数、strcat函数、strcmp函数都不受长度限制,所以存在一定的不安全性,于是有了对应的另一些相对安全的函数解决长度不受限的问题
分别是strncpy函数、strncat函数、strncmp函数,原理是一样的,只是多了一个参数控制大小
char*strncpy(char*destination,const char*source,size_t num);
char*strncat(char*destination,const char*source,size_t num);
char*strncmp(char*destination,const char*source,size_t num);
如果size_f count的值大于源字符串,那么后面的值会补上空符号'\0'
assert()断言函数
用于在调试过程中捕捉程序错误
void assert (int expression);
断言函数,用于在调试过程中捕捉程序的错误。
“断言”在语文中的意思是“断定”、“十分肯定地说”,在编程中是指对某种假设条件进行检测,如果条件成立就不进行任何操作,如果条件不成立就捕捉到这种错误,并打印出错误信息,终止程序执行。
assert() 会对表达式expression进行检测:
如果expression的结果为 0(条件不成立),那么断言失败,表明程序出错,assert() 会向标准输出设备(一般是显示器)打印一条错误信息,并调用 abort() 函数终止程序的执行。
如果expression的结果为非 0(条件成立),那么断言成功,表明程序正确,assert() 不进行任何操作。
要打印的错误信息的格式和内容没有统一的规定,这和标准库的具体实现有关(也可以说和编译器有关),但是错误信息至少应该包含以下几个方面的信息:
1.断言失败的表达式,也即expression
2.源文件名称
3.断言失败的代码的行号
大部分编译器的格式如下所示:
Assertion failed: expression, file filename, line number
参数expression是要检测的表达式。如果表达式的值为 0,那么断言失败,程序终止执行;如果表达式的值为非 0,那么断言成功,assert() 不进行任何操作。
aasert()函数无返回值
assert() 的用法和机制:
在大部分编译器下,assert() 是一个宏;在少数的编译器下,assert() 就是一个函数。我们无需关心这些差异,只管把 assert() 当做函数使用即可。
assert() 的用法很简单,我们只要传入一个表达式,它会计算这个表达式的结果:如果表达式的结果为“假”,assert() 会打印出断言失败的信息,并调用 abort() 函数终止程序的执行;如果表达式的结果为“真”,assert() 就什么也不做,程序继续往后执行。
下面是一个具体的例子:
#include <stdio.h>
#include <assert.h>
int main(){
int m, n, result;
scanf("%d %d", &m, &n);
assert(n != 0); //写作 assert(n) 更加简洁
result = m / n;
printf("result = %d\n", result);
return 0;
}
本例用来计算两个数相除的结果,由于被除数不能为 0,所以我们加入了 assert() 来检测错误。
如果输入100 20,那么 n 的值为 20,n != 0这个条件成立,assert() 不进行任何操作,最终的输出结果为:
result = 5
如果输入100 0,那么 n 的值为 0,n != 0这个条件不成立,assert() 就会报告错误,并终止程序执行,最终的结果如下所示:
Assertion failed: n != 0, file E:\CDemo\main.c, line 6
NDEBUG 宏
如果查看 <assert.h> 头文件的源码,会发现 assert() 被定义为下面的样子:
#ifdef NDEBUG
#define assert(e) ((void)0)
#else
#define assert(e) \
((void) ((e) ? ((void)0) : __assert (#e, __FILE__, __LINE__)))
#endif
这意味着,一旦定义了NDEBUG宏,assert()就无效了。
NDEBUG 是”No Debug“的意思,也即“非调试”。有的编译器(例如 Visual Studio)在发布(Release)模式下会定义 NDEBUG 宏,在调试(Debug)模式下不会定义定义这个宏;有的编译器(例如 Xcode)在发布模式和调试模式下都不会定义 NDEBUG 宏,这样当我们以发布模式编译程序时,就必须自己在编译参数中增加 NDEBUG 宏,或者在包含 <assert.h> 头文件之前定义 NDEBUG 宏。
调试模式是程序员在测试代码期间使用的编译模式,发布模式是将程序提供给用户时使用的编译模式。在发布模式下,我们不应该再依赖 assert() 宏,因为程序一旦出错,assert() 会抛出一段用户看不懂的提示信息,并毫无预警地终止程序执行,这样会严重影响软件的用户体验,所以在发布模式下应该让 assert() 失效。
修改上面的代码,在包含 <assert.h> 之前定义 NDEBUG 宏:
#define NDEBUG
#include <stdio.h>
#include <assert.h>
int main(){
int m, n, result;
scanf("%d %d", &m, &n);
assert(n);
result = m / n;
printf("result = %d\n", result);
return 0;
}
当以发布模式编译这段代码时,assert() 就会失效。如果希望继续以调试模式编译这段代码,去掉 NDEBUG 宏即可。
在代码中显式地增加 NDEBUG 宏比较麻烦,因为当以调试模式编译代码时还得再去掉它,更加科学的做法是在 IDE 的编译参数中添加 NDEBUG 宏。不同的 IDE 添加宏的方式不同,这里不再深入探讨。
注意事项:
-
使用 assert() 时,被检测的表达式最好不要太复杂,以下面的代码为例:
assert( expression1 && expression2 && expression3);
当发生错误时,assert() 只会告诉我们expression1 && expression2 && expression3整个表达式为不成立,但是这个大的表达式还包含了三个小的表达式,并且它们之间是&&运算,任何一个小表达式为不成立都会导致整个表达式为不成立,这样我们就无法推断到底是expression1有问题,还是expression2或者expression3有问题,从而给排错带来麻烦。
这里我们应该遵循使用 assert() 的一个原则:每次断言只能检验一个表达式。根据这个原则,上面的代码应改为:
assert(expression1);
assert(expression2);
assert(expression3);
如此,一旦程序出错,我们就知道是哪个小的表达式断言失败了,从而快速定位到有问题的代码。
-
使用 assert() 的另外一个注意事项是:不要用会改变环境的语句作为断言的表达式。请看下面的代码:
#include <stdio.h> #include <assert.h> int main(){ int i = 0; while(i <= 110){ assert(++i <= 100); printf("我是第%d行\n",i); } return 0; }
在 Debug 模式下运行,程序循环到第 101 次时,i 的值为 100,++i <= 100不再成立,断言失败,程序终止运行。
在 Release 模式下运行,编译参数中设置了 NDEBUG 宏(如果编译器没有默认设置,那么需要你自己来设置),assert() 会失效,++i <= 100这个表达式也不起作用了,while() 无法终止,成为一个死循环。
定义了 NDEBUG 宏后,assert(++i <= 100)会被替换为((void)0)。
导致程序在 Debug 模式和 Release 模式下的运行结果大相径庭的罪魁祸首就是++运算,我们本来希望它改变 i 的值,逐渐达到 while 的终止条件,但是在 Release 模式下它失效了。
修改这段不规范的代码也很容易,将++i移出 assert() 即可:
#include <stdio.h>
#include <assert.h>
int main(){
int i = 0;
while(i <= 110){
++i;
assert(i <= 100);
printf("我是第%d行\n",i);
}
return 0;
}
如此,不管是 Debug 模式还是 Release 模式,每次循环 i 的值都会增加,逐渐达到 while 的终止条件时,结束循环。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了