C-string

本文深入探讨了C语言中的字符串操作函数,包括strcpy、strncpy、memcpy、strcat、strncat、strchr、strrchr、strchrnul等。通过实例分析了它们的功能、使用场景及注意事项,如strcpy和strncpy在拷贝字符串时的差异,strcat和strncat在拼接字符串时的行为,以及strchr和strrchr在查找字符上的不同。此外,还提到了strlen函数计算字符串长度的方法以及sizeof运算符的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、C-string描述

二、C-string相关操作

1、字符数组初始化

1.1、memset函数

2、字符串拷贝

2.1、strcpy函数

函数原型:
    #include <string.h>
    char *strcpy(char *dest, const char *src);
作用:
    strcpy()函数把src所指向的字符串(包括结尾的空字符'\0')拷贝到dest所指向的空间中,dest空间必须足够大,以容纳拷贝的字符。
返回值:
    返回指向目标空间dest的指针。

验证程序:

/* 遍历打印字符数组 */
void arr_print(const char *arr, size_t arr_len)
{
    for (int index = 0; index < arr_len; index++) {
        /* 如果遇到空字符('\0')则用NULL替换 */
        if (arr[index] == '\0') {
            printf(" NULL ");
            continue;
        }
        printf(" %c ", arr[index]);
    }
}
char dest[10];
memset(dest, '*', sizeof(dest));
char src[] = {'A', 'B', 'C', 'D', '\0', 'E', 'F'};
strcpy(dest, src);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

  图1 strcpy()函数输出结果

结论:       

       由以上结果可以得出,strcpy()函数在拷贝src中的字符时遇到空字符就停止拷贝,并连同空字符一起拷贝到dest所指向的内存空间中。 

2.2、strncpy函数

函数原型:
    #include <string.h>
    char *strncpy(char *dest, const char *src, size_t n);
作用:
    strncpy()函数的作用和strcpy()类似,不过它只拷贝src的前n个字符。
返回值:
    返回指向目标空间dest的指针。

 思考:当src指向的字符串分别处于以下几种情况时,strncpy()函数会如何处理?

(假设此时dest所指向的内存空间足够大)

       情况一:当src的长度大于n,并且src的前n个字符不包含空字符时

       情况二:当src的长度大于n,并且src的前n个字符包含空字符时

       情况三:当src的长度小于n时

编写验证程序,分别对以上几种情况进行测试。

对情况一的验证:

char dest[10];
memset(dest, '*', sizeof(dest));
/* 情况一:当src的长度大于n,并且src的前n个字符不包含空字符时 */
char src[] = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
strncpy(dest, src, 5);
arr_print(dest, sizeof(dest));

 输出结果如下图所示:

图2 strncpy()函数对情况一的处理结果 

对情况二的验证:

/*  情况二:当src的长度大于n,并且src的前n个字符包含空字符时 */
char src[] = {'A', 'B', 'C', '\0', 'E', 'F', '\0'};
strncpy(dest, src, 5);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

图3 strncpy()函数对情况二的处理结果

对情况三的验证:

/* 情况三:当src的长度小于n时 */
char src[] = {'A', 'B', 'C', '\0'};
strncpy(dest, src, 5);

输出结果如下图所示:

图4 strncpy()函数对情况三的处理结果

