【C语言】 字符数组相关库函数

puts() 函数详解

函数概述

puts() 是C语言标准库中用于输出字符串的函数,其功能比简单的字符串输出更为完善,因为它会在输出字符串后自动添加换行符。

头文件#include <stdio.h>

函数原型

c 复制代码
int puts(const char *str);

参数说明

  • str:指向要输出的字符串的指针。可以是字符数组的首地址,也可以是字符串常量的首地址。

返回值

  • 成功时返回非负整数(通常是最后输出的字符的索引值加一)
  • 失败时返回EOF(通常是-1)

函数功能详解

puts()函数从参数str指定的地址开始,逐个字符输出直到遇到字符串结束符'\0'为止。输出完成后,它会自动输出一个换行符(\n),这是与printf("%s")的主要区别之一。

使用示例

c 复制代码
#include <stdio.h>

int main(void) {
    // 示例1:输出字符数组
    char str1[] = "Hello, World!";
    puts(str1);  // 输出:Hello, World!(自动换行)
    
    // 示例2:直接输出字符串常量
    puts("Welcome to C Programming!");  // 输出:Welcome to C Programming!(自动换行)
    
    // 示例3:比较puts和printf
    char str2[] = "Comparison";
    printf("%s", str2);  // 不会自动换行
    puts(str2);          // 会自动换行
    
    return 0;
}

注意事项

  1. puts()函数只能输出字符串,不能像printf()那样格式化输出
  2. 参数必须是有效的字符串(以'\0'结尾)
  3. 如果传入空指针(NULL),可能导致程序崩溃
  4. 自动添加换行符的特性使得在连续输出多行文本时代码更简洁

内部实现原理(简析)

puts()函数大致相当于以下代码:

c 复制代码
int my_puts(const char *str) {
    while (*str != '\0') {
        putchar(*str);  // 输出当前字符
        str++;          // 移动到下一个字符
    }
    putchar('\n');      // 输出换行符
    return 0;
}

gets() 函数详解

函数概述

gets() 函数用于从标准输入(通常是键盘)读取一行字符串。由于安全漏洞,在现代C语言编程中已被弃用,但了解其原理仍有教育意义。

头文件#include <stdio.h>

函数原型

c 复制代码
char *gets(char *str);

参数说明

  • str:指向存储输入字符串的字符数组的指针

返回值

  • 成功时返回str(输入的字符串)
  • 失败或遇到文件结束符(EOF)时返回NULL

函数功能详解

gets()函数从标准输入读取字符,直到遇到换行符(\n)或文件结束符(EOF)。读取的换行符会被转换为字符串结束符'\0',然后存储到str指向的内存中。

使用示例(历史用法)

c 复制代码
#include <stdio.h>

int main(void) {
    char name[50];
    
    printf("Please enter your name: ");
    gets(name);  // 危险:可能导致缓冲区溢出
    
    printf("Hello, ");
    puts(name);
    
    return 0;
}

安全问题与替代方案

安全问题

gets()函数最大的问题是无法限制输入的长度,如果用户输入的字符串长度超过目标数组的大小,会导致缓冲区溢出,这是严重的安全漏洞。

安全替代方案

  1. fgets()函数(推荐):

    c 复制代码
    #include <stdio.h>
    
    int main(void) {
        char name[50];
        
        printf("Please enter your name: ");
        fgets(name, sizeof(name), stdin);  // 安全:指定最大读取长度
        
        // 去除可能的换行符
        size_t len = strlen(name);
        if (len > 0 && name[len-1] == '\n') {
            name[len-1] = '\0';
        }
        
        printf("Hello, %s\n", name);
        return 0;
    }
  2. scanf()函数带宽度限制

    c 复制代码
    scanf("%49s", name);  // 限制最多读取49个字符

为什么gets()被废弃

  1. 无法防止缓冲区溢出攻击
  2. C11标准已将其从标准库中移除
  3. 现代编译器通常会给出警告或错误

strlen() 函数详解

函数概述

strlen() 函数用于计算字符串的长度(不包括结尾的'\0')。

头文件#include <string.h>

函数原型

c 复制代码
size_t strlen(const char *str);

参数说明

  • str:指向要计算长度的字符串的指针

返回值

  • 返回字符串的长度(size_t类型,是无符号整数)

函数功能详解

strlen()函数从str指向的地址开始,逐个字符计数,直到遇到字符串结束符'\0'为止。计数结果不包括'\0'本身。

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    // 示例1:基本使用
    char str1[] = "Hello";
    printf("Length of '%s' is %zu\n", str1, strlen(str1));  // 输出:5
    
    // 示例2:空字符串
    char str2[] = "";
    printf("Length of empty string is %zu\n", strlen(str2));  // 输出:0
    
    // 示例3:包含空格
    char str3[] = "Hello World";
    printf("Length of '%s' is %zu\n", str3, strlen(str3));  // 输出:11
    
    // 示例4:中文(注意:一个中文占多个字节)
    char str4[] = "你好";
    printf("Length of '%s' is %zu\n", str4, strlen(str4));  // 输出:6(UTF-8编码)
    
    return 0;
}

