目录
长度不受限制的字符串函数 strcpy strcat strcmp
长度受限制的字符串函数介绍 strncpy strncat strncmp
内存操作函数 memcpy memmove memset memcmp
在使用字符串函数时,一定要使用 #include <string.h> 头文件
求字符串长度 strlen
功能:求字符串长度。从开始的第一个字符开始计数,直到遇到'\0'停止计数。如果你只定义了一个字符型数组但没有给它赋初值,这个结果是不定的,它会从首地址一直找下去,直到遇到'0'停止。
注意返回值是size_t。
模拟实现
int my_strlen(const char* str)
{
if (*str == 0)
return 0;
char* end=str;
do
{
end++;
} while (*end);
return end - str;
}
int main()
{
char arr[] = "abcdefgh";
printf("%d", my_strlen(arr));
return 0;
}
长度不受限制的字符串函数 strcpy strcat strcmp
strcpy
功能:将源头指向的 C 字符串复制到目标所指向的数组中,包括终止 null 字符(并在该点停止)。
返回值为目的地的地址,为了便于使用链式访问。
注意:为避免溢出,目标所指向的数组的大小应足够长,以包含与 source 相同的 C 字符串(包括终止的 null 字符)。否则会出现错误。
并且destination在内存中不应与 source 重叠。
如图,拷贝到i时,源头的结束标志变成了正常字符,无法停下。
模拟实现:
char* my_strcpy(char* dest,const char* src)
{
assert(dest && src);//包含头文件:#include <assert.h>
char* r = dest;
while (*dest++ = *src++);
return r;
}
int main()
{
char arr1[] = "**************";
char arr2[] = "abcdefgh";
printf("%s", my_strcpy(arr1,arr2));
return 0;
}
strcat
功能:将源字符串的副本追加到目标字符串。目标中的终止空字符被源的第一个字符覆盖,并且遇到源头的'\0'字符结束,'\0'也被追加了。
返回值为目的地的地址,为了便于使用链式访问。
注意:为避免溢出,目标所指向的数组的大小应足够长,以包含与 source 相同的 C 字符串(包括终止的 null 字符)。否则会出现错误。
目的地和源不得重叠,即自己给自己追加。
如图,源头的'\0'被覆盖了,源头不能遇到'\0'而停下。
模拟实现:
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);//#include <assert.h>
char* r = dest;
while (*dest)
dest++;
while (*dest++ = *src++);
return r;
}
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "xyz!";
printf("%s", my_strcat(arr1, arr2));
return 0;
}
strcmp
功能:将 C 字符串 str1 与 C 字符串 str2 进行比较。此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续执行以下对操作,直到字符不同或达到终止空字符。字符不同时,str1更大返回>0的数,反之返回<0的数,全部相等返回0.
模拟实现:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);//#include <assert.h>
while (*str1 && *str2)
{
if (*str1 != *str2)
return *str1 - *str2;
str1++, str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "abcdefghz";
printf("%d", my_strcmp(arr1, arr2));
return 0;
}
长度受限制的字符串函数介绍 strncpy strncat strncmp
strncpy
功能:将源的前 num 个字符复制到目标。
返回值为目的地的地址,为了便于使用链式访问。
注意:如果在复制数字字符之前找到源 C 字符串(由空字符表示)的末尾,则目标将用零填充,直到总共写入数字字符为止。
如果没复制数字字符之前找到源 C 字符串(由空字符表示)的末尾,则只copy对应数目字符,不会加'\0'。
源头与目的地不应该重叠。如果源头与目的地有重叠会导致拷贝达不到要的效果。
如:
如果想正确拷贝可以调用memomove,利用从后往前拷贝解决。最后结果为:ststunt
模拟实现:
char* my_strncpy(char* dest, char* src,int num)
{
char* ret = dest;
int i = 0;
while (i < num && *src != 0)
{
*dest = *src;
dest++, src++;
i++;
}
while (i < num)
{
*dest = *src;
dest++;
i++;
}
return ret;
}
int main()
{
char a[] = "abcd";
char b[] = "destination";
char c[] = "jiaruxuanni";
printf("%s\n", my_strncpy(b,a,6));
printf("%s\n", my_strncpy(c,a,3));
return 0;
}
strncat
功能:将源的前 num 个字符追加到目标,外加一个终止空字符。
返回值为目的地的地址,为了便于使用链式访问。
注意: 如果源中 C 字符串的长度小于 num,则仅复制到终止空字符之前的内容。
模拟实现:
char* my_strncat(char* dest,const char* src,int num)
{
char* ret = dest;
while (*dest)
{
dest++;
}
int i = 0;
while (i < num && *src)
{
*dest = *src;
src++, dest++, i++;
}
*dest = 0;
return ret;
}
int main()
{
char a[30] = "zhangsan";
char b[] = "--abcd";
printf("%s\n", my_strncat(a, b, 4));
char c[30] = "wangdawu";
printf("%s", my_strncat(c, b, 7));
return 0;
}
strncmp
功能:将 C 字符串 str1 的num个数字字符与 C 字符串 str2 的字符进行比较。
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续执行以下对操作,直到字符不同,直到达到终止空字符,或者直到两个字符串中的数字字符不匹配,字符不同时,str1更大返回>0的数,反之返回<0的数,全部相等返回0.
模拟实现:
int my_strncmp(const char* s1,const char* s2,int num)
{
for (int i = 0; i < num; i++)
{
if (*(s1 + i) == *(s2 + i))
{
if (s1[i] == 0)
return 0;
else
continue;
}
else
return *(s1 + i) - *(s2 + i);
}
return 0;
}
int main()
{
char s1[] = "abcdef";
char s2[] = "abcde";
char s3[] = "abcdek";
printf("%d ", my_strncmp(s1, s2, 4));
printf("%d ", my_strncmp(s1, s2, 6));
printf("%d ", my_strncmp(s1, s3, 6));
printf("%d ", strncmp(s1, s2, 4));
printf("%d ", strncmp(s1, s2, 6));
printf("%d ", strncmp(s1, s3, 6));
return 0;
}
字符串查找 strstr strtok
strstr
功能: 返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针。匹配过程不包括终止空字符,但它在那里停止。
注意:
- 不论
str1
是不是空串,只要str2是空串,总是返回str1
的首地址- 当
str1
是空串,且str2不是空串时,返回NULL
模拟实现:
int fun(const char* dest, const char* src)
{
while (*dest && *src)
{
if (*dest != *src)
return 0;
dest++, src++;
}
if (*src == 0)
return 1;
return 0;
}
char* my_strstr(const char* dest, const char* src)
{
assert(dest && src);
while (*dest)
{
if (fun(dest, src))
return dest;
dest++;
}
return NULL;
}
int main()
{
char arr1[20] = "abcdefxyz!";
char arr2[] = "fxay";
printf("%s", my_strstr(arr1, arr2));
return 0;
}
KMP算法:看这里
strtok
功能:分解字符串为一组字符串。str为要分解的字符,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。
注意:1.每次调用函数都会把查找到的第一个分割字符改成'\0',然后返回分割的两断字符串的前一个字符串的首地址,前一个字符串的末尾地址会被记录下来。 如图:分隔符为'-'。 2. 首次调用时,str指向要分解的字符串,之后再次调用要把s设成NULL,这样函数会使用前一次记录下来的末尾位置作为起始位置开始扫描。否则开始位置还是str开头。
3. 如果第一次分割查找不到delim中的分割字符时,返回当前strtok的字符串的指针。如果第二次 及以后str 已经查找到了终止空字符,则对此函数的所有后续调用(以空指针作为第一个参数)都将返回空指针。
4. 需要注意的是,使用该函数进行字符串分割时,会破坏被原来字符串,调用前和调用后的str已经不一样了,所以一般分割str的临时拷贝。而且不能分割常量字符串。
5. 如果查找过程中,第一到第n个字符都是分隔符,函数将跳过他们,从第一个不是分割字符的字符开始查找处理。
模拟实现:
static char* end =NULL;//记录每次分割后的末尾位置,便于下次调用
static int flag =0;//是否遍历完成了的标志
char* my_strtok(char* str, char* d)
{
if (flag == 1)
{
return NULL;
}
if (str == NULL)
{
str = end + 1;
}
int i = 0;
char arr[2] = "";
arr[0] = str[i];
//找到第一个非分隔符字符
while (strstr(d, arr) && str[i] != 0)
{
str++;
i = 0;
arr[0] = str[i];
}
//遍历完了没有出现非分隔符
if (str[i] == 0)
{
flag = 1;
return NULL;
}
//找到第一个分隔符
while (!strstr(d,arr)&&str[i]!=0)
{
i++;
arr[0] = str[i];
}
if (str[i] != 0)
{
str[i] = 0;
end = str + i;
}
else
{
flag = 1;
}
return str;
}
int main()
{
char str[] = "- This, a sample string.";
char* pch;
pch = my_strtok(str, " ,.-");
while (pch != NULL)
{
printf("%s\n", pch);
pch = my_strtok(NULL, " ,.-");
}
//char a[100] = "12nkdaoiakl";
//char a1[100] = "*****";
//printf("%s\n", strtok(a1,"*"));
//printf("%s\n", strtok(NULL,"*"));
return 0;
}
错误信息报告 strerror
功能:用来依参数errnum 的错误代码来查询其错误原因的描述字符串, 然后将该字符串指针返回.
返回的指针指向静态分配的字符串,程序不得修改该字符串。对此函数的进一步调用可能会覆盖其内容。strerror 产生的错误字符串可能特定于每个系统和库实现。
注意:使用方法,传入
errno即可实现打印错误信息。
#include <errno.h> int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) printf (" %s\n",strerror(errno)); return 0; } 系统调用的错误都存储于 errno中,errno由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。使用必须引用头文件:#include <errno.h>
另外,perror函数与之相似,都可以实现打印错误信息。
#include<errno.h>
int main()
{
FILE* pFile;
pFile = fopen("unexist.ent", "r");
if (pFile == NULL)
{
printf(" %s\n", strerror(errno));
perror("The following error occurred");
}
return 0;
}
字符分类和转换操作函数
使用前需要引用头文件ctype.h
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v' |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
如果 c 是大写字母并且具有小写等效字母,则将 c 转换为其小写等效字母。如果无法进行此类转换,则返回的值 c 保持不变。
如果 c 是小写字母并且具有大写等效字母,则将 c 转换为其大写等效字母。如果无法进行此类转换,则返回的值 c 保持不变。
例子:(注意返回值是int需要赋值给对应字符,而不是直接改了值)
#include<ctype.h>
int main()
{
char arr[] = "I like C VERY muCh!";
for (int i = 0; i < strlen(arr); i++)
{
if (islower(arr[i]))
arr[i]=toupper(arr[i]);
}
for (int i = 0; i < strlen(arr); i++)
{
arr[i] = toupper(arr[i]);
}
printf("%s ", arr);
return 0;
}
内存操作函数 memcpy memmove memset memcmp
memcpy
功能:将数字字节的值从源指向的位置直接复制到目标所指向的内存块。停止依据是字节数。
注意:
1.为避免溢出,目标和源参数所指向的数组大小应至少为数字字节,否则会发生错误。
2.目的地和源头不应重叠,否则可能会与想要的效果不同。
想要的是ststunt ,对于重叠的内存块,memmove 是一种更安全的方法,会考虑这个问题得到正确答案。
但其实很多编译器里的库函数memocpy进行了优化也可以达到效果,但是C语言的标准并不要求memocpy达到这样的效果。
模拟实现:(考虑到优化问题版本)
void* my_memcpy(void* dest, void* src,int num)
{
void* ret = dest;
char* r1 = dest;
char* r2 = src;
if (r1 > r2)
{
for (int i = num-1; i >= 0; i--)
{
*(r1 + i) = *(r2 + i);
}
}
else
{
for (int i = 0; i < num; i++)
{
*(r1 + i) = *(r2 + i);
}
}
return dest;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 12);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
my_memcpy(arr1+6, arr1+4, 12);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
memmove
功能: 将数字字节的值从源指向的位置复制到目标所指向的内存块。复制就像使用了中间缓冲区一样进行,从而允许目标和源重叠。
注意:
为避免溢出,目标和源参数所指向的数组大小应至少为数字字节。
模拟实现:
void* my_memmove(void* dest, void* src, int num)
{
void* ret = dest;
char* r1 = dest;
char* r2 = src;
if (r1 > r2)
{
for (int i = num - 1; i >= 0; i--)
{
*(r1 + i) = *(r2 + i);
}
}
else
{
for (int i = 0; i < num; i++)
{
*(r1 + i) = *(r2 + i);
}
}
return dest;
}
typedef struct s
{
int a;
}ster,*p,s2;
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memmove(arr2, arr1, 20);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
printf("\n");
my_memmove(arr1 + 6, arr1 + 4, 16);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
memset
功能: 将 ptr 所指向的内存块的前 num 个字节设置为指定的值。
注意:指定的值类型为无符号字符。
模拟实现:
void* my_memset(void* buf, int set, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
*((char*)buf + i) = set;
}
return buf;
}
int main()
{
int buf[] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
my_memset(buf,0, 5);
for (int i = 0; i < 10; i++)
{
printf("%d ", buf[i]);
}
return 0;
}
memcmp
功能:将 ptr1 所指向的内存块的前 num 个字节与 ptr2 所指向的前num个字节数进行比较,如果它们全部匹配,则返回零,或者返回一个不同于零的值,表示如果它们不匹配,则返回更大的数字字节。
注意:与 strcmp 不同,该函数在找到空字符后不会停止比较。
int my_memcmp(const void* str1, const void* str2, size_t count) {
assert(str1 && str2);
const char* pstr1 = (const char*)str1;
const char* pstr2 = (const char*)str2;
while (count--) {
if (*pstr1 && *pstr2 && (*pstr1 == *pstr2)) {
continue;
}
else {
break;
}
}
return (*pstr1 - *pstr2);
}
int main()
{
int a[] = { 1,2,3,4,5,6,7,8,9 };
int r = my_memcmp(a, a + 4, 8);
printf("%d\n", r);
return 0;
}