结论:

        对于情况一,strncpy()函数会拷贝src的前n个字符到dest所指向的内存空间中。但是请仔细观察输出结果,dest不是以空字符结尾的,此时的dest就仅仅只是一个字符数组而不是一个字符串。为了解决这个问题,可以令dest[n] = '\0'。(注:此种情况有风险!!!

        对于情况二和情况三,strncpy()函数的处理策略一致,即遇到空字符就停止拷贝(包括空字符),并将剩余的位置都用空字符填充,以确保共有n个字节的字符被拷贝到dest所指向的内存空间中

2.3、memcpy函数

函数原型:
    #include <string.h>
    void *memcpy(void *dest, const void *src, size_t n);
作用:
    memcpy()函数从src指向的内存空间拷贝n个字节到dest所指向的内存空间中,内存空间必须非重叠,否则就要使用memmove()函数。
返回值:
    返回指向目标空间dest的指针。

思考:对于strncpy()中所述的情况一、情况二、情况三,memcpy()函数又会如何处理呢? 

对情况一的验证:

char dest[10];
memset(dest, '*', sizeof(dest));
/* 情况一:当src的长度大于n,并且src的前n个字符不包含空字符时 */
char src[] = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
memcpy(dest, src, 5);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

图5 memcpy对情况一的处理结果 

对情况二的验证:

char dest[10];
memset(dest, '*', sizeof(dest));
/*  情况二:当src的长度大于n,并且src的前n个字符包含空字符时 */
char src[] = {'A', 'B', 'C', '\0', 'E', 'F', '\0'};
memcpy(dest, src, 5);
arr_print(dest, sizeof(dest));

 输出结果如下图所示:

图6 memcpy对情况二的处理结果  

对情况三的验证:

char dest[10];
memset(dest, '*', sizeof(dest));
/* 情况三:当src的长度小于n时 */
char src[] = {'A', 'B', 'C', '\0'};
memcpy(dest, src, 5);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

图7 memcpy对情况三的处理结果  

结论:

       对于情况一,memcpy()函数的处理方式和strncpy()的处理方式类似,拷贝src的前n个字符到dest所指向的内存空间中(有风险,此时的dest是一个字符数组而不是字符串,可以考虑令dest[n] == '\0')。

       对于情况二,memcpy()函数在遇到空字符时并不会停止拷贝仍会将str的前n个字符拷贝到dest所指向的内存空间中。

       对于情况三,memcpy()函数将src中的全部字符拷贝到dest所指向的内存空间后就会停止拷贝并不会在剩余的位置填充空字符。

3、字符串拼接

3.1 strcat函数

函数原型:
    #include <string.h>
    char *strcat(char *dest, const char *src);
作用:
    strcat()函数会将字符串src附加到dest字符串末尾。首先覆盖掉字符串dest末尾的空字符,
并在拼接后的字符串末尾添加空字符。dest必须足够大,以容纳所有的字符,否则,其行为是不可预知的。
返回值:
    strcat()函数返回指向字符串dest的指针。

验证程序:

char dest[100] = {'A', 'B', 'C', 'D', '\0'};
char src[10] = {'E', 'F', '\0'};
strcat(dest, src);

输出结果:

图8 strcat()输出结果 

 结论:

        由上图的输出结果可知strcat()函数的执行过程如下:

        (1)、检查src是否为空串,若src为空串则什么也不做;

        (2)、若src不为空,则使用第一个字符覆盖掉dest的空字符,然后依次将src中的字符拷贝到dest字符串中,遇到空字符则停止拷贝。

        (3)、最后在dest的末尾添加空字符。

3.2 strncat函数

函数原型:
    #include <string.h>
    char *strncat(char *dest, const char *src, size_t n);
作用:
    strncat()函数的功能和strcat类似,不过最多只拷贝src的前n个字符,同时dest字符串总是以空字符结尾。
    如果src的长度个数大于等于n,则src不必以空字符结尾,strncat()函数会拷贝n+1个字符串到dest
    (src的前n个字符 + 1个空字符),因此dest的长度至少要为strlen(dest) + n + 1。
返回值:
    返回指向dest的指针。
strncat()函数的实现可能如下:
char *strncat(char *dest, const char *src, size_t n)
{
    size_t dest_len = strlen(dest);
    size_t i;
    
    for (i = 0; i < n && src[i] != '\0'; i++)
        dest[dest_len + i] = src[i];
    dest[dest_len + i] = '\0';

    return dest;
}

思考:当src分别处于以下几种情况时,strncat()函数会如何处理,会和上文中描述一致吗?

        情况一:当src的长度大于n时

        情况二:当src的长度等于n时

        情况三:当src的长度小于n时

对情况一的验证:

char dest[10] = {'A', 'B', 'C', 'D', '\0'};
char src[10]  = {'E', 'F', 'G', 'H', '\0'};
// 情况一:当src的长度大于n时
strncpy(dest, src, 2);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

 图9 strncat()函数对情况一的处理结果

对情况二的验证:

char dest[10] = {'A', 'B', 'C', 'D', '\0'};
char src[10]  = {'E', 'F', 'G', 'H', '\0'};
// 情况二:当src的长度等于n时
strncpy(dest, src, 4);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

图10 strncat()函数对情况二的处理结果 

 对情况三的验证:

char dest[10] = {'A', 'B', 'C', 'D', '\0'};
char src[10]  = {'E', 'F', 'G', 'H', '\0'};
// 情况三:当src的长度小于n时
strncpy(dest, src, 6);
arr_print(dest, sizeof(dest));

输出结果如下图所示:

图11 strncat()函数对情况三的处理结果 

结论:

        由上图的输出结果,可以得出strncat()函数对字符串的处理策略和上面描述的过程一致。

4、查找

4.1 查找字符

4.1.1 strchr函数

函数原型:
    #include <string.h>
    char *strchr(const char *s, int c);
作用:
    在字符串s中查找字符c(末尾的空字符也是字符串的一部分,所以在查找范围内)。
返回值:
    返回字符c在字符串s中第一次出现位置的指针,若未找到则返回NULL。

验证程序:

char s[] = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
char *p1 = strchr(s, 'E');
char *p2 = strchr(s, 'G');
char *p3 = strchr(s, '\0');
printf("s : %p", s);
printf("p1: %p", p1);
printf("p2: %p", p2);
printf("p3: %p", p3);

 输出如下图所示:

图12 strchr()函数输出 

4.1.2 strchr函数实用小技巧----多次查找指定字符

小技巧:strchr()函数中的参数s不一定要是数组的首地址,它可以为数组中的任意地址,表示起始搜索位置。

示例程序----在字符串s中查找字符'A'所有出现过的位置:

char s[] = {'A', 'B', 'C', 'D', 'A', 'E', 'F', 'A', 'G', '\0'};
printf("s:   %p\n", s);
char *pos = strchr(s, 'A');
if (pos != NULL)
    printf("pos: %p\n", pos);
while (pos != NULL && pos < s + strlen(s)) {
    pos++;
    pos = strchr(pos, 'A');
    if (pos != NULL)
        printf("pos: %p\n", pos);
}

 输出结果如下图所示:

图13 strchr()函数反复查找某一字符输出结果 

strrchr函数

函数原型:
    #include <string.h>
    char *strrchr(const char *s, int c);
作用:
    在字符串s中反向查找字符c
返回值:
    返回字符c在字符串s中最后一次出现位置的指针,若未找到则返回NULL。

验证程序:

char s[] = {'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', 'A', 'B', 'C', 'D', '\0'};

 strchrnul函数

函数原型:
    #includ <string.h>
    char *strchrnul(const char *s, int c);
作用:
    strchrnul()函数在字符串s中查找字符c
返回值:
    和strchr()函数类似,区别是当在字符串s中未找到字符c的时候,会返回指向字符串s末尾空字符的指针而不是NULL。

验证程序:

char s[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', '\0'};
char *pos1 = strchrnul(s, 'E');
char *pos2 = strchrnul(s, 'I');
printf("s:    %p\n", s);
printf("pos1: %p\n", pos1);
printf("pos2: %p\n", pos2);

输出结果如下图所示:

  

4.2 查找字符串

4.3 其他查找

5、字符串比较

6、获取字符串长度

6.1 strlen函数

函数原型:
    #include <sring.h>
    size_t strlen(const char *s);
作用:
    strlen()函数计算s所指向的字符串的长度(不包括结尾的空字符)。
返回值:
    返回s指向的字符串中包含字符的个数。

6.2 sizeof运算符和strlen()函数的区分

        sizeof运算符和strlen()函数是易混淆的两个概念,但是两者还是有比较大的区别。sizeof作用于数组名获取的是为整个数组分配的内存空间,而strlen()函数获取的是以空字符结尾的字符串的长度(不包括结尾的空字符)。

详细内容可参考我的另一篇博客:sizeof运算符和strlen()函数的区分(详细)_frame_c的博客-优快云博客

验证程序:

// 情形一
char s[] = "ABCDEF";
sizeof(s);   // 输出结果:7
strlen(s);   // 输出结果:6   
sizeof(*s);  // 输出结果:1
strlen(*s);  // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形二
char s[] = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
sizeof(s);    // 输出结果:7
strlen(s);    // 输出结果:6
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形三
char s[] = {'A', 'B', 'C', 'D', '\0', 'F', '\0'};
sizeof(s);    // 输出结果:7
strlen(s);    // 输出结果:4
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形四
char s[100] = "ABCDEF";
sizeof(s);    // 输出结果:100
strlen(s);    // 输出结果:6
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形五
char s[100] = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
sizeof(s);    // 输出结果:100
strlen(s);    // 输出结果:6
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形六
char s[100] = {'A', 'B', 'C', 'D', '\0', 'F', '\0'};
sizeof(s);    // 输出结果:100
strlen(s);    // 输出结果:4
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形七
char *s = "ABCDEF";
sizeof(s);    // 输出结果:8  (linux 16 Inter(R) Xeon(R) CPU E7- 4807 @ 1.87GHz)
strlen(s);    // 输出结果:6
sizeof(*s);   // 输出结果:1
strlen(*s);   // 输出结果:error: invalid conversion from 'char' to 'const char*'
// 情形八
char *s = {'A', 'B', 'C', 'D', 'E', 'F', '\0'};
error:没有这种写法
// 情形九
char *s = {'A', 'B', 'C', 'D', '\0', 'F', '\0'};
error:没有这种写法

7、字符检查

8、字符串到数值类型转换

9、数值类型到字符串转换

三、自定义字符串操作函数

1、C语言实现----从一个字符串的指定位置提取指定长度的字符串

2、C语言实现----查找指定字符在给定字符串中出现的所有位置

     (1)、size_t loop_strchr(char s[], int c, size_t pos[]);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值