注意事项

  1. strlen()计算的是字符数(字节数),不是显示长度(对于多字节字符)
  2. 参数必须是以'\0'结尾的有效字符串
  3. 时间复杂度是O(n),需要遍历整个字符串
  4. 在循环中重复调用strlen()效率低下

性能优化技巧

c 复制代码
// 低效写法
for (int i = 0; i < strlen(str); i++) {
    // 每次循环都调用strlen(),时间复杂度O(n²)
}

// 高效写法
size_t len = strlen(str);  // 只计算一次
for (size_t i = 0; i < len; i++) {
    // 时间复杂度O(n)
}

手动实现strlen()

c 复制代码
size_t my_strlen(const char *str) {
    size_t length = 0;
    while (*str != '\0') {
        length++;
        str++;
    }
    return length;
}

strcpy() 函数

函数概述

strcpy() 用于将一个字符串复制到另一个字符串。

头文件#include <string.h>

函数原型

c 复制代码
char *strcpy(char *dest, const char *src);

参数说明

  • dest:目标字符串地址
  • src:源字符串地址

返回值 :返回dest(目标字符串的地址)

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    char src[] = "Hello, World!";
    char dest[20];
    
    strcpy(dest, src);
    printf("Source: %s\n", src);    // 输出:Hello, World!
    printf("Destination: %s\n", dest); // 输出:Hello, World!
    
    // 验证复制效果
    printf("strlen(dest) = %zu\n", strlen(dest));  // 输出:13
    
    return 0;
}

安全注意事项

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    // 危险示例:缓冲区溢出
    char small_buffer[5];
    char long_string[] = "This is a very long string";
    
    // 以下操作会导致缓冲区溢出
    // strcpy(small_buffer, long_string);  // 不要这样做!
    
    // 安全做法:确保目标缓冲区足够大
    char large_buffer[100];
    strcpy(large_buffer, long_string);  // 安全的
    
    return 0;
}

strncpy() 函数

函数概述

strncpy() 是strcpy()的安全版本,可以指定最大复制字符数。

头文件#include <string.h>

函数原型

c 复制代码
char *strncpy(char *dest, const char *src, size_t n);

参数说明

  • dest:目标字符串地址
  • src:源字符串地址
  • n:最多复制的字符数

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    char src[] = "Hello, World!";
    char dest1[10];
    char dest2[20];
    
    // 示例1:n小于源字符串长度
    strncpy(dest1, src, 5);
    dest1[5] = '\0';  // 必须手动添加结束符
    printf("dest1: %s\n", dest1);  // 输出:Hello
    
    // 示例2:n大于源字符串长度
    strncpy(dest2, src, 20);
    printf("dest2: %s\n", dest2);  // 输出:Hello, World!
    
    // 示例3:查看填充情况
    char dest3[15] = "XXXXXXXXXXXXXX";
    strncpy(dest3, src, 20);
    for (int i = 0; i < 15; i++) {
        printf("dest3[%d] = %c (ASCII: %d)\n", i, 
               dest3[i] ? dest3[i] : ' ', dest3[i]);
    }
    
    return 0;
}

重要特性

  1. 如果n小于等于src的长度,不会自动添加'\0'
  2. 如果n大于src的长度,会用'\0'填充剩余空间
  3. 总是复制正好n个字符到目标缓冲区

strcat() 函数

函数概述

strcat() 用于将一个字符串追加到另一个字符串的末尾。

头文件#include <string.h>

函数原型

c 复制代码
char *strcat(char *dest, const char *src);

参数说明

  • dest:目标字符串(必须有足够空间)
  • src:要追加的源字符串

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    char greeting[50] = "Hello, ";
    char name[] = "Alice";
    
    strcat(greeting, name);
    printf("%s\n", greeting);  // 输出:Hello, Alice
    
    // 可以连续拼接
    strcat(greeting, "! Welcome to C programming.");
    printf("%s\n", greeting);  // 输出:Hello, Alice! Welcome to C programming.
    
    return 0;
}

strncat() 函数

函数概述

strncat() 是strcat()的安全版本,可以指定最大追加字符数。

头文件#include <string.h>

函数原型

c 复制代码
char *strncat(char *dest, const char *src, size_t n);

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    char buffer[20] = "Hello";
    
    // 安全地追加字符串
    strncat(buffer, ", World!", 10);
    printf("%s\n", buffer);  // 输出:Hello, World!
    
    // 示例:限制追加长度
    char buffer2[15] = "Start";
    strncat(buffer2, " too long string", 9);  // 只追加前9个字符
    printf("%s\n", buffer2);  // 输出:Start too long
    
    return 0;
}

安全优势

