字符函数和字符串函数

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常存入在常量字符串中或者字符数组

字符串常量适用于那些对它不做修改的字符串函数。

1.1 字符串长度

库函数 strlen 函数原型如下:

size_t strlen ( const char * str );

返回 str 所指向字符串的长度

重点:

  • 字符串以'\0'作为结束标志,strlen 函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')。

  • 参数指向的字符串必须要以'\0'结束。

  • 注意函数的返回值为size_t,是无符号的(易错)。

参数以’\0’结尾情况

在这里插入图片描述

参数没有以’\0’结尾情况

strlen 函数一定会返回一个值,只有在找到 '\0' 时才会停止。

image-20220326165831555

函数返回值

int main()
{
	char* str1 = "abcdef"; // 6
	char* str2 = "bbb";    // 3
	
	if (strlen(str2) - strlen(str1) > 0) // 输出什么???
		printf("str2 > str1\n");
	else
		printf("str1 >= str2\n");

	return 0;
}

我想这一题很多人第一次做基本上都会错,你看,str1 的长度是 6str2 的长度是 3, str2 - str1 的结果一定是小于 0 的啊,那么输出的结果就一定是 str1 >= str2 了。 好那让我们运行一下程序看看结果是否和我们想的一样呢?

image-20220326205847195

嗯嗯嗯…这是怎么一回事呢?不是应该输出 str1 > =str2 吗? 怎么会输出 str2 > str1 呢???

注意一下我们上面所说的第三点:

注意函数的返回值为size_t,是无符号的(易错)

strlen 函数的返回值是个无符号数,所以 > 符号左边的表达式也将是无符号数,而无符号数绝对不可以是负的

如果要为了避免这个问题我们可以将上述语句改为以下两种

直接对它们的返回值进行判断。

if (strlen(str2) > strlen(str1))

或者利用强制类型转换,将无符号数转换为有符号数。

if ((int)strlen(str2) - (int)strlen(str1) > 0)

模拟实现

由于库函数中已经存在了 strlen 函数,我们无法使用同一个名字来自定义函数,即使不包含 strlen 所在的头文件也不行。

在指针前面加上 const,是为了防止我们在函数内部一不小心修改了指针所指向的值。相当于增加了一个只读权限,我们只有访问这块空间的权限,没有修改的权限。增加了程序的健壮性。

方式一:计数器方式

定义一个计数器,当指针指向的字符不是’\0’时,计数器 + 1,同时指针指向下一个字符,直到找到'\0'为至,返回count。

