文章目录
1. C语言字符串函数
1. strlen
size_t strlen(const char* str)
我们知道字符串是以’\0’作为结束符的,而strlen计算的字符串是从起始的地址指向的字符串到
‘\0’ 之前的字符个数,返回值是size_t类型(注意:是无符号的)
如果我们用数组来存放字符串或者是字符串常量,用strlen都是没有问题的。
const char* str1 = "abcdef";
char arr1[] = {"hello word!"}
但有一点要注意:如果数组中存放单个字符时,末尾要有’\0’。否则没有字符结束符,strlen会一直向数组后面的空间一直寻找 ‘\0’ ,但这是未知的。例如:
char arr2[] = { 'a','b','c' };
int main()
{
const char* str1 = "abcdef";
char arr1[] = { "hello word!" };
char arr2[] = { 'a','b','c' };
size_t ret1 = strlen(str1);
size_t ret2 = strlen(arr1);
size_t ret3 = strlen(arr2);
printf("%zd\n", ret1);
printf("%zd\n", ret2);
printf("%zd\n", ret3);
return 0;
}
从中我们可以看出第三种确实没有打印出我们想要的 3,而是37,这是随机的,我们不妨验证一下,输出arr2起始位置到’\0’是不是37个字符值。
乱码是因为后面的数组中的某些元素不属于ASCII字符集,或者编码方式与程序中指定的格式不匹配,例如无效的编码或未初始化的内存。
以下是三种模拟实现strlen函数的方法:
1. 计数器方式
int my_strlen(const char * str)
{
int count = 0;
assert(str);//如果地址为NULL,报错
while(*str)
{
count++;
str++;
}
return count;
}
2. 不借助临时变量(递归)
int my_strlen(const char * str)
{
assert(str);//如果地址为NULL,报错
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
3. 指针 - 指针
int my_strlen(char *s)
{
assert(str);
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
2. strcpy
char* strcpy(char * destination, const char * source );
destination是source指向的字符串要复制的目标地址。
这里有几点要注意:
- 源字符串必须以 ’\0’ 结束。(不然再次访问字符串依然会出现访问越界的问题)
- 目标空间必须⾜够大,以确保能存放源字符串。
- 目标空间必须可修改。
下面是对strcpy 的模拟实现:
char* MyStrcpy(char* dest, const char* src)
{
int i = 0;
while (src[i]!= '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
return dest;
}
1. strncpy
相比于strcpy函数,接下来我要介绍可以限制字符串拷贝长度的函数strncpy
char* strncpy(char* dest, const char* src ,szie_t n)
这个函数可以让我们将源字符串按照我们想要的长度拷贝到目标字符串中。
接下来是对其的使用:
首先我们要清除strncpy要拷贝的空间需要有足够的空间。否则会出现访问越界:
//同时要做到目标字符串有 ‘\0’,保证拷贝后访问目标字符串正常。
接下来让我们对其进行模拟实现:
在这里插入代码片
3. strcat
char * strncat ( char * destination, const char * source);
其作用是将源字符串的字符追加在目标字符串的后面(这里同样要求目标空间可以容纳源字符串的长度),最后在追加后的结尾添加 ‘\0’ 字符。
1. strncat
char * strncat ( char * destination, const char * source, size_t num );
char * strncat ( char * destination, const char * source, size_t num );
这是在strcat的基础上限制了追加的字符个数(将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 \0 字符)。
4. strcmp
int strncmp ( const char * str1, const char * str2);
这个函数是将str1和str2一个一个作比较,如果str1字符大于str2,返回一个正数(比较的是字符的ASCII值),小于则返回负数,如一直比较到两个字符的结束符 \0,如果有一个字符先到达结束符,改字符对应的ASCII码值为0。
1. strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
这个函数是只比较两个字符串的前 num 个字符,计算的返回值同上。
5. strstr
用来寻找字串的函数(函数返回字符串str2在字符串str1中第⼀次出现的位置),但字符串的⽐较匹配不包含 \0 字符,以 \0 作为结束标志。
char * strstr ( const char * str1, const char * str2);
本质上是对目标字符串的暴力寻找(一次次地遍历),若遍历到结束符都没有找到则返回空指针LNULL。
char * strstr (const char * str1, const char * str2)
{
char *cp = (char *) str1;
char *s1, *s2;//便于指针回溯
if ( !*str2 )
return((char *)str1);
while (*cp)
{
s1 = cp;
s2 = (char *) str2;
while ( *s1 && *s2 && !(*s1-*s2) )//两字符相等写没有为0
s1++, s2++;
if (!*s2)//待找字串已经找到
return(cp);
cp++;
}
return(NULL);//没有找到
}
这里我们还有另外一个算法叫KMP算法,他同样可以寻找字串,但不同的是他可以用失败的信息减少回溯的长度,会更高效地匹配字符串,这里我们不作过多解释。
6. strtok
char * strtok ( char * str, const char * sep);
这里是形参的解读:
- sep参数指向⼀个字符串,定义了⽤作分隔符的字符集合
- 第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标记。
- strtok函数找到str中的下⼀个标记,并将其⽤ \0 结尾,返回⼀个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。)
• strtok函数的第⼀个参数不为NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置。
• strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
• 如果字符串中不存在更多的标记,则返回 NULL 指针。
2. C语言内存函数
2.1 memset
void * memset ( void * ptr, int value, size_t num );
memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。通常用来初始化内存为想要的值。
2.2 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
• 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
• 这个函数在遇到 ‘\0’ 的时候并不会停下来。
• 如果source和destination有任何的重叠,复制的结果都是未定义的。(所以memcpy并不适合自身内存重叠时的拷贝)
int c[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int d[10] = { 10,9,8,7,6,5,4,3,2,1 };
My_memcpy(c+1, c, 20);//重叠
for (int i = 0; i < 10; i++) {
printf("%d ", c[i]);
}
我们并没有成功将c指向的5个整形拷贝给c+1的位置,是因为在拷贝重叠位置的整形时,这个位置的内存已经被覆盖了,如下标0的字符拷贝到下标为1的位置后,这时再将下标为1的信息拷贝到下标为2的位置时就不能如愿了,因为此时下标为1的信息已经被改为1了,后面的情况一样。
2.3 memmove
memmove与memcpy很相似,但唯一不同的是它可以用在自身重叠的情况下拷贝内存。
void * memmove ( void * destination, const void * source, size_t num );
下面是对其模拟实现:
void* My_memmove(void* dest, const void* src, size_t n)
{
assert(dest && src && n > 0);
void* ret = dest;
//后 -》前
if (src < dest)
{
while (n--)
{
*((char* )dest + n) = *((char*)src + n);
}
}
//前 -》后
else
{
while (n--)
{
*((char*)dest) = *((char*)src);
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}
2.4 memcmp
内存比较函数
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
以下是对函数的应用:
#include <stdio.h>
#include <string.h>
int main()
{
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
int n;
n = memcmp(buffer1, buffer2, sizeof(buffer1));
if (n > 0)
printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
else if (n < 0)
printf("'%s' is less than '%s'.\n", buffer1, buffer2);
else
printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
return 0;
}
我们知道g的ASCII码值要比G大32,所以返回值大于0。