size_t strlen ( const char * str );
函数功能:
- 获取字符串的长度,即返回一个字符串到中到\0'字符之前的的字符个数
//int main()
//{
// char* arr = "abcdef";
// size_t len = strlen(arr);
// printf("%d\n", len);//6
//
// return 0;
//}
int main()
{
char* arr = "abc\0def";
size_t len = strlen(arr);
printf("%d\n", len);//3
return 0;
}
注意:strlen的返回值是size_t类型,是无符号的
int main()
{
char str1[] = "abcdef";
char str2[] = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
代码解读:
- 因为strlen的返回值是size_t类型的,两个size_t类型的值相减,结果仍然是size_t类型
3-6结果是-3,因为是size_t类型,会被编译器当成一个很大的正数,因此打印str2>str1
方法1:计数器
size_t my_strlen(const char* str)
{
assert(str);
char* p = str;
int count = 0;
while (*p != '\0')
{
count++;
p++;
}
return count;
}
方法2:递归
size_t my_strlen(const char* str)
{
assert(str);
char* p = str;
if (*p == '\0')
return 0;
return 1 + my_strlen(p + 1);
}
方法三:指针-指针
size_t my_strlen(const char* str)
{
assert(str);
char* p = str;
while (*p != '\0')
{
p++;
}
return (p - str);
}
char * strcpy ( char * destination, const char * source );
函数功能:
- 将source处的内容拷贝到destination指向的空间,包括'\0'
注意事项:
- source中的内容要有'\0'
- 会将'\0'一起拷贝
- destination指向的空间要足够大
char* my_strcpy(char* destination, const char* source)
{
assert(destination && source);
char* ret = destination;
while (*destination++ = *source++)
{
;
}
return ret;
}
char * strcat ( char * destination, const char * source );
函数功能:
- 在destination末尾追加字符串source
注意事项:
- 先找到destination字符串中的'\0',在'\0'处开始拷贝
- 会将source字符串中的'\0'一起拷贝
- destination字符串必须要有'\0';source字符串也必须要有'\0'
- destination的空间必须足够大
char* my_strcat(char* destination, const char* source)
{
assert(destination && source);
char* ret = destination;
//找到'\0'
while (*destination++)
{
;
}
//拷贝
while (*destination++ = *source++)
{
;
}
return ret;
}
问:能不能自己给自己追加?
答:我们自己写的模拟实现代码是不能追加给自己追加的,程序会陷入死循环;库函数中的strcat更加完善,能够完成自己追加自己,但还是不建议这样做
int strcmp ( const char * str1, const char * str2 );
函数功能:
- 比较两个字符串
注意事项:
- str1>str2,返回正数
- str1==str2,返回0
- str1<str2,返回负数
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdeg";
int ret = strcmp(arr1, arr2);
if (ret > 0)
printf("str1 > str2\n");
else if (ret == 0)
printf("str1 == str2\n");
else
printf("str1 < str2\n");
return 0;
}
//输出:str1 < str2
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
上面的strcpy,strcat都是长度不受限制的字符串函数,与它们相对应的是长度受限制的字符串函数
char * strncpy ( char * destination, const char * source, size_t num );
函数功能:
- 相较于strcpy,strncpy函数多了一个num参数,该参数表示要拷贝的字节个数
也就是我们可以指定拷贝的字符个数
注意事项:
- ?destination指向的空间必须足够大
- 如果source中的字符串个数小于num,则拷贝完source的字符串后自动补'\0'
- 如果source中的字符串个数大于num,则仅拷贝num个字符,不会加上'\0'
int main()
{
char arr1[20] = "xxxxxxxxx";
char arr2[] = "abcde";
strncpy(arr1, arr2, 3);
printf("%s\n", arr1);
return 0;
}
//输出:abcxxxxxx
char* my_strncpy(char* destination, const char* source, int num)
{
assert(destination && source);
char* ret = destination;
while (num--)
{
if (*source == '\0')
*destination++ = '\0';
else
*destination++ = *source++;
}
return ret;
}
char * strncat ( char * destination, const char * source, size_t num );
函数功能:
- 从source中追加num个字符到destination中
注意事项:
- destination的空间必须足够大
- 如果source字符串个数小于num,则相当于追加source字符串
- 如果source字符串个数大于num,则追加完num个字符后,自动补上'\0'
char* my_strncat(char* destination, const char* source, int num)
{
assert(destination && source);
char* ret = destination;
//找'\0'
while (*destination != '\0')
{
destination++;
}
while (num--)
{
if (num < 0 || *source == '\0')
break;
*destination++ = *source++;
}
*destination = '\0';
return ret;
}
int strncmp ( const char * str1, const char * str2, size_t num );
函数功能:
- 比较str1和str2中前num个字符构成的字符串的大小
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdef";
int ret = strncmp(arr1, arr2, 3);
if (ret > 0)
printf("str1>str2\n");
else if (ret == 0)
printf("str1==str2\n");
else
printf("str1<str2\n");
return 0;
}
//输出:str1 == str2
const char * strstr ( const char * str1, const char * str2 );?
函数功能:
- 在字符串str1中查找字符串str2
- 如果找到了,返回str2第一个字符在str1中的地址;如果找不到,返回NULL
int main()
{
char arr1[] = "abcdef";
char arr2[] = "cde";
char* ret = strstr(arr1, arr2);
if (ret)
printf("%s\n", ret);
else
printf("找不到\n");
return 0;
}
//输出:cdef
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
if (*str2 == '\0')
return str1;
int i = 0;//str1的下标
int j = 0;//str2的下标
while (str1[i])
{
while (str1 && str2 && str1[i] == str2[j])
{
i++;
j++;
}
if (str2[j] == '\0')
return str1 + i - j;
i = i - j + 1;
j = 0;
}
return NULL;
}
char * strtok ( char * str, const char * delimiters );
函数功能:
- 根据delimiters中的字符分割str字符串中的内容
int main()
{
char arr1[] = "baiyahua@qq.com";
char arr2[] = "@.";
char* ret = strtok(arr1, arr2);
printf("%s\n", ret);
return 0;
}
//输出:baiyahua
注意事项:
- delimiters中是自己定义的分隔符的集合
- str中包含delimiters中的分隔符
- 如果strtok的第一个参数不为空,会将str中的第一个分隔符替换成'\0',并记录该位置,返回起始位置的地址
- 如果strtok的第一个参数为空,从上次记录的位置开始寻找分隔符
- 如果没有找到分隔符,返回NULL
由于strtok会改变字符串的内容,所以我们一般先拷贝一份原来的字符串,对拷贝的字符串进行操作
int main()
{
char arr1[] = "baiyahua@qq.com";
char* p = "@.";
char copyArr1[50] = { 0 };
strcpy(copyArr1, arr1);
char* s = strtok(copyArr1, p);
while (s != NULL)
{
printf("%s\n", s);
s = strtok(NULL, p);
}
return 0;
}
//输出:
//baiyahua
//qq
//com
char * strerror ( int errnum );
函数功能:
- 翻译错误码对应的错误信息,并返回错误信息的起始地址
在C语言中,使用库函数发生错误时,会将错误码存放在一个叫errno的全局变量中;strerror的作用就是返回errnum中的值所对应的错误信息,然后返回错误信息的起始地址
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
printf("打开文件失败,原因是:%s\n", strerror(errno));
return 0;
}
else
{
printf("打开文件成功\n");
}
fclose(pf);
pf = NULL;
return 0;
}
//输出:打开文件失败,原因是: No such file or directory
void perror ( const char * str );
函数功能:
- 直接将错误码翻译成错误信息,并打印出来
perror相当于strerror+printf;在使用库函数出错时,它首先会打印str字符串,再将错误码对应的错误信息打印
int main()
{
FILE* pf = fopen("data.txt", "r");
if (pf == NULL)
{
perror("打开文件失败,原因是");
return 0;
}
else
{
printf("打开文件成功\n");
}
fclose(pf);
pf = NULL;
return 0;
}
//输出:打开文件失败,原因是: No such file or directory
strerror和perror
- 如果只想要错误码对应的错误信息,用strerror
- 如果还想打印出错误信息,用perror更方便
常见的字符分类函数:
函数 | 符合下面的条件返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符,空格' ',换页'\f',换行'\n',回车'\r',制表符'\t',垂直制表符'\v' |
isdigit | 数字0~9 |
isxdigit | 十六进制数字,0~9,a~f或A~F |
islower | 小写字母,a~z |
isupper | 大写字母,A~Z |
isalpha | 字母,a~z,A~Z |
isalnum | 字母或数字,0~9,a~z,A~Z |
ispunct | 标点符号,任何不属于数字或字母的可打印字符 |
isgraph | 任何图形字符 |
isprint | 任何可打印字符 |
我们就拿其中一个函数举例,其他函数同理
isdigit函数
函数功能:
- 判断c是否是一个数字字符,如果是数字返回非0;如果不是,返回0
int main()
{
int ch = '1';
if (isdigit(ch))
printf("是数字字符\n");
else
printf("不是数字字符\n");
return 0;
}
//输出:是数字字符
int toupper ( int c );
int tolower ( int c );
函数功能:
- toupper:如果c是小写字母,则返回c的大写字母;否则就直接返回c
- tolower:如果c是大写字母,则返回c的小写字母;否则就直接返回c
int main()
{
char arr[] = "Test String.\n";
char* p = arr;
while (*p)
{
if (isupper(*p))
*p = tolower(*p);
p++;
}
printf("%s\n", arr);
return 0;
}
//输出:test string.
void * memcpy ( void * destination, const void * source, size_t num );
函数功能:
- 从source指向的内存拷贝num个字节到destination中,返回destination首元素的地址
前面的strcpy是只能对字符串进行拷贝的函数,而memcpy能对任意类型的数据进行拷贝
int main()
{
int arr1[20] = { 0 };
int arr2[] = { 1,2,3,4,5,6,7 };
memcpy(arr1, arr2, 20);
for (int i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
return 0;
}
//输出1 2 3 4 5
void* my_memcpy(void* destination, const void* source, size_t num)
{
assert(destination && source);
void* ret = destination;
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
return destination;
}
如果我们想要对重叠的内存进行拷贝,my_memcpy是完成不了的;库函数中的memcpy虽然可以进行重叠内存的拷贝,但我们规定好:
- 两段不重叠的内存拷贝用memcpy
- 两段有重叠的内存拷贝用memmove
void * memmove ( void * destination, const void * source, size_t num );
函数功能:
- 从source指向的内存拷贝num个字节到destination中,返回destination首元素的地址
同样是拷贝内存,用法也与memcpy一样,只不过对于重叠内存的拷贝我们一般选择memmove
void* my_memmove(void* destination, const void* source, size_t num)
{
assert(destination && source);
void* ret = destination;
if (source < destination)
{
//从后往前拷贝
while (num--)
{
*((char*)destination + num) = *((char*)source + num);
}
}
else
{
//从前往后拷贝
while (num--)
{
*(char*)destination = *(char*)source;
destination = (char*)destination + 1;
source = (char*)source + 1;
}
}
return ret;
}
void * memset ( void * ptr, int value, size_t num );
函数功能:
- 设置ptr指向内存的内容改为value;以字节为单位,一共该num个字节
int main()
{
char arr[] = "Welcome to my world.\n";
memset(arr + 14, 'x', 3);
printf("%s", arr);
return 0;
}
//输出:Welcome to my xxxld.
注意事项:
- 该函数是以字节为单位进行修改的,每次只会修改一个字节
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
函数功能:
- 以字节为单位比较两块内存
- ptr1>ptr2,返回正数;
- ptr1==ptr2,返回0
- ptr1<ptr2,返回负数
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8 };
int arr2[] = { 1,2,3,4,0x11111105 };
int ret = memcmp(arr1, arr2, 17);
printf("%d\n", ret);
return 0;
}
//输出0,前16字节都是相等
//arr1和arr2的第十七字节数据都是05,所以相等
字符函数和字符串函数的内容就到这!