编译环境:vs2017
编译的时候注意包含头文件。
在学习字符串函数之前,先看看size_t和int的区别吧:
- size_t是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int。size_t在32位架构上是4字节,在64位架构上是8字节。
- int是一个数据类型,应为signed int,在32位和64位上都是4个字节。
在我的上一篇博客里边已经讲了 strlen() 和 sizeof 的区别,那么在这里稍加概括:
- strlen()是函数,参数必须是字符型指针(char*), 且以’\0’结尾,返回的长度大小不包括’\0’。
- sizeof是运算符,返回对象的字节大小,包括’\0’。
- 辨析:
char str[] = "hftr\0fv\\0fgy\\\0mfg";
printf("%d\n", strlen(str)); // 4
printf("%d\n", sizeof(str)); // 18
再介绍一个会用到的断言函数,assert() 函数:
- 头文件是 #include <assert.h>
- 如果它的条件返回错误,则终止程序执行
- 原型是:void assert( int expression )
字符串函数
1、先来看一个最简单的字符串函数:strlen() (长度 --> 重要)
- strlen() 所做的仅仅是一个计数器的工作
- 碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)
- 原型: size_t strlen ( const char * string ),返回值千万不能搞错!!!!!!
模拟实现strlen():
size_t my_strlen(const char *string)
{
assert(string);
size_t count = 0;
while (*string)
{
++count;
++string;
}
return count;
}
int main()
{
char string[] = "abcdef\0abcd";
printf("%u\n", my_strlen(string)); // 6
system("pause");
return 0;
}
2、常用字符串函数之一:strcpy() (拷贝 --> 重要)
- strcpy() 把一个字符串拷贝(覆盖)到另一个字符串, '\0’也会拷进去。
- 目标字符串要能装下源字符串
注意: - 如果源字符串和目标字符串重叠,strcpy 行为不确定。
- 由于 strcpy 不检查在 strDestination中是否有足够的空间,所以可能会产生缓冲区溢出的问题。
解决:strcpy之前判断一下。长了就抛弃! - 原型: char* strcpy (char* destination, const char* source),注意返回值!!!!!!
模拟实现strcpy():
// 模拟实现strcpy
char* my_strcpy(char* des, const char* src)
{
assert(des&&src);
char* ret = des;
while (*des = *src)
{
des++;
src++;
}
return ret;
}
int main()
{
char des[100];
char src[100];
printf("请输入源字符串:");
scanf("%s", src);
printf("请输入目标字符串:");
scanf("%s", des);
printf("更改后的目标字符串:%s\n", my_strcpy(des, src));
system("pause");
return 0;
}
3、常用字符串函数之一:strstr() (查找子串 --> 重要)
- strstr() 返回要查找的字符串在目标字符串中的位置
- 比如:在"123abcd12345de6789123efg"中查找"12345de",返回的是"12345de6789123efg"
- 原型: char* my_strstr(const char* string, const char* strCharSet),注意返回值!!!!!!
模拟实现strstr():
//模拟实现strstr(判断子串)
char* my_strstr(const char* string, const char* strCharSet)
{
assert(string&&strCharSet);
while (*strCharSet)
{
const char* p = string;
const char* q = strCharSet;
while (*p == *q && *q)
{
++p;
++q;
}
if (*q == '\0')
return string;
++string;
if (*string == '\0')
{
return NULL;
}
}
}
int main()
{
char string[100];
char strCharSet[100];
printf("请输入目标字符串:");
scanf("%s", string);
printf("请输入要查找的字符串:");
scanf("%s", strCharSet);
if (my_strstr(string, strCharSet) == NULL)
{
printf("没有找到!\n");
}
else printf("要查找的字符串在目标字符串的位置是:%s\n", my_strstr(string, strCharSet));
system("pause");
return 0;
}
4、常用字符串函数之一:strcmp() (比较)
-
strcmp() 比较两个字符串str1和str2,
当str1>str2时,函数返回一个大于0的数
当str1=str2时,函数返回 0
当str1<str2时,函数返回一个小于0的数 -
原型: int strcmp(const char* des, const char* src),注意返回值!!!!!!
-
下面的一定要注意了!!!!!!
如果一个字符值的ASCII码值超过127,该如何比较?也就是说,这类字符是非常规、不常用的字符。如字符¥的ASCII码值在850 (Latin 1)为190,将其和'a'比较,得到的结果可能会和你想的不一样。超过127的字符在处理时应该统一将其转换为对应的无符号型。
这就是代码中会出现 ** (unsigned char * )** 的原因。 模拟实现strcmp():
//模拟实现strcmp(比较)
int my_strcmp(const char* str1, const char* str2)
{
assert(str1&&str2);
while (*str1&&*str2)
{
if (*(unsigned char*)str1 > *(unsigned char*)str2)
{
return 1;
}
else if (*(unsigned char*)str1 < *(unsigned char*)str2)
{
return -1;
}
else
{
++str1;
++str2;
}
}
if (*str1 != '\0')
{
return 1;
}
else if (*str2 != '\0')
{
return -1;
}
else return 0;
}
int main()
{
char str1[100];
char str2[100];
printf("请输入第一个字符串:");
scanf("%s", str1);
printf("请输入第二个字符串:");
scanf("%s", str2);
printf("比较结果:%d\n", my_strcmp(str1, str2));
system("pause");
return 0;
}
5、常用字符串函数之一:strcat() (拼接)
- strcat() 把一个字符串拼接到另一个字符串上,拼接到目标字符串后,覆盖掉’\0’
- 目标字符串必须足够大
- 原型: char* strcat (char* destination, const char* source),注意返回值!!!!!!
模拟实现strcat():
// 模拟实现strcat(拼接)
char* my_strcat(char* des, const char* src)
{
assert(des&&src);
char* ret = des;
while (*des)
++des;
while (*src)
{
*des++ = *src++;
}
*des = '\0';
return ret;
}
int main()
{
char des[100];
char src[10];
printf("请输入源字符串:");
scanf("%s", src);
printf("请输入目标字符串:");
scanf("%s", des);
printf("目标字符串修改为:%s\n", my_strcat(des, src));
system("pause");
return 0;
}
6、常用字符串函数之一:strchr() (字符串中查找字符)
- strchr() 查找字符串s中首次出现字符c的位置。
- 原型: char* my_strchr(const char* string, int c),注意返回值!!!!!!
模拟实现strchr():
// 模拟实现strchr(串中找字符)
char* my_strchr(const char* string, int c)
{
assert(string);
while (*string)
{
if (*string == c)
{
return string;
}
++string;
}
return NULL;
}
int main()
{
char string[100];
int c;
printf("请输入一个字符串:");
scanf("%s", string);
c = 'b';
if (my_strchr(string, c) == NULL)
{
printf("没有找到!\n");
}
else printf("%c的位置是:%s\n", c,my_strchr(string, c));
system("pause");
return 0;
}
7、常用字符串函数之一:strncpy() (拷贝字符串片段)
- strncpy() 把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中, 如果src不够n个,则追加’\0’,直至num个, 并返回被复制后的dest,复制后的dest打印时遇到第一个’\0’就停止。
- 只是将src的前n个字符复制到dest的前n个字符,后面不自动添加’\0’,也就是结果dest中的src后面不包括’\0’,需要再手动添加一个’\0’。
- 原型: char* my_strncpy(char* dest, const char* src, size_t num),注意返回值!!!!!!
模拟实现strncpy():
// 模拟实现strncpy
char* my_strncpy(char* dest, const char* src, size_t num)
{
assert(dest&&src);
char* ret = dest;
while (num)
{
--num;
*dest++ = *src++;
}
if (num > 0) // src的个数不够需要拷贝的字符个数,后面追加'\0'
{
while (num--)
{
*dest++ = '\0';
}
}
return ret;
}
int main()
{
char des[50] = { NULL };
char src[50] = { NULL };
size_t num = 0;
printf("请输入目标字符串:");
scanf("%s", des);
printf("请输入源字符串:");
scanf("%s", src);
printf("请输入拷贝个数:");
scanf("%u", &num);
my_strncpy(des, src, num * sizeof(char));
printf("%s\n", des);
system("pause");
return 0;
}
8、常用字符串函数之一:strncat() (拼接字符串片段)
- strncat() 把src中n个元素拼接到dest的’\0’之前,src拼接到dest完成后,在dest后追加一个’\0’,dest后面还有它自带的’\0’。
- 原型: char* my_strncat(char* des, const char* src, size_t num),注意返回值!!!!!!
模拟实现strncat():
char* my_strncat(char* dest, const char* src, size_t num)
{
assert(dest&&src);
char* ret = dest;
while (*dest)
{
++dest;
}
while (num-- && *src)
{
*dest++ = *src++;
}
*dest = '\0';
return ret;
}
int main()
{
char des[50] = { NULL };
char src[50] = { NULL };
size_t num = 0;
printf("请输入目标字符串:");
scanf("%s", des);
printf("请输入源字符串:");
scanf("%s", src);
printf("请输入拼接个数:");
scanf("%u", &num);
printf("拼接后,目标字符串为:%s\n", my_strncat(des, src, num));
system("pause");
return 0;
}
9、常用字符串函数之一:strncmp() (比较字符串片段)
- strncmp() 比较两个长度为n的字符串str1和str2,
当str1>str2时,函数返回一个大于0的数
当str1=str2时,函数返回 0
当str1<str2时,函数返回一个小于0的数 - 原型: int my_strncmp(const char* str1, const char* str2, size_t num),注意返回值!!!!!!
- 还有一点,请借鉴上述strcmp()函数。
模拟实现strncmp():
// 模拟实现strncmp()
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1&&str2);
while (num && (*str1) && (*str2))
{
if (*(unsigned char*)str1 > *(unsigned char*)str2)
{
return 1;
}
else if (*(unsigned char*)str1 < *(unsigned char*)str2)
{
return -1;
}
else
{
++str1;
++str2;
--num;
}
}
if (num == 0)
{
return 0;
}
else
{
if ((*str1 == '\0') && (*str2 != '\0'))
{
return -1;
}
else if ((*str1 != '\0') && (*str2 == '\0'))
{
return 1;
}
else return 0;
}
}
int main()
{
char str1[50] = { NULL };
char str2[50] = { NULL };
size_t num = 0;
printf("请输入第一个字符串:");
scanf("%s", str1);
printf("请输入第二个字符串:");
scanf("%s", str2);
printf("请输入比较个数:");
scanf("%u", &num);
printf("比较结果是:%d\n", my_strncmp(str1, str2, num));
system("pause");
return 0;
}
库函数
memcpy() VS memmove()
两者都是从原数组中拷贝一定长度的元素到目标数组中。
内存重叠问题:
1、memcpy() (任意类型拷贝 --> 重要)
- 发生内存重叠时,不保证正确性。
- 从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。
- 原型: void* memcpy(void* destin, void* source, unsigned n)
- 头文件:<string.h>
模拟实现memcpy():
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
void* my_memcpy(void* dest, const void* src, size_t sz)
{
assert(dest&&src);
char* dest_ = (char*)dest;
char* src_ = (const char*)src;
for (size_t i = 0; i < sz; ++i)
{
dest_[i] = src_[i];
}
return dest;
}
int main()
{
int dest[10] = { 0,1,2,3,4,5 };
int src[] = { 8,8,8 };
my_memcpy(dest, src, sizeof(src));
for (int i = 0; i < 10; ++i)
{
printf("%d ", dest[i]);
}
printf("\n");
system("pause");
return 0;
}
2、memmove() (拷贝–> 重要)
- 发生内存重叠时,保证正确性。
- 由src所指内存区域复制count个字节到dest所指内存区域。
- 原型: void* memmove(void* dest, const void* src, size_t count)
- 头文件:<string.h>
模拟实现memmove():
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
void* my_memmove(void* dest, const void* src, size_t size)
{
assert(dest&&src);
char* dest_ = (char*)dest;
const char* src_ = (const char*)src;
if ((dest_ > src_)&&(dest_ < src_ + size))
{
for (size_t i = size - 1; i >= 0; --i)//从后往前拷,存在内存重叠问题
{
dest_[i] = src_[i];
}
}
else
{
for (size_t j = 0; j < size; ++j)//从前往后拷,不存在内存重叠问题
{
dest_[j] = src_[j];
}
}
return dest;
}
int main()
{
int dest[10] = { 0,1,2,3,4,5,6,7,8,9 };
//int src[] = { 9,9 }; // 1.把src中的所有拷贝到dest中(两个数组,不存在内存重叠问题)
//my_memmove(dest, src, sizeof(src));
my_memmove(dest, dest+3, 2*sizeof(int)); // 2.把dest中的3和4拷贝到dest中(一个数组,存在内存重叠问题)
// dest+3 : 从下标为(0+3)处开始拷贝
// 2*sizeof(int) : 拷贝2*4个字节,就是两个int元素 3和4
for (size_t i = 0; i < 10; ++i)
{
printf("%d ", dest[i]);
}
printf("\n");
system("pause");
return 0;
}