C语言中字符串的复制与memcpy函数

时间:2023-01-07 15:16:24

今天在线做一道360实习生笔试题, 发现大多数题还是比较基础的, 但都需要仔细斟酌. 不过我已被虐得面目全非, 操作系统和计算机网络相关的很多知识都不会, 还得努力学习. 不过说重点, 面试题有两个编程题, 第一个是实现hashmap, 第二题是一个字符串的处理, 我主要说说第二题, 题目描述如下:

实现域名的字段顺序翻转函数,比如输入www.so.com, 输出为com.so.www, 要求实现的效率尽可能的优化

在做这个题的时候还剩下不到10分钟, 慌忙中将题目看复杂了,我想成会有www.abc.efg.com这样的情况了, 于是惯性思维促使我使用c++将域名按”.”分段, 然后逆序输出, 写的也烂, 也不是效率最高的, 提交后再看题目发现域名是只有www域名本体com三个部分, 当然后面的的com可能是其他的后缀,比如io或者info之类的, 发现直接是memcpy()复制内存片段效率应该会更高, 即首先复制后缀, 然后复制域名本体, 然后再复制www到字符串缓冲区,然后输出就是结果了,于是用c实现了这个方法:

#include <stdio.h>
#include <string.h>
void trans(char *res, char *a)
{
    strcat(a, "."); //在域名默认加上".", 因为"com", 移动到最前面后后面需要一个"."
    const char *src = a; //复制一个常量副本, 因为memcpy()函数的参数中要求常量
    int len = strlen(src) - 1;
    int i = len;
    while(src[--i] != '.'); //找到后开始的下标

    memcpy(res, src + i + 1, (len - i) * sizeof(char)); //复制后缀到res字符串缓冲区的起点
    memcpy(res + (len - i), src + 4, (i - 3) * sizeof(char)); //将域名内容复制到res缓冲区的末尾
    memcpy(res + (len + 1 - 4), src, 3 * sizeof(char)); //继续将"www"复制到res末尾
    res[len] = '\0'; //设置字符串结束标志
}

int main()
{
    char a[100], res[100];
    scanf("%s", a);
    trans(res, a);
    puts(res);
    return 0;
}

输出结果如下:

www.baidu.com
com.baidu.www

关于c语言中字符串的复制, 通常可以使用strcpy()来实现, 当然也可手动写个循环, 但是复制效率是最低的, 无论是从人的角度还是计算机的角度来说, 第三个就是直接使用memcpy(), 直接将复制目标内存片段到指定位置, 并且复制速度是最快的, 但是strcpy()memcpy()在用来复制字符串的时候有一个小小的区别就是无论如何strcpy()从字符串的开始只复制到第一个”\0”的位置, 但是由于memcpy()是整个内存片段,所以”\0”也不会被忽略, 虽然最后复制内容输入到屏幕显示的结果是一样的, 我们用个小程序演示一下这个区别:

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

int main()
{
    char a[20] = "asdf\0uiop";
    char b[20];
    char c[20];

    strcpy(b, a);
    puts(b);
    printf("%d\n", strlen(a));
    for (int i = 5; i < 9; i++)
        printf("%c", b[i]);
    printf("\n");

    puts("-----------------------");
    memcpy(c, a, 9 * sizeof(char));
    puts(c);
    printf("%d\n", strlen(c));
    for (int i = 5; i < 9; i++)
        printf("%c", c[i]);
    printf("\n");
    return 0;
}

输出结果如下:

asdf
4
S

-----------------------
asdf
4
uiop
请按任意键继续. . .

从结果可以看出,使用strcpy()仅仅复制到了”\0”, 因为输出b的后半部分结果是未定义的,说明并没有复制过来, 但是输出c的后半部分是, 输出了事先定义的结果

同样, 利用memcpy()函数的特性, 我们可以做一个泛型的交换函数:

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

//因为交换的数据类型是未知的, 所以添加一个额外的参数告诉函数复制多长内存片段
void swap(void *a, void *b, int size)
{
    char buffer[size];
    memcpy(buffer, a, size);
    memcpy(a, b, size);
    memcpy(b, buffer, size);
}

int main()
{
    char a = 'a', b = 'b';
    swap(&a, &b, sizeof(char));
    printf("a = %c b = %c\n", a , b);

    int x = 11, y = 99;
    swap(&x, &y, sizeof(int));
    printf("x = %d y = %d\n", x , y);
    return 0;
}

输出:

a = b b = a
x = 99 y = 11