刚开始学习C语言的时候,就自己写过一些常用的字符串操作函数,比如strcmp,strcpy,strcat,strlen,现在再重新回顾一下,发现之前的写法还有可以简化的地方,于是便又写了一遍,这次顺便把strncmp,strncpy,strncat,strnlen也写了一下。
前言
首先引入一个函数:assert()
C语言中的 assert() 方法可以诊断程序 bug,使用之前应该包含 <assert.h> 头文件,它的C语言原型如下:
void assert(int expression);
参数 expression 可以是任意C语言表达式。如果表达式的计算结果为真,assert() 不执行任何操作,反之,如果表达式的计算结果为假,assert() 将在 stderr 上显示错误信息并且终止程序运行。
assert() 一般用于跟踪C语言程序的运行时(与编译时不同)错误,一般这样的错误不是语法错误,所以能够编译通过,但是最终得到的C语言程序在执行时,可能会给出不预期的错误结果。
int mystrlen(const char* str1)
{
assert(str1 != nullptr);
int len = 0;
while (*str1++)
{
len++;
}
return len;
}
int main()
{
char *str1 = nullptr;
cout << mystrlen(str1) << endl;
return 0;
}
比如在上面的字符串操作函数中,如果传入的str为nullptr,则程序运行会报错,报错信息如下:
与此同时,后续的C语言代码将不再有执行机会。
1.strcmp和strncmp
#include <iostream>
#include <assert.h>
using namespace std;
int mystrcmp(const char* str1, const char* str2)
{
assert(str1 != nullptr && str2 != nullptr);
//返回的是首个不相等的字符的ASCII值的差
//如果相等,返回0;如果str1 > str2,返回正数;如果str1 < str2,返回负数。
while (*str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
}
return *str1 - *str2;
}
int mystrncmp(const char* str1, const char* str2, int n)
{
assert(str1 != nullptr && str2 != nullptr);
//循环结束,找到的是第n个字符。如果条件改成 n--,则找到的是第 n + 1 个字符。
while (--n && *str1 && *str2 && *str1 == *str2)
{
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr[] = "asdf";
char str[] = "asdf";
char str1[] = "asdg";
cout << mystrcmp(arr, str) << endl; //0
cout << mystrcmp(arr, str1) << endl; //102
cout << mystrncmp(arr, str1, 3) << endl;//0
return 0;
}
2.strlen和strnlen
strnlen功能:获取字符串str中实际字符个数,不包括结尾的'\0';
如果实际个数 <= 整个字符串长度,则返回n,否则返回整个字符串的长度。
#include <iostream>
#include <assert.h>
#include <string.h>
using namespace std;
int mystrlen(const char* str1)
{
assert(str1 != nullptr);
int len = 0;
while (*str1++)
{
len++;
}
return len;
}
int mystrnlen(const char* str1, int n)
{
assert(str1 != nullptr);
int len = 0;
while (len != n && *str1++)
{
len++;
}
return len;
}
int main()
{
char str1[20] = "hello";
cout << mystrlen(str1) << endl; //5
cout << mystrnlen(str1, 3) << endl; //3
cout << mystrnlen(str1, 7) << endl; //5
return 0;
}
3.strcpy和strncpy
#include <iostream>
#include <assert.h>
using namespace std;
char* mystrcpy(char* str1, const char* str2)
{
assert(str1 != nullptr && str2 != nullptr);
char* begin = str1;
while((*str1++ = *str2++) != '\0'){} // "!= '\0'" 可以省略
return begin;
}
char* mystrncpy(char* str1, const char* str2, int n)
{
assert(str1 != nullptr && str2 != nullptr);
char* begin = str1;
while (n-- && (*str1++ = *str2++)) {} //循环结束时,str指向第n个字符
*str1 = '\0'; //在结尾添加'\0'
return begin;
}
int main()
{
char str1[8];
char str2[] = "asdfgf";
cout << mystrcpy(str1, str2) << endl; //asdfgf
cout << mystrncpy(str1, str2, 3) << endl; //asd
return 0;
}
4.strcat和strncat
strncat功能:将第二个参数的前n个字符拷贝到第一个字符串参数的末尾。
#include <iostream>
#include <assert.h>
#include <string.h>
using namespace std;
char* mystrcat(char* str1, const char* str2)
{
assert(str1 != nullptr && str2 != nullptr);
char* begin = str1;
while (*++str1) {} //找到str1的'\0'字符
while((*str1++ = *str2++)){} //将str2拷贝到str1的末尾,退出循环时,str2的'\0'也被拷贝
return begin;
}
char* mystrncat(char* str1, const char* str2, int n)
{
assert(str1 != nullptr && str2 != nullptr);
char* begin = str1;
while (*++str1) {} //找到str1的'\0'字符
while (n-- && (*str1++ = *str2++)) {}
*str1 = '\0'; //在末尾加上'\0'
return begin;
}
int main()
{
char str1[20] = "hello";
char str2[] = "world";
cout << mystrcat(str1, str2) << endl; //helloworld
cout << mystrncat(str1, str2, 3) << endl; //helloworldwor
return 0;
}