C语言 strlcpy 和 strlcat

strlcpy 和 strlcat 是在某些 UNIX 系统(如 BSD)中提供的字符串操作函数,它们是 strcpy 和 strcat 的更安全替代方案,主要用于防止缓冲区溢出。尽管它们比 strcpy 和 strcat 更安全,仍然在某些情况下被认为是不安全的。

1、strlcpy 和 strlcat 的使用

strlcpy(dest, src, size) 复制 src 字符串到 dest,最多 size-1 个字符,并确保 dest 以 \0 结尾。

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

int main() {
    char dest[10];
    const char *src = "Hello, World!";
    
    size_t copied = strlcpy(dest, src, sizeof(dest));
    
    printf("Copied string: %s\n", dest);
    printf("Total length of source: %zu\n", copied);

    if (copied >= sizeof(dest)) {
        printf("Warning: String was truncated!\n");
    }

    return 0;
}

strlcat(dest, src, size)dest 末尾追加 src,最多 size-1 个字符,并确保 dest\0 结尾。

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

int main() {
    char dest[15] = "Hello";
    const char *src = ", World!";
    
    size_t total_length = strlcat(dest, src, sizeof(dest));

    printf("Concatenated string: %s\n", dest);
    printf("Total length required: %zu\n", total_length);

    if (total_length >= sizeof(dest)) {
        printf("Warning: String was truncated!\n");
    }

    return 0;
}

2、strlcpy 和 strlcat 被认为不安全的原因

1)可能导致数据截断

strlcpystrlcat 确保目标字符串以 \0 结尾,但它们不会报错,如果 src 太长,只会截断数据,并返回原始字符串的长度。这可能导致部分数据丢失,而开发者未意识到问题。

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

int main() {
    char buffer[10];
    const char *source = "This is a long string";

    size_t result = strlcpy(buffer, source, sizeof(buffer));

    printf("Buffer: %s\n", buffer);
    // 需要的大小是 22,但 buffer 只有 10
    printf("Required buffer size: %zu\n", result); 

    return 0;
}

2)strlcat 可能导致低效的性能

strlcat 需要计算 dest 的长度,也就在长字符串上使用 strlcat 可能导致性能下降,尤其是在循环中调用 strlcat 时,每次都需要从头计算 strlen(dest)

char buffer[1024] = "Initial data";
for (int i = 0; i < 1000; i++) {
    strlcat(buffer, "Appending", sizeof(buffer));
}

3)不能防止缓冲区溢出

尽管 strlcpystrlcat 试图防止缓冲区溢出,但如果开发者没有正确检查返回值,仍然可能出现溢出或数据丢失问题。例如,开发者可能在 strlcpy 之后继续写入字符串,导致缓冲区溢出。

char buffer[10];
strlcpy(buffer, "Hello", sizeof(buffer));
strcat(buffer, " World");  // 这里会导致缓冲区溢出

4)不适用于所有系统

strlcpystrlcat 并不是 C 标准库的一部分,它们最初来自 BSD,许多系统(如 Windows 和部分 Linux 发行版)并不支持它们。因此,使用这些函数会影响代码的可移植性。

3、更安全的替代方案

1) snprintf

snprintf 在目标字符串满时不会丢失数据,同时提供格式化功能。

char buffer[10];
snprintf(buffer, sizeof(buffer), "%s", "This is a long string");
printf("%s\n", buffer);  // 结果: "This is "

2)memcpy + strnlen

如确定数据不会被截断,可以使用 memcpystrnlen 进行更安全的操作。

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

void safe_strcopy(char *dest, const char *src, size_t dest_size) {
    size_t len = strnlen(src, dest_size - 1);  // 计算可复制长度
    memcpy(dest, src, len);
    dest[len] = '\0';
}

int main() {
    char buffer[10];
    safe_strcopy(buffer, "This is a long string", sizeof(buffer));
    printf("Buffer: %s\n", buffer);
    return 0;
}

推荐阅读
cjavapy编程之路首页