十一、字符串操作函数与内存操作函数

一、字符函数与字符串函数

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数.

strcpy, strncpy, strcat, strncat, strcmp, strncmp 这些操作的都是字符串,主要针对 '\0'操作。但是对于整型数组,浮点型数组,结构体数组的时候就不能用了,所以还设有内存函数

1.1 strlen(字符串长度)

size_t strlen ( const char * str );

/*
    1. 字符串已经'\0' 作为结束标志,strlen函数返回的是在字符串中'\0' 前面出现的字符个数(不包含'\0' )。
    2. 参数指向的字符串必须要以'\0' 结束。
    3. 注意函数的返回值为size_t,是无符号的( 易错)
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

int my_strlen(const char * str)
{
    int count = 0;
    assert(str != NULL);
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}

int main()
{
    int len = strlen("abcdef");
    char arr[] = { 'a', 'b', 'c', 'd' };
    printf("%d\n", my_strlen("abcdef")); // 6
    printf("%d\n", len);          // 6
    printf("%d\n", strlen(arr));  // 随机值,找不到 '\0'
    
    if (strlen("abc") - strlen("abcdef") > 0)
    {
        printf("haha\n"); //haha, 因为 strlen返回的值是 size_t(unsigned int)。 3 - 6 = -3.但是是无符号,所以转换为正数了
    }
    else
    {
        printf("hehe\n");
    }


    return 0;
}

 

1.2 strcpy、strncpy(字符串拷贝)

1.2.1 strcpy

char* strcpy(char * destination, const char * source );
/*
1. 源字符串必须以'\0' 结束。 char a[] = {'1', '2'}这样就不行了
2. 会将源字符串中的'\0' 拷贝到目标空间。 
3. 目标空间必须足够大,以确保能存放源字符串。
4. 目标空间必须可变。  char *p = "abcdef";这样也不行,因为 p指向的是常量字符串,不能改变。
5. 学会模拟实现。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

char * my_strcpy(char* dest, const char* src)
{
    assert(dest != NULL);
    assert(src != NULL);
    char* ret = dest;
    // 拷贝 src指向的字符串到dest指向的空间, 包含 '\0',返回目的空间的起地址
    while (*src != '\0')
    {
        *dest++ = *src++;
    }
    *dest = *src; // 添加'\0'
    /*
    下面这样写也行
    while (*dest++ = *src++)
    {
        ;
    }
    */
    return ret;
}

int main()
{
    char arr1[] = "abcdefg";
    char arr2[] = "wy";
    char arr3[] = "wangyong";
    char arr4[] = "wangy";
    strcpy(arr1, arr2);
    printf("%s\n", arr1);  //  wy

    my_strcpy(arr3, arr4);
    printf("%s\n", arr3);  // wangy

    return 0;
}

1.2.2 strncpy

