字符串拷贝函数memcpy()、strncpy()和snprintf()性能之比较

问题:

函数memcpy(dest, src, sizeof(dest))、strncpy(dest, src, sizeof(dest))和snprintf(dest, sizeof(dest), "%s", src)都可以将src字符串中的内容拷贝到dest字符串中。

哪一种方式效率最高呢?

就是说,哪种方式性能最好呢?


解决办法:

1. 建立三个文件test_memcpy.c,test_strncpy.c和test_snprintf.c:

文件test_memcpy.c:

david@u1110-hp:~/wrk/tmp/cstring$ cat test_memcpy.c
#include <string.h>
int main(){
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;

	for(int i = 0; i < 10000000; ++i){
		memset(dest, 0, sizeof(dest));
		len = strlen(src);
		len = sizeof(dest) - 1 > len? len: sizeof(dest) -1;
		memcpy(dest, src, len);
		dest[len] = '\0';
	}

	return 0;
}

文件test_strncpy.c:

#include <string.h>
int main() {
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;
	
	for(int i = 0; i < 10000000; ++i) {
		memset(dest, 0, sizeof(dest));
		strncpy(dest, src, sizeof(dest));
	}
	
	return 0;
}
文件test_snprintf.c:

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

int main() {
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;
	
	for(int i = 0; i < 10000000; ++i) {
		memset(dest, 0, sizeof(dest));
		snprintf(dest, sizeof(dest), "%s", src);
	}
	
	return 0;
}

2. 分别编译三个文件:

david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_memcpy test_memcpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_strncpy test_strncpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_snprintf test_snprintf.c

3. 没有优化的情况下不同函数消耗时间对比:

david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m16.472s
user	0m16.309s
sys	0m0.036s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m6.106s
user	0m6.100s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m4.179s
user	0m4.144s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$

从上面运行结果可以看出:没有任何优化的情况下,memcpy()和strncpy()性能相差4倍,snprintf()和strncpy()性能相差约2.5倍。


4.采用O3优化情况下不同函数消耗时间对比:

david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_snprintf test_snprintf.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_strncpy test_strncpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_memcpy test_memcpy.c
david@u1110-hp:~/wrk/tmp/cstring$
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m16.178s
user	0m16.161s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m6.242s
user	0m6.032s
sys	0m0.056s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m3.567s
user	0m3.436s
sys	0m0.012s
david@u1110-hp:~/wrk/tmp/cstring$

从上面运行结果可以看出:采用O3优化后,memcpy()和strncpy()性能相差近5倍,snprintf()和strncpy()性能相差基本不变约2.5倍。

5. 性能对比结论:

在需要用到字符串拷贝函数的时候,永远不要使用strncpy(),无论什么时候都用snprintf()来代替,而memcpy()是性能更好的实现方式

strlen+memcpy也是linux内核的实现方式。

6. 意外收获结论:

将上述三个文件中的memset()改为用bzero()来实现数组的清零操作。

使用O3来进行优化,三个函数的耗时时间如下:

david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m14.395s
user	0m13.929s
sys	0m0.092s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m3.785s
user	0m3.772s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m1.241s
user	0m1.236s
sys	0m0.004s
david@u1110-hp:~/wrk/tmp/cstring$

结论:仅仅换了一个清零函数,使得memcpy()和strncpy()的性能差别达到约12倍,而snprintf()和strncpy()的性能差别也达到约4倍。

就清零操作来说,bzero()远比memset()更高效