size_t my_strlen(const char* str)
{
	assert(str); // 断言 保证传入指针的有效性
	size_t count = 0; // 计数器
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

动画

方式二:递归的方式

如果当前指针指向的字符不是'\0',继续递归调用,直到找到'\0',开始返回

size_t my_strlen(const char* str)
{
	assert(str);// 断言 保证传入指针的有效性
	if (*str != '\0')
		return 1 + my_strlen(str + 1); // 当前字符不为 '\0',继续递归调用
    else
		return 0; // 找到了'\0',开始返回
}

image-20220326174537865

方式三:指针 - 指针的方式

定义一个指针,让指针一直往后走,直到找到'\0',在同一个数组中两个指针之差,即为两指针之间的元素个数。

image-20220403002504652

size_t my_strlen(const char* str)
{
    assert(str); // 断言 保证传入指针的有效性
	const char* end = str; // 用于找到 '\0'
	while (*end != '\0')
	{
		end++;
	}
	return end - str; // 元素个数
}

1.2 不受限制的字符串函数

1.2.1 字符串拷贝

库函数 strcpy 函数原型如下:

char * strcpy ( char * destination, const char * source );

这里我们把 source 称为源字符串,把 destination 称为目标数组。

将源指向的字符串,拷贝到目标数组,包括末尾的'\0'。函数返回指向目标数组指针。

重点:

  • 源字符串必须以'\0'结尾。

  • 目的地空间要大于源字符串。

  • 目的地必须可修改。

int main()
{
    char arr[10] = {0};    
    strcpy(arr, "hello");  // 将 "hello" 拷贝到数组arr中
    
    strcpy(arr, "hello world"); // error, 要拷贝的字符个数为12(包括'\0'),而我们arr数组无法容纳     
    // strcpy 函数不会在意这些,它只有将'\0'拷贝到指定地点才会停下来,即便超出了最大容量,也不会停止拷贝
    
    // 同时又引出了一个问题,如果源字符串中没有'\0',strcpy函数才不管三七二十一呢
    // 它会继续访问源字符串的后面的内存空间,将其拷贝到目地的,直到在内存空间中
    // 找到 '\0',并将其拷贝到目地的时才会停止,程序发生了越界访问,可能会崩溃
    
    // 目地的不能是常量字符串
    // 如:直接放一个字符串 "abcdef"
    // 或者用指针指向的常量字符串: char* p = "abcdef";
    
    return 0;
}

模拟实现

char * my_strcpy ( char * dest, const char * src )
{
    assert(dest && src);
    char* ret = dest;
    while (*dest++ = *src++) // 拷贝 直到将源字符串中'\0'拷贝到目标数组中
    {
        ;
    }
    return ret;
}

1.2.2 字符串连接

库函数 strcat 函数原型如下:

char * strcat ( char * destination, const char * source );

将源字符串添加到目地的数组的未尾(从目地的数组的'\0'位置开始拷贝),函数返回指向目标数组指针。

重点:

  • 源字符串必须以'\0'结束。

  • 目标空间必须有足够在的空间,能容纳下源字符串的内容

  • 目标空间必须可修改。

int main()
{
    char arr1[30] = "test";
    char arr2[] = " strcat";
    
    strcat(arr1, arr2);
    printf("%s\n", arr1);
    
    // 我们的strcat函数也是和strcpy函数一个脾气,只有将源字符串中的'\0',拷贝到目标字符串时,才会停止
    // 不一样的一点是,我们的目标字符串必须要以'\0'结尾,如果目标字符串没有以'\0'结尾,strcat函数才不
    // 管这么多,就一直往后找,直到找到'\0'才开始拷贝操作,同样可能发生越界访问,程序崩溃
        
    // 目地的必须可修改 同strcpy
    
    return 0;
}

模拟实现

char * my_strcat ( char * dest, const char * src )
{
    assert(dest && src);
    char* ret = dest;
    // 找目标数组 ‘\0’
    while (*dest != '\0')
    {
        dest++;
    }
    // 目标数组拷贝,直到将源字符串'\0',拷贝到目标数组
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}

1.2.3 字符串比较

库函数 strcmp 函数原型如下:

int strcmp ( const char * str1, const char * str2 );

按字典顺序(ASCII),比较两个字符串是否相等

C语言标准规定:

  • 第一个字符串大于第二个字符串,则返回大于0的数字。
  • 第一个字符串等于第二个字符串,则返回0。
  • 第一个字符串小于第二个字符串,则返回小于0的数字。
int main()
{
    if (strcmp("abc", "abc") == 0) // 两个字符串相等返回0
        printf("相等\n");
    
    if (strcmp("abc", "abd") < 0) // 前两个字符相等,第三个字符 'c' < 'd', 返回一个小于0的数
        printf("小于\n");
    
    if (strcmp("abd", "abc") > 0) // 前两个字符相等,第三个字符 ‘d' > 'c', 返回一个大于0的数
        printf("大于\n");
    
    return 0;
}

模拟实现

int my_strcmp ( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    while (*str1 == *str2)
    {
        // 两字符串相等
        if (*str1 == '\0') // 如果条件成立,说明str1 == str2,且已走到字符串末尾,两字符串相等
        {
            return 0;
        }
        str1++;
        str2++:
    }
    // vs 实现方式
    if (*str1 > *str2)
        return 1;
    else
        return -1;
    //return (*str1 - *str2); // gcc 实现方式
}

1.3 长度受限的字符串函数

1.3.1 strncpy

库函数 strncpy 函数原型如下:

char * strncpy ( char * destination, const char * source, size_t num );

和 strcpy 函数一样,strncpy 函数把源字符串的字符复制到目标数组,向目标数组写入 num 个字符,函数返回指向目标数组指针。

  • 如果源字符串的长度小于 num, 那么就会用'\0',填充到 num个为止。

  • 如果源字符串的长度大于等于num,那么将num个字符拷贝到目标数组中,它的结果使得目标数组没有以'\0'结尾。

image-20220402165543939

模拟实现

char * my_strncpy ( char * dest, const char * src, size_t num )
{
    assert(dest && src);
    char* ret = dest;
    while (num-- && (*dest++ = *src++)) // 拷贝
    {
        ;
    }
    while (num--) // 多余的用'\0'填充
    {
        *dest++ = '\0';
    }
    return ret;    
}

1.3.2 strncat

库函数 strncat 函数原型如下:

char * strncat ( char * destination, const char * source, size_t num );

strcat函数一样,它从 source 中最多复制 num 个字符到目标数组的后面。但是strncat总是会使目标数组以'\0'结尾 。函数返回指向目标数组指针。

  • 如果源字符串长度小于 num, 将源字符串'\0'拷贝到目标数组结束。
    -image-20220402214655925

  • 如果源字符串长度大于等于num,拷贝num个字符,并最终在末尾添加一个'\0'

image-20220402213623967

模拟实现

char * my_strncat ( char * dest, const char * src, size_t num )
{
    asssert(dest && src);
    char* ret = dest;
    while (*dest) // 找目标数组'\0'
    {
        dest++;
    }
    while (num-- && (*dest = *src)) // 开始拷贝
    {
        dest++;
        src++;
    }
    *dest = '\0'; // 向末尾添加'\0'
    return ret;
}

1.3.3 strncmp

库函数 strncmp 函数原型如下:

int strncmp ( const char * str1, const char * str2, size_t num );

功能同strcmp,增加了第三个参数 num ,比较指定个数个字符。

  • 第一个字符串大于第二个字符串,则返回大于0的数字。
  • 第一个字符串等于第二个字符串,则返回0。
  • 第一个字符串小于第二个字符串,则返回小于0的数字。
int main()
{
    if (strncmp("abc", "abc", 2) == 0) // 比较前两个字符,前两个字符相同,返回0
        printf("等于\n");
    
    if (strncmp("abc", "abd", 3) < 0) // 比较前三个字符,第三个字符'c' < 'd',返回小于0的数
        printf("小于\n");
    
    return 0;
}

模拟实现

int my_strncmp ( const char * str1, const char * str2, size_t num )
{
    assert(str1 && str2);
    while (num && (*str1 == *str2))
    {
        str1++:
        str2++:
        num--;
    }
    
    if (num == 0)
    {
        return 0; // 相等
    }        
    
    return (*str1 - *str2);   
}

1.4 字符串查找

1.4.1 查找一个字符

在一个字符串中查找一个特定字符可以使用函数 strchrstrrchr,它们的函数原型如下:

char *strchr( const char *str, int ch );
char *strrchr( const char *str, int ch );

strchr 在字符串 str 中查找字符 ch第一次出现的位置,找到后返回指向该位置的指针。如果该字符并不存在于字符串中,函数就返回一个 NULL 指针。strrchr 的功能和 strchr基本一致,只是它返回的是一个指向字符串中该字符最后一次出现的位置(最右边那个)。

#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "abcdefca";
    int ch = 'c';
    
    // 如果只想显示单个字符可以使用 %c 格式打印
    printf("%s\n", strchr(str, ch));  // cdefca
    printf("%s\n", strrchr(str, ch)); // ca
    
    return 0;   
}