char * strncpy ( char * destination, const char * source, size_t num );
/*
拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    char arr1[6] = "world";
    char arr2[] = "abc";
    strncpy(arr1, arr2, 4);
    printf("%s\n", arr1);
    my_strncpy(arr1, arr2, 4);
    return 0;
}

 

1.3 strcat、strncat(字符串追加)

1.3.1 strcat

char * strcat ( char * destination, const char * source );
/*
1. 源字符串必须以'\0' 结束。
2. 目标空间必须有足够的大,能容纳下源字符串的内容。
3. 目标空间必须可修改。
4. 字符串自己给自己追加,如何?  报错
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strcat(char *dest, const char *src)
{
    char *ret = dest;
    assert(dest != NULL);
    assert(src);
    while (*dest != '\0') // 找到目标字符串的末尾
    {
        dest++;
    }
    while (*dest++ = *src++)// 追加
    {
        ;
    }
    return ret;
}

int main()
{
    char arr1[10] = "abc";  // 将arr2追加到arr1上, arr1的空间一定要足够大才行,这样才能追加成功,否则报错
    char arr2[] = "def";
    char arr3[15] = "hello";
    char arr4[] = "world";
    strcat(arr1, arr2);    // abcdef
    printf("%s\n", arr1);
    my_strcat(arr3, arr4);
    printf("%s\n", arr3);  // helloworld
 
    return 0;
}

 

1.3.2 strncat

char * strncat ( char * destination, const char * source, size_t num );
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    strncat(arr1, arr2, 6);
    printf("%s\n", arr1);
    return 0;
}

1.4 strcmp、strncmp(字符串比较)

1.4.1 strcmp

int strcmp ( const char * str1, const char * str2 );
/*
1. 第一个字符串大于第二个字符串,则返回大于0的数字
2. 第一个字符串等于第二个字符串,则返回0
3. 第一个字符串小于第二个字符串,则返回小于0的数字
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 && str1);
    int ret = 0;
    int i = 0;
    while (*str1 == *str2)
    {
        if (*str1 == '\0')
        {
            return 0;  // 相等
        }
        str1++;
        str2++;
    }
    return (*str1 - *str2);
}

int main()
{
    char* p1 = "abcdef";
    char* p2 = "abqwer";
    int ret = 0;
    ret = strcmp(p1, p2);
    printf("ret = %d\n", ret);  // <0
    ret = my_strcmp(p1, p2);
    printf("ret = %d\n", ret);  // ret = -14

    return 0;
}

1.4.2 strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    int ret = strncmp("abcd","abcf", 3);
    printf("ret = %d\n",ret);  // ret = 0
    return 0;
}

1.5 strstr(查找子字符串)

char * strstr ( const char *str1, const char * str2);
// 在字符串 str1 中查找第一次出现字符串 str2(不包含空结束字符)的位置。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

char* my_strstr(const char *p1, const char *p2)
{
    assert(p1 != NULL);
    assert(p2 != NULL);
    char *s1 = NULL;
    char *s2 = NULL;
    char *cur = (char *)p1;
    if (*p2 == '\0') // 空字符串
    {
        return (char *)p1;
    }
    while (*cur)
    {
        s1 = cur;
        s2 = (char *)p2;
        while ((*s1 == *s2) && *s1  && *s2)
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
        {
            return cur;
        }
        if (*s1 == '\0')  // 提前终止,可以没有这个
        {
            return NULL;
        }
        cur++;
    }

    return NULL;
}


int main()
{
    char *p1 = "abcdefgdef";
    char *p2 = "def";
    char *ret = strstr(p1, p2);  // 从 p1 中找 p2, 输出的结果是从找到第一次子字符串的首地址一直往后的字符串
    if (ret == NULL)
    {
        printf("不存在字符串\n");
    }
    else
    {
        printf("%s\n", ret); // defgdef
    }

    ret = my_strstr(p1, p2);
    if (ret == NULL)
    {
        printf("不存在字符串\n");
    }
    else
    {
        printf("%s\n", ret); // defgdef
    }
    return 0;
}

 

1.6 strtok(字符串切分)

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

int main()
{
    char arr[] = "abc.def@gh";
    char *p = ".@";
    char buff[1024] = { 0 };
    strcpy(buff, arr);

    char *ret = strtok(buff, p);
    printf("ret = %s\n", ret); // ret = abc

    ret = strtok(NULL, p);    
    printf("ret = %s\n", ret); // ret = def

    ret = strtok(NULL, p);
    printf("ret = %s\n", ret); // ret = gh

    return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

int main()
{
    char arr[] = "abc.def@gh";
    char *p = ".@";
    char buff[1024] = { 0 };
    char* ret = NULL;
    
    strcpy(buff, arr);
    
    for (ret = strtok(buff, p); ret != NULL;ret = strtok(NULL, p))
    {
        printf("%s  ", ret);  // abc  def  gh
    }
    return 0;
}

1.7 strerror(错误信息函数)

char * strerror ( int errnum );
// 返回错误码,所对应的错误信息。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    char *str = strerror(0);
    printf("%s\n", str);  // No error

    str = strerror(1);
    printf("%s\n", str);  // Operation not permitted

    str = strerror(2);
    printf("%s\n", str);  // No such file or directory

    // 0, 1, 2就是错误码
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<errno.h>

int main()
{
    // error 是一个全局的错误码变量,当C语言的库函数在执行过程中发生了错误,就会把对应的错误码复制到error中
    
    // 打开文件
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        printf("%s\n", strerror(errno)); // No such file or directory
    }
    else
    {
        printf("open file syccess\n");
    }

    return 0;
}

1.8 字符分类函数

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>

int main()
{
    char ch = 'w';
    int ret = islower(ch);
    printf("%d\n", ret);  // 2  非 0 的数
    return 0;
}

 

1.9 字符转换函数

int tolower ( int c );
int toupper ( int c );
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<ctype.h>

int main()
{
    char ch = tolower('Q');
    printf("%c\n", ch); // q
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<ctype.h>

int main()
{
    char arr[] = "I Am Wang Yong";
    int i = 0;
    while (arr[i])
    {
        if (isupper(arr[i]))
        {
            arr[i] = tolower(arr[i]);
        }
        i++;
    }
    printf("%s\n", arr); // i am wang yong
    
    return 0;
}

二、内存函数

2.1 memcpy(不重叠内存拷贝)

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

/*
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到'\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

struct S
{
    char name[20];
    int age;
};

int main()
{
    int arr1[] = { 1, 2, 3, 4, 5 };
    int arr2[5] = { 0 };
    struct S arr3[] = { { "张三", 20 }, {"李四",30} };
    struct S arr4[3] = {0};
    memcpy(arr2, arr1, sizeof(arr1));
    memcpy(arr4, arr3, sizeof(arr3));
    return 0;
}

 

 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

struct S
{
    char name[20];
    int age;
};

void* my_memcpy(void* dest, const void* src, size_t num)
{
    void *ret = dest;
    assert(dest != NULL);
    assert(src != NULL);
    while (num)
    {
        *(char *)dest = *(char *)src;
        ++(char *)dest;
        ++(char *)src;
        num--;
    }
    return ret;
}

int main()
{
    int arr1[] = { 1, 2, 3, 4, 5 };
    int arr2[5] = { 0 };
    struct S arr3[] = { { "张三", 20 }, {"李四",30} };
    struct S arr4[3] = {0};
    my_memcpy(arr2, arr1, sizeof(arr1));
    my_memcpy(arr4, arr3, sizeof(arr3));
    return 0;
}

 

 

2.2 memmove(重叠内存拷贝)

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

/*
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int i;
    memmove(arr + 2, arr, 20);
    for (i = 0; i < 10; i++)
    {
        printf("%d  ", arr[i]);// 1 2 1 2 3 4 5 8 9 10 
    }
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

void* my_memmove(void* dest, const void* src, size_t count)
{
    void *ret = dest;
    assert(dest != NULL);
    assert(src != NULL);
    if (dest < src)
    {
        // 从前向后
        while (count--)
        {
            *(char *)dest = *(char *)src;
            ++(char *)dest;
            ++(char *)src;
        }
    }
    else
    {
        // 从后向前
        while (count--)
        {
            *((char *)dest + count) = *((char *)src + count);
        }
    }
    return ret;
}

int main()
{
    int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int arr2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    my_memmove(arr1, arr1+2, 20);
    my_memmove(arr2+2, arr2, 20);

    return 0;
}

2.3 memcmp(内存比较)

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

/*
比较从ptr1和ptr2指针开始的num个字节
<0
=0
>0
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    int arr1[] = { 1, 2, 3, 4, 5 };
    int arr2[] = { 1, 2, 5, 4, 3 };
    int ret = memcmp(arr1, arr2, 9);
    printf("%d\n", ret);  // -1

    return 0;
}

2.4 memset(内存设置)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>

int main()
{
    char arr[10] = "";
    int i = 0;
    memset(arr, '#', 10);
    for (i = 0; i<10; i++)
    {
        printf("%c ", arr[i]); // # # # # # # # # #
    }
    return 0;
}

 

posted on 2022-01-14 10:43  软饭攻城狮  阅读(35)  评论(0编辑  收藏  举报

导航