与strcat()相比,strncat():

  1. 可以防止缓冲区溢出
  2. 总是确保目标字符串以'\0'结尾
  3. 即使n大于src的长度,也只会追加实际存在的字符

strcmp() 函数

函数概述

strcmp() 用于比较两个字符串。

头文件#include <string.h>

函数原型

c 复制代码
int strcmp(const char *str1, const char *str2);

返回值

  • 返回0:两个字符串相等
  • 返回负数:str1小于str2
  • 返回正数:str1大于str2

比较规则

strcmp()按字典顺序比较字符串:

  1. 逐个比较字符的ASCII值
  2. 遇到不同字符时,根据ASCII值差返回结果
  3. 如果一直相同直到'\0',则字符串相等

使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    // 示例1:相等比较
    printf("strcmp('apple', 'apple') = %d\n", strcmp("apple", "apple"));  // 0
    
    // 示例2:小于比较
    printf("strcmp('apple', 'banana') = %d\n", strcmp("apple", "banana"));  // 负数
    
    // 示例3:大于比较
    printf("strcmp('banana', 'apple') = %d\n", strcmp("banana", "apple"));  // 正数
    
    // 示例4:大小写敏感
    printf("strcmp('Apple', 'apple') = %d\n", strcmp("Apple", "apple"));  // 负数('A'=65, 'a'=97)
    
    // 示例5:实际应用
    char password[] = "secret123";
    char input[50];
    
    printf("Enter password: ");
    fgets(input, sizeof(input), stdin);
    
    // 去除换行符
    input[strcspn(input, "\n")] = '\0';
    
    if (strcmp(input, password) == 0) {
        printf("Access granted!\n");
    } else {
        printf("Access denied!\n");
    }
    
    return 0;
}

注意事项

  1. strcmp()是大小写敏感的
  2. 对于包含数字的字符串,比较的是字符而不是数值
  3. 如果要进行不区分大小写的比较,使用stricmp()或strcasecmp()

字符串结束符 \0 的重要性

\0 的作用

\0(空字符,ASCII值为0)是C语言中字符串的结束标志,具有以下重要作用:

  1. 确定字符串长度 :所有字符串处理函数都依赖\0来确定字符串的结束位置
  2. 防止内存越界 :没有\0,函数可能读取超出分配内存的数据
  3. 提高效率:不需要单独存储字符串长度

正确使用示例

c 复制代码
#include <stdio.h>
#include <string.h>

int main(void) {
    // 正确:自动添加\0
    char str1[] = "Hello";  // 实际上是'H','e','l','l','o','\0'
    
    // 正确:手动添加\0
    char str2[6];
    str2[0] = 'H';
    str2[1] = 'i';
    str2[2] = '\0';  // 必须手动添加
    
    // 错误:忘记\0
    char str3[5] = {'H', 'e', 'l', 'l', 'o'};  // 没有\0,不是有效的C字符串
    
    printf("str1: %s (length: %zu)\n", str1, strlen(str1));  // 正确
    printf("str2: %s (length: %zu)\n", str2, strlen(str2));  // 正确
    // printf("str3: %s\n", str3);  // 危险:可能越界
    
    return 0;
}

常见错误

  1. 忘记分配\0的空间

    c 复制代码
    char str[5] = "Hello";  // 错误:需要6个字节(5个字符+1个\0)
  2. 手动构建字符串忘记\0

    c 复制代码
    char str[10];
    for (int i = 0; i < 5; i++) {
        str[i] = 'A' + i;
    }
    // 忘记:str[5] = '\0';
  3. 覆盖\0导致问题

    c 复制代码
    char str[] = "Hello";
    str[2] = '\0';  // 现在str变成"He",后面的字符被忽略
相关推荐
草莓熊Lotso1 小时前
Linux 基础 IO 初步解析:从 C 库函数到系统调用,理解文件操作本质
linux·运维·服务器·c语言·数据库·c++·人工智能
微风中的麦穗7 小时前
【MATLAB】MATLAB R2025a 详细下载安装图文指南:下一代科学计算与工程仿真平台
开发语言·matlab·开发工具·工程仿真·matlab r2025a·matlab r2025·科学计算与工程仿真
2601_949146538 小时前
C语言语音通知API示例代码:基于标准C的语音接口开发与底层调用实践
c语言·开发语言
开源技术8 小时前
Python Pillow 优化,打开和保存速度最快提高14倍
开发语言·python·pillow
学嵌入式的小杨同学8 小时前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
wfeqhfxz25887828 小时前
YOLO13-C3k2-GhostDynamicConv烟雾检测算法实现与优化
人工智能·算法·计算机视觉
Aaron15889 小时前
基于RFSOC的数字射频存储技术应用分析
c语言·人工智能·驱动开发·算法·fpga开发·硬件工程·信号处理
mftang9 小时前
Python 字符串拼接成字节详解
开发语言·python
爱编码的小八嘎9 小时前
C语言对话-21.模板特化,缺省参数和其他一些有趣的事情
c语言