模拟实现

char* my_strchr(const char* str, int ch)
{
    assert(str);
    while (*str != '\0' && *str != ch)
    {
        str++;
    }
    if (*str == ch) 
        return (char*)str; 
    else
        return NULL;
}
char* my_strrchr(const char* str, int ch)
{
    assert(str);
    const char* ret = NULL;
    while (*str != '\0')
    {
        if (*str == ch)
            ret = str; // 记住上一次查找到的位置
        str++;
    }
    return (char*)ret; 
}

1.4.2 查找任何几个字符

库函数 strpbrk 函数原型如下:

char * strpbrk ( const char * str1, const char * str2 );

strpbrk并不是查找某个特定的字符,而是查找任何一组字符第一次在字符串中出现的位置,这个函数返回一个指向 str1 中第一次匹配

str2 中任意一个字符的字符位置。如果未找到,返回NULL指针。

#include <stdio.h>
#include <string.h>

int main()
{
    char str1[] = "This is for test strpbrk";
    char str2[] = "aeiou";
    char* pch = NULL;
    printf("Vowel in '%s': ", str1);
    pch = strpbrk(str1, str2);
    while (pch != NULL)
    {
        printf("%c ", *pch);
        pch = strpbrk(pch + 1, str2);
    }
    printf("\n");
    return 0;
}