<think>好的,我现在需要回答用户关于C语言中替代sprintfsnprintf的其他字符串拷贝函数的问题。用户已经知道sprintfsnprintf,但想找其他选项。首先,我得回忆一下C标准库中有哪些字符串处理函数可以用来复制或格式化字符串。 首先想到的是strcpystrncpy,这两个是基本的字符串拷贝函数strcpy直接复制整个字符串,但存在缓冲区溢出的风险,而strncpy允许指定最大拷贝长度,相对安全些。不过它们只能处理简单的字符串拷贝,不带格式化功能,这点需要说明清楚。 接下来是memcpymemmove,这两个函数用于内存块的拷贝,适合处理任何类型的数据,包括字符串。特别是memmove,可以处理内存重叠的情况,而memcpy在重叠时行为未定义。不过它们也不支持格式化,只是单纯的内存复制。 然后是strcatstrncat,用于字符串连接。虽然主要功能是连接,但结合strcpy的话,可以分步构建字符串,但同样没有格式化功能,用户可能需要多次调用来组合内容。 如果用户需要格式化功能,可能需要其他替代方案。例如,asprintf函数,但这个是非标准的,主要在GNU扩展中提供,可以在支持的环境下动态分配内存并格式化字符串,避免了缓冲区溢出的问题。不过需要注意可移植性。 另外,Windows环境下可能有_snprintf_s或strcpy_s等安全版本的函数,属于C11标准中的可选部分,即附录K,但并非所有编译器都支持。这些函数提供了更严格的参数检查,增强安全性。 用户还可能考虑使用第三方库,如glib中的g_strdup_printf,或者自己编写安全的格式化函数,结合vsnprintf动态内存分配,确保足够的缓冲区大小。 需要提醒用户注意目标缓冲区的大小,避免溢出,特别是在使用strcpymemcpy时。同时,强调不同函数的适用场景,比如是否需要格式化,是否需要处理内存重叠,或者是否需要动态分配内存。 最后,总结这些替代方案,并根据用户的需求推荐合适的函数。如果用户需要格式化,且环境允许,可以建议asprintf或安全版本的函数;如果只是简单拷贝,推荐strncpymemcpy。同时,注意各函数的局限性平台依赖性。</think>在C语言中,除了$sprintf$$snprintf$之外,还有以下几种字符串拷贝函数可供选择,根据具体需求选择合适的方案: --- ### 一、基础字符串拷贝函数 1. **strcpy** ```c char *strcpy(char *dest, const char *src); ``` - **功能**:将$src$指向的字符串(包括空终止符)复制到$dest$。 - **风险**:不检查目标缓冲区大小,可能导致缓冲区溢出[^1]。 - **适用场景**:已知目标缓冲区足够大时。 2. **strncpy** ```c char *strncpy(char *dest, const char *src, size_t n); ``` - **功能**:最多复制$n$个字符到$dest$,若$src$长度超过$n$,不会自动添加空终止符。 - **改进**:相比$strcpy$更安全,但需手动添加空终止符。 --- ### 二、内存级拷贝函数 3. **memcpy** ```c void *memcpy(void *dest, const void *src, size_t n); ``` - **功能**:从$src$复制$n$字节到$dest$,不处理空终止符。 - **适用场景**:非字符串的二进制数据复制,或明确知道数据长度时。 4. **memmove** ```c void *memmove(void *dest, const void *src, size_t n); ``` - **功能**:与$memcpy$类似,但支持源目标内存区域重叠的情况[^1]。 --- ### 三、安全增强函数(C11可选) 5. **strcpy_s**(需编译器支持) ```c errno_t strcpy_s(char *dest, rsize_t dest_size, const char *src); ``` - **功能**:检查目标缓冲区大小,若$dest_size$不足则触发运行时约束错误。 - **优势**:强制安全校验,避免溢出。 6. **memcpy_s** ```c errno_t memcpy_s(void *dest, rsize_t dest_size, const void *src, rsize_t n); ``` - **功能**:带长度校验的内存拷贝,防止越界。 --- ### 四、动态格式化方案 7. **asprintf**(GNU扩展) ```c int asprintf(char **strp, const char *format, ...); ``` - **功能**:动态分配内存并格式化字符串,避免手动计算长度。 - **示例**: ```c char *buffer; asprintf(&buffer, "Value: %d", 42); // 需手动free(buffer) ``` --- ### 五、自定义安全函数 若需完全控制逻辑,可结合$vsnprintf$与动态内存分配: ```c int safe_format(char **output, const char *format, ...) { va_list args; va_start(args, format); int length = vsnprintf(NULL, 0, format, args); va_end(args); *output = malloc(length + 1); if (!*output) return -1; va_start(args, format); vsnprintf(*output, length + 1, format, args); va_end(args); return length; } ``` --- ### 总结对比 | 函数 | 安全性 | 格式化支持 | 动态内存 | 标准支持 | |--------------|-------------|------------|----------|----------------| | strcpy | 低 | 否 | 否 | C89 | | strncpy | 中 | 否 | 否 | C89 | | memcpy/memmove| 中(需手动) | 否 | 否 | C89 | | strcpy_s | 高 | 否 | 否 | C11(可选) | | asprintf | 高 | 是 | 是 | GNU扩展 | | 自定义函数 | 高 | 是 | 是 | 依赖实现 | ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值