ZetCode

C strnlen 函数

最后修改:2025 年 4 月 8 日

字符串操作在 C 编程中是基础性的,而 strnlen 是安全计算字符串长度的关键函数。本教程将深入介绍 strnlen,包括其语法、用法以及相比 strlen 的优势。我们将探讨实用示例,并讨论有界字符串操作为何对安全性至关重要。理解 strnlen 有助于防止缓冲区溢出,同时保持程序的可靠性。

什么是 strnlen?

strnlen 函数计算字符串的长度,最多检查指定的给定大小。它在 string.h 中声明,并接受两个参数:字符串指针和要检查的最大长度。strnlenstrlen 更安全,因为它不会读取超出指定边界。如果未在边界内找到空终止符,它将返回字符串长度或最大大小。

基本的 strnlen 用法

此示例演示了如何使用 strnlen 安全地计算字符串长度。

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

int main() {
    char str[] = "Hello, World!";
    size_t max_len = 20;
    
    size_t len = strnlen(str, max_len);

    printf("String: %s\n", str);
    printf("Length: %zu\n", len);

    return 0;
}

在这里,strnlen 计算 str 的长度,最多可达 max_len 个字符。由于字符串的长度小于 20 个字符,因此它返回实际长度。%zu 格式说明符用于 size_t 值。在处理潜在的不可信输入时,这比 strlen 更安全。

处理无界字符串

此示例显示了 strnlen 如何防止无界字符串长度计算。

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

int main() {
    char str[50] = "This is a test";
    // Intentionally missing null terminator
    str[14] = 'X';
    
    size_t max_len = sizeof(str);
    size_t len = strnlen(str, max_len);

    printf("String (may be corrupted): %s\n", str);
    printf("Length (bounded): %zu\n", len);
    printf("Max possible length: %zu\n", max_len);

    return 0;
}

此示例演示了 strnlen 对缺失空终止符的保护。如果没有终止符,strlen 将读取缓冲区之外。strnlen 会在 max_len(在本例中为 50)处停止。这可以防止在格式错误的字符串中发生潜在的缓冲区溢出和未定义行为。

比较 strnlen 和 strlen

此示例将 strnlen 与不安全的 strlen 函数进行对比。

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

int main() {
    char buffer[10];
    // Create a string that's too long for buffer
    strcpy(buffer, "This string is too long");
    
    size_t unsafe_len = strlen(buffer);
    size_t safe_len = strnlen(buffer, sizeof(buffer));

    printf("Unsafe length: %zu\n", unsafe_len);
    printf("Safe length: %zu\n", safe_len);

    return 0;
}

在这里,strlen 返回实际字符串长度(22),忽略了缓冲区边界。strnlen 正确返回缓冲区大小(10),因为字符串超出了它。这表明了为什么在安全关键代码中首选 strnlen。缓冲区溢出可能导致 C 程序出现严重漏洞。

处理用户输入

此示例演示了使用 strnlen 安全地处理用户输入。

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

#define MAX_INPUT 100

int main() {
    char input[MAX_INPUT + 1];
    
    printf("Enter a string: ");
    fgets(input, sizeof(input), stdin);
    
    // Remove newline if present
    input[strcspn(input, "\n")] = '\0';
    
    size_t len = strnlen(input, MAX_INPUT);

    printf("You entered: %s\n", input);
    printf("Length: %zu\n", len);

    return 0;
}

此代码使用 fgetsstrnlen 安全地处理用户输入。输入受到 MAX_INPUT 的限制,可防止缓冲区溢出。strnlen 确保长度计算尊重此限制。此模式推荐用于在安全应用程序中处理不可信的输入。

使用固定大小的缓冲区

此示例显示了 strnlen 与固定大小的网络缓冲区一起使用。

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

#define PACKET_SIZE 256

void process_packet(const char *packet) {
    size_t len = strnlen(packet, PACKET_SIZE);
    
    if (len == PACKET_SIZE) {
        printf("Warning: Packet may be truncated\n");
    }
    
    printf("Processing packet of length %zu\n", len);
    // Process packet contents...
}

int main() {
    char packet1[PACKET_SIZE] = "Normal packet";
    char packet2[PACKET_SIZE];
    memset(packet2, 'A', PACKET_SIZE); // No null terminator
    
    process_packet(packet1);
    process_packet(packet2);

    return 0;
}

此示例使用固定大小的缓冲区模拟网络数据包处理。strnlen 可安全地处理具有正确终止符和未终止符的数据包。当长度等于缓冲区大小时,表示可能已截断。这对于网络编程至关重要,因为在那里无法假定数据包的完整性。

使用 strnlen 的最佳实践

来源

C strnlen 文档

本教程探讨了 strnlen 函数,从基本用法到安全注意事项。在 C 中,请始终优先使用有界字符串操作,以防止程序中的缓冲区溢出和未定义行为。

作者

我叫 Jan Bodnar,是一名敬业的程序员,对编码充满热情。自 2007 年以来,我通过 1,400 多篇文章和 8 本电子书分享我的专业知识。凭借十多年的教学经验,我努力使编程变得易于理解且引人入胜。

列表 C 标准库