模拟实现

char* my_strpbrk ( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    while (*str1)
    {
        const char* s1 = str2;
        while (*s1)
        {
            if (*s1++ == *str1) // 与str2中每一个字符进行比较,看是否相同
                return (char*)str1;
        }
        str1++;
    }    
    return NULL;
}

如果我们想查找最后一个匹配的字符,因为库函数中没有strrpbrk函数,我们也可以自己实现一个

char* my_strrpbrk ( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    char* ret = NULL;
    char* pos = strpbrk(str1, str2); // 为了简便直接调用库函数
    while (pos != NULL)
    {
        ret = pos;
        pos = strpbrk(pos + 1, str2);
    }
    return ret;
}

1.4.3 查找子串

库函数 strstr 函数原型如下:

char * strstr ( const char * str1, const char * str2 );

str1 中查找子串 str2,找到了返回指向第一次匹配成功位置的指针,找不到返回NULL。如果str2为空字符串,直接返回字符串str1的起始位置。

int main()
{
    char arr1[] = "abdabc";
    char arr2[] = "abc";
    char* p = strstr(arr1, arr2); // 在arr1中查找arr2,第一次出现的位置
    if (p != NULL)
        printf("%s\n", p);
    else
        printf("未找到\n");
    return 0;	
}

模拟实现

以下两个写法均为BF算法(暴力破解),我们也可以使用更优的算法KMP。我们将在下一篇文章来介绍KMP算法。

定义三个指针变量

cur 指向当前开始匹配的位置。

s1从cur所指向的位置开始向后匹配。

s2从str2所指向的位置开始向后匹配。

image-20220402233231386[外链图片转存中...(img-gEl9nGdi-1648920643891)]

char * my_strstr ( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    const char* s1 = str1;
    const char* s2 = str2;
    const char* cur = s1;
    
    if (!*str2)
        return (char*)str1; // 当str2为空直接返回str1
    
    while (*cur) // 如果cur指向'\0',说明查找不到子串
    {
        s1 = cur;  // 从cur所指向的位置,向后匹配
        s2 = str2; // 重新开始匹配
        while (*s1 && *s2 && (*s1 == *s2))
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
        {
            return cur; // 找到了
        }            
        cur++; // 本轮匹配完毕并未找到,从下一个位置开始,重新匹配
    }    
    return NULL; // 找不到
}

第二种写法各第一种写法本质上没有什么区别只是采用了循环变量

定义两个循环变量i,j,分别求出两个字符串的长度

image-20220403005138626

char * my_strstr ( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    int lenStr1 = strlen(str1);
    int lenStr2 = strlen(str2);
    int i = 0, j = 0;
    
    if (lenStr2 == 0)
        return (char*)&str1[0];
    while (i < lenStr1 && j < lenStr2)
    {
        if (str1[i] == str2[j])
        {
            i++;
            j++;
        }
        else
        {
           	i = i - j + 1; // 回到本次起始位置下一位
            j = 0; // 回到起始位置
		}
    }
    
    if (j == lenStr2) // j先走完,代表查找到了子串,返回i-j所对应下标地址
    {
        return (char*)&str1[i - j];
    }  
    
   return NULL;   
}

有时候我们并不是想查找第一次出现子串的位置而是最后一次出现子串的位置 (和strchr,strrchr第一次出现字符和最后一次出现字符相似)但是我们的库函数并没有给我们提供strrstr函数,实现起来也很简单代码如下:

char* my_strrstr( const char * str1, const char * str2 )
{
    assert(str1 && str2);
    char* ret = NULL;
    char* pos = strstr(str1, str2); // 为了方便这里我们直接调用库函数来查找子串
    while (pos != NULL)
    {
		ret = pos; // 记录最后一次子串的位置
        pos = strstr(pos + 1, str2); // 从下一个位置继续查找子串
    }
    
    return ret;    
}

1.4.4 字符串分割

库函数strtok原型如下:

char * strtok ( char * str, const char * sep );
  • sep 参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由 sep 字符串中一个或者多个分隔符分割的标记。
  • strtok 函数找到 str 中的下一个标记,并将其用 '\0' 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改)。
  • strtok 函数的第一个参数不为 NULL,函数将找到 str 中第一个标记,strtok 函数将保存它在字符串中的位置。
  • strtok 函数的第一个参数为 NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回NULL指针。

image-20220402162132927

int main()
{
    char str[] = "1234567890@qq.com"; // 待分割字符串
    char buf[30] = {0};
    strcpy(buf, str); // strtok 会修改原字符串,所以这里我们创建一份临时拷贝
    char sep[] = "@."; // 分隔符
    char* ret;
    for (ret = strtok(buf, sep); ret != NULL; ret = strtok(NULL, sep))
        printf("%s\n", ret);    
    
    return 0;    
}

1.5 字符分类函数

函数如果它的参数符合下列条件就返回非0值,否则返回0
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任何可打印字符,包括图形字符和空白字符

加粗的几个函数是我们在刷题时,经常会用到的函数。这几个函数都相对比较简单,就不一一详细介绍了。

使用以上函数需要引用头文件 #include <ctype.h>

字符转换

int toupper ( int c ); // 将字符的小写形式,转换为大写形式
int tolower ( int c ); // 将字符的大写形式,转换为小写形式
// 注意这里并不会修改原字符,函数返回的是原字符的大写或小写形式
// 如果原字符不具有大小写,直接返回原字符 如:'2', '@', '['等。
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "ABCDEF";
    
    // 小写转大写
    for (int i = 0; i < strlen(arr1); i++)
        printf("%c", toupper(arr1[i]));
    printf("\n");
    // 大写转小写
    for (int i = 0; i < strlen(arr2); i++)
        printf("%c", tolower(arr2[i]));
    
    return 0;
}

错误信息报告

1. strerror

库函数 strerror 函数原型如下:

char * strerror ( int errnum );

strerror函数可以把错误码转换为所对应的错误信息,返回错误信息所对应的字符串的起始地址。

2. perror

库函数 perror 函数原型如下:

void perror ( const char * str );

perror 函数可以打印一个错误信息,无返回值。

在库函数调用过程中,如果发生了错误,都会产生对应的错误码,这些错误码都保存在全局变量 errno 中,如果使用这个全局变量需要引用对应的对文件 #include <errno.h>

malloc 向堆区申请一块指定大小的内存,如果malloc函数开辟成功,会返回指向该内存的指针,开辟失败会返回NULL

INT_MAX#define 定义的符号常量,表示整型所能表示的最大值,使用需要引用头文件 #include <limits.h>

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>

int main()
{
    int *p = (int*)malloc(INT_MAX);
    if (p == NULL) // 开辟失败,产生的错误码,存在 errno 中
    {
        printf("%s\n", strerror(errno)):
        perror("malloc");
    }
    return 0;
}

总结:

strerror只负责将错误码转换为对应的错误信息,不打印。

perror直接打印错误信息,并且我们可以加上注释来说明错误来源于那个函数

1.6 内存函数

1.6.1 memcpy

库函数 memcpy 函数原型如下:

void * memcpy ( void * destination, const void * source, size_t num );

重点:

  • 函数memcpysource 的位置开始向后复制 num 个字节的数据到 destination 的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果 sourcedestination 的任何的重叠,复制的结果都未定义的。
int main()
{
    int arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int arr2[10] = {0};
    
    // 这里我们把 arr1 的值全部拷贝到 arr2    
    memcpy(arr2, arr1, sizeof(int) * 10);
    
    for (int i = 0; i < 10; i++)
        printf("%d ", arr2[i]);    
    
    return 0;
}

模拟实现

void * memcpy ( void * dest, const void * src, size_t num )
{
    assert(dest && src);
    void* ret = dest;
    while (num--)
    {
        *(char*)dest = *(char*)src;
        dest = (char*)dest + 1;
        src = (char*)src + 1;
    }
    return ret;    
}

1.6.2 memmove

库函数 memmove 函数原型如下:

void * memmove ( void * destination, const void * source, size_t num );
  • memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

image-20220403010152527

image-20220401224242788

将数据从 src 的开头及结束分开来,一共分成为三个部分

当dest指针的起始位置始于第一部分时,我们采用从前往后拷贝的方式。

image-20220401224049429

当dest指针指针的起始位置始于第二部分时,这里我们就要采用从后往前的拷贝方式。

如果仍采用从前往后拷贝,那么第一次拷贝将4拷贝到6的位置将6给覆盖了,

image-20220401224647568

当dest指针起始位置始于第三部分时,无论采用从前往后拷贝,还是从后往前拷贝都可以。

image-20220401224930818

这里我们为了简单起见,如果dest指针的起始位置始于第一部分时,我们采用从前往后的方式进行拷贝,当dest指针的起始位置始于第二、三部分时,采用从后往前的拷贝方式,如果两块内存重叠那么我们可以不用拷贝。

模拟实现

void * my_memmove ( void * dest, const void * src, size_t num )
{
    assert(dest && src);
    void* ret = dest;
    if (dest < src) // 第一部分
    {
        // 从前往后
        while (num--)
        {
            *(char*)dest = *(char*)src;
            dest = (char*)dest + 1;
            src = (char*)src + 1;
        }
    }    
    else if (dest > src) // 第二、三部分
    {
		// 从后往前
        while (num--)
        {
            *((char*)dest + num) = *((char*)src + num);
        }
    }    
    return ret;
}

1.6.3 memcmp

库函数 memcmp 函数原型如下:

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

比较从 ptr1ptr2 指针开始的 num 个字节

image-20220402155618063

int main()
{
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {1, 2, 3, 4, 6};
    // 由于我们当前机器采用的是小端字节序
    // arr1在内存中存放顺序就为:
    // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
    // arr1在内存中存放顺序就为:
    // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 06 00 00 00
    
    printf("%d\n", memcmp(arr1, arr2, 16)); // 前十六个字节相同,返回0
    printf("%d\n", memcmp(arr1, arr2, 17)); // 第十七个字节 arr1 < arr2, 返回小于0的数
    
    return 0;
}

模拟实现

int my_memcmp ( const void * ptr1, const void * ptr2, size_t num )
{
	assert(ptr1 && ptr2);
    char* dest = (char*)ptr1;
    char* src = *(char*)ptr2;
    while (num-- && (*dest == *src))
    {
        dest++;
        src++;
    }
	return *dest - src;
}

1.6.4 memset

库函数 memset 函数原型如下:

void * memset ( void * ptr, int value, size_t num );

ptr 表示被填充字节起始位置。

value 表示要被填充的值。

num 表示要填充几个字节。

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    memset(arr, 0, sizeof(int) * 5); // 将每个字节都设置为0    
    for (int i = 0; i < 5; i++)
        printf("%d ", arr[i]);
    
    // 在VS中,arr在内存中是按小端字节序来存储的
    // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
    // 利用memset 将第个字节置为0
    // 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    
    // 通常来说我们利用memset来设置整形,只能设置为 0 或 -1, 如果设置为其它值往往得不到我们想要的结果
    // 如将每个字节设置为2
    // 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
    // 将其转换为对就的十进制为
    // 33686018 33686018 33686018 33686018 33686018
    // 并不是我们想要的 2 2 2 2 2
    memset(arr, 2, sizeof(int) * 5);
	for (int i = 0; i < 5; i++)
		printf("%d ", arr[i]);
    
    char str[] = "test memset";    
    memset(str, '-', 5); // 将前5个字节置为 '-'
    puts(str);
    
    return 0;
}

模拟实现

void * my_memset ( void * ptr, int value, size_t num )
{
	assert(ptr);
    char* ret = (char*)ptr;
    while (num--)
    {       
        *(char*)ptr = value;
        ptr = (char*)ptr + 1;
    }   
    return ret;
}
  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值