C语言字符串处理相关函数详解

news2025/4/3 1:20:19

C语言字符串处理相关函数详解

  • 相关函数详解
    • 1. **字符串拷贝**:
      • `strcpy` 和 `strncpy`
    • 2. **字符串连接**:
      • `strcat` 和 `strncat`
    • 3. **字符串比较**:
      • `strcmp` 和 `strncmp`
    • 4. **字符串长度**:
      • `strlen`
    • 5. **字符串查找**:
      • `strchr` 和 `strrchr`
      • `strstr`
    • 6. **字符串切分**:
      • `strtok`
    • 7. **内存操作**(与字符串相关):
      • `memset`, `memcpy`, `memmove`
    • 8. **其他函数**:
      • `strdup`(非标准函数)
      • `strcasecmp` 和 `strncasecmp`(扩展函数)
  • 在C语言中,如何安全地使用字符串处理函数来避免缓冲区溢出等常见安全问题?
    • **1. 确保目标缓冲区大小足够**
      • **解决方案:使用长度限制的函数**
      • 示例:
    • **2. 正确终止字符串**
      • **解决方案:手动添加终止符**
      • 示例:
    • **3. 避免目标缓冲区内存不足**
      • **解决方案:始终检查缓冲区大小**
      • 示例:
    • **4. 使用更安全的函数**
    • **5. 使用动态分配内存**
      • 示例:
    • **6. 避免使用不安全的函数**
      • 示例:
    • **7. 小心使用 `strtok` (非线程安全)**
      • 示例:
    • **8. 输入验证和边界检查**
      • 示例:
    • **9. 使用静态分析工具**
    • 管理缓冲区溢出的步骤总结:

相关函数详解

在 C 语言中,字符串其实是以 \0(空字符)结尾的字符数组。C 标准库提供了许多内置函数来处理字符串,下面是常用的字符串处理相关函数的详细说明。这些函数都定义在 <string.h> 头文件中。


1. 字符串拷贝

strcpystrncpy

  • char *strcpy(char *destination, const char *source);

    • 功能:将源字符串 source 拷贝到目标字符串 destination
    • 使用注意:destination 必须有足够的空间容纳 source 字符串及其终止空字符(\0)。
    • 示例:
      char src[] = "Hello";
      char dest[10];
      strcpy(dest, src); // dest now contains "Hello"
      
  • char *strncpy(char *destination, const char *source, size_t n);

    • 功能:最多拷贝 n 个字符到 destination
    • 特点:如果 source 长度小于 n,会填充剩余部分为 \0;如果 source 长度大于 n,不会添加空字符,destination 可能不以 \0 结尾。
    • 示例:
      char src[] = "Hello";
      char dest[10];
      strncpy(dest, src, 3); // dest contains "Hel"
      

2. 字符串连接

strcatstrncat

  • char *strcat(char *destination, const char *source);

    • 功能:将 source 连接到 destination 的末尾,并自动在末尾加上 \0
    • 使用注意:destination 必须有足够的空间容纳结果字符串。
    • 示例:
      char str1[20] = "Hello ";
      char str2[] = "World!";
      strcat(str1, str2); // str1 now contains "Hello World!"
      
  • char *strncat(char *destination, const char *source, size_t n);

    • 功能:从 source 连接最多 n 个字符到 destination,并自动在末尾添加 \0
    • 示例:
      char str1[20] = "Hello ";
      char str2[] = "World!";
      strncat(str1, str2, 3); // str1 now contains "Hello Wor"
      

3. 字符串比较

strcmpstrncmp

  • int strcmp(const char *str1, const char *str2);

    • 功能:比较两个字符串 str1str2 的大小。
    • 返回值:
      • < 0str1 < str2
      • 0str1 == str2
      • > 0str1 > str2
    • 示例:
      strcmp("abc", "abc"); // returns 0
      strcmp("abc", "abd"); // returns -1
      strcmp("abd", "abc"); // returns 1
      
  • int strncmp(const char *str1, const char *str2, size_t n);

    • 功能:比较两个字符串的前 n 个字符。
    • 示例:
      strncmp("abcdef", "abcxyz", 3); // returns 0
      strncmp("abcdef", "abcxyz", 4); // returns -1
      

4. 字符串长度

strlen

  • size_t strlen(const char *str);
    • 功能:返回字符串 str 的长度(不包括 \0)。
    • 示例:
      char str[] = "Hello";
      size_t len = strlen(str); // len is 5
      

5. 字符串查找

strchrstrrchr

  • char *strchr(const char *str, int c);

    • 功能:查找字符串 str 中第一次出现字符 c 的位置。
    • 返回值:
      • 如果找到,返回指向字符 c 的指针。
      • 如果未找到,返回 NULL
    • 示例:
      char str[] = "Hello";
      char *pos = strchr(str, 'l'); // pos points to the first 'l' in "Hello"
      
  • char *strrchr(const char *str, int c);

    • 功能:查找字符串 str 中最后一次出现字符 c 的位置。
    • 示例:
      char str[] = "Hello";
      char *pos = strrchr(str, 'l'); // pos points to the last 'l' in "Hello"
      

strstr

  • char *strstr(const char *haystack, const char *needle);
    • 功能:查找子字符串 needle 在字符串 haystack 中第一次出现的位置。
    • 返回值:
      • 如果找到,返回指向子字符串的指针。
      • 如果未找到,返回 NULL
    • 示例:
      char str[] = "Hello World!";
      char *pos = strstr(str, "World"); // pos points to "World!"
      

6. 字符串切分

strtok

  • char *strtok(char *str, const char *delim);
    • 功能:将字符串 str 根据分隔符 delim 切分成多个子字符串。
    • 特点:此函数内部使用静态变量保存状态,所以多次调用同一个字符串时必须依赖它的状态。
    • 示例:
      char str[] = "Hello,World,2023";
      char *token = strtok(str, ",");
      while (token != NULL) {
          printf("%s\n", token); // Output: Hello\n World\n 2023
          token = strtok(NULL, ",");
      }
      

7. 内存操作(与字符串相关):

memset, memcpy, memmove

  • void *memset(void *ptr, int value, size_t num);

    • 功能:将内存块 ptr 开头的 num 个字节设置为 value(以字节为单位)。
    • 示例:
      char str[10];
      memset(str, 'A', 10); // str contains "AAAAAAAAAA"
      
  • void *memcpy(void *destination, const void *source, size_t num);

    • 功能:将 source 开头的 num 个字节拷贝到 destination
    • 使用注意:sourcedestination 不能重叠。
    • 示例:
      char src[] = "12345";
      char dest[10];
      memcpy(dest, src, 5); // dest contains "12345"
      
  • void *memmove(void *destination, const void *source, size_t num);

    • 功能:同 memcpy,但支持 sourcedestination 可能重叠的情况。
    • 示例:
      char str[] = "12345";
      memmove(str + 1, str, 4); // str contains "11234"
      

8. 其他函数

strdup(非标准函数)

  • char *strdup(const char *str);
    • 功能:分配内存并复制字符串 str
    • 注意:返回的指针需要用 free 释放。
    • 示例:
      char str[] = "Hello";
      char *copy = strdup(str); // copy contains "Hello"
      free(copy);
      

strcasecmpstrncasecmp(扩展函数)

  • 类似于 strcmpstrncmp,但忽略大小写比较。

以上是 C 语言中常用的字符串处理函数,使用时需要注意内存安全,特别是函数涉及目标字符串的大小,避免缓冲区溢出。同时,记得字符串必须以 \0 结尾作为结束标志。

在C语言中,如何安全地使用字符串处理函数来避免缓冲区溢出等常见安全问题?

在 C 语言中,因为字符串实际上是以空字符 \0 结尾的字符数组,错误处理字符串的方式可能会导致缓冲区溢出(Buffer Overflow)以及未定义行为等问题,从而引发安全风险。以下是一些注意事项和安全实践,帮助您在使用字符串处理函数时避免这些常见的问题:


1. 确保目标缓冲区大小足够

缓冲区溢出常发生在目标缓冲区(destination)的大小不足以容纳输入内容时,如 strcpystrcat 等函数会将整个源字符串拷贝到目标缓冲区而不进行边界检查。

解决方案:使用长度限制的函数

  • 使用有长度限制的函数,例如:
    • strncpy 替代 strcpy
    • strncat 替代 strcat
  • 始终显式声明并检查目标缓冲区的大小,确保总长度不会超过缓冲区。

示例:

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

void safe_copy() {
    char src[] = "Hello, World!";
    char dest[10]; // 缓冲区只有10个字符(不够容纳src)

    // 使用 strncpy 替代 strcpy,最多拷贝 9 个字符,留出一个位置给 '\0'
    strncpy(dest, src, sizeof(dest) - 1);
    dest[sizeof(dest) - 1] = '\0'; // 手动添加字符串结束符
    printf("Copied string: %s\n", dest); // 输出结果 "Hello, Wo"
}

2. 正确终止字符串

有些字符串函数(如 strncpy)不会自动在字符串结尾添加 NUL 字符 \0,这可能导致未终止的字符串引发未定义行为。

解决方案:手动添加终止符

确保显式添加空字符 \0,尤其是在长度限制的拷贝操作中。

示例:

char src[] = "Hello";
char dest[10];
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; // 确保字符串以 '\0' 结尾

3. 避免目标缓冲区内存不足

一些函数(例如 strcatstrcpy)假定目标缓冲区有足够的空间。如果目标缓冲区不足以容纳拼接或拷贝的内容,会导致缓冲区溢出。

解决方案:始终检查缓冲区大小

在执行操作之前,计算源字符串与目标缓冲区的大小关系,以确保不会发生溢出。

示例:

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

void safe_concat() {
    char src[] = "World!";
    char dest[20] = "Hello, ";

    // 检查 dest 是否足够大
    if (strlen(dest) + strlen(src) + 1 <= sizeof(dest)) {
        strcat(dest, src); // 安全拼接
        printf("Concatenated string: %s\n", dest);
    } else {
        printf("Buffer overflow risk avoided!\n");
    }
}

4. 使用更安全的函数

许多 C 标准库函数(如 strcpy, strcat)存在已知问题,现代 C 标准库或第三方库提供了更安全的替代函数,例如:

  • POSIX扩展函数(如 strlcpy, strlcat):
    • strlcpystrlcat 是更安全的拷贝和拼接函数,它们始终保证目标字符串是以 \0 结尾的,并不会导致缓冲区溢出。
    • 示例:
      #include <string.h> // POSIX 标准
      
      char src[] = "Hello, World!";
      char dest[10];
      
      // strlcpy: 拷贝最多 sizeof(dest) - 1 个字符,并确保以 '\0' 结尾
      strlcpy(dest, src, sizeof(dest));
      printf("Destination: %s\n", dest);
      

5. 使用动态分配内存

如果目标缓冲区的大小是未知且不可固定的,采用动态内存分配(如 mallocfree)可以避免缓冲区溢出的问题。

示例:

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

void dynamic_allocation() {
    char src[] = "Hello, dynamically allocated world!";
    char *dest = malloc(strlen(src) + 1); // 动态分配精确大小

    if (dest == NULL) {
        fprintf(stderr, "Memory allocation failed.\n");
        return;
    }

    strcpy(dest, src); // 安全拷贝
    printf("Copied string: %s\n", dest);

    free(dest); // 释放内存
}

6. 避免使用不安全的函数

一些函数完全不应该使用,特别是在处理未受信任的数据时。例如:

  • gets (已废弃 in C11):因为它完全不限制输入的大小,极易引发缓冲区溢出。
  • 替代:使用 fgets

示例:

#include <stdio.h>

void read_input() {
    char buffer[20];

    // fgets 会限制输入大小,避免缓冲区溢出
    printf("Enter a string: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        printf("You entered: %s\n", buffer);
    }
}

7. 小心使用 strtok (非线程安全)

strtok 全局维护状态,无法在多线程中安全使用。使用 strtok_r 它的线程安全版本(POSIX 提供)。

示例:

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

void tokenize() {
    char str[] = "Hello,World,2023";
    char *saveptr;
    char *token = strtok_r(str, ",", &saveptr); // 使用 strtok_r 替代 strtok
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_r(NULL, ",", &saveptr);
    }
}

8. 输入验证和边界检查

用户输入的内容可能过长或恶意,必须在读取数据时验证边界,避免安全问题。

示例:

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

void read_and_validate() {
    char buffer[50];

    // 读取用户输入,但限制长度为 sizeof(buffer) - 1
    printf("Enter input: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        // 检查并去除末尾换行符
        size_t len = strlen(buffer);
        if (len > 0 && buffer[len - 1] == '\n') {
            buffer[len - 1] = '\0';
        }
        printf("Validated input: %s\n", buffer);
    }
}

9. 使用静态分析工具

使用代码分析工具可以帮助您检测潜在的字符串处理问题,例如:

  • Valgrind:检测内存泄漏以及溢出问题。
  • Lint:静态分析工具,可发现潜在的代码问题。

管理缓冲区溢出的步骤总结:

  1. 始终显式定义缓冲区大小。
  2. 使用长度限制的函数(如 strncpy, snprintf)替代没有边界检查的函数。
  3. 确保目标字符串以 \0(空字符)结尾。
  4. 在操作之前检查缓冲区的大小是否足够。
  5. 使用动态内存分配处理长字符串。
  6. 避免使用危险、不安全的函数(如 gets)。
  7. 如果可用,首选更安全的替代函数(例如 strlcpy, strlcat)。

通过这些方法,可以减少甚至完全避免由于字符串处理引起的缓冲区溢出问题,提高代码的安全性和鲁棒性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2326135.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【深度学习新浪潮】图像修复(Image Inpainting)技术综述:定义、进展与应用展望

本文为精简版,完整技术细节与参考文献可与作者讨论。 1. 图像修复的定义与核心目标 图像修复(Image Inpainting)是一种通过算法手段填补图像中缺失区域或移除不需要对象的技术,其核心目标是利用图像上下文信息生成与周围像素一致且视觉自然的内容。该技术通过计算机视觉和…

计算机视觉——传统数字图像处理中图像去噪原理与代码实现细节

1. 概述 在现实世界中捕获的图像常常受到噪声的影响&#xff0c;这些噪声可能来源于环境因素、信号不稳定、相机传感器问题、照明条件差、电损失等多种因素。为了进一步处理这些图像并对结果进行准确解释&#xff0c;拥有尽可能低噪声的图像至关重要。图像去噪是数字图像处理中…

【STM32】最后一刷-江科大Flash闪存-学习笔记

FLASH简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过闪存存储器接口&#xff08;外设&#xff09;可以对程序存储器和选项字节进行擦除和编程&#xff0c;&#xff08;系统存储器用于存储原厂写入的BootLoader程序&#xff0c;用于串口…

JS实现动态点图酷炫效果

实现目标 分析问题 整个图主要是用canvas实现&#xff0c;其中难点是将线的长度控制在一定范围内、并且透明度随长度变化。 前置知识 canvas绘制点、线、三角形、弧形 // 点ctx.moveTo(this.x, this.y);ctx.arc(this.x, this.y, this.r,0, 2 * Math.PI, false);ctx.fillStyle …

【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】【思路篇】A题解题全流程(持续更新)

【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】A题解题全流程-思路&#xff08;持续更新&#xff09; 写在前面&#xff1a; 1、A题、C题将会持续更新&#xff0c;陆续更新发布文章 2、赛题交流咨询Q群&#xff1a;1037590285 3、全家桶依旧包含&#xff1a; 代码、…

爱普生晶体单元FC2012AN在5G RedCap中的应用

在 5G 技术向物联网领域深度渗透的今天&#xff0c;RedCap&#xff08;5G 轻量化&#xff09;作为衔接中高速物联网场景的关键技术&#xff0c;正加速推动工业、医疗、可穿戴等领域的智能化升级。爱普生 FC2012AN 低 ESR 晶体单元凭借其突破性的小尺寸、低功耗与高稳定性设计&a…

vue 两种路由模式

一、两种模式比较 在vue.js中&#xff0c;路由模式分为两种&#xff1a;hash 模式和 history 模式。这两种模式决定了URL的结构和浏览器历史记录的管理方式。 1. hash 模式带 #&#xff0c;#后面的地址变化不会引起页面的刷新。换句话说&#xff0c;hash模式不会将#后面的地址…

Java-servlet(十)使用过滤器,请求调度程序和Servlet线程(附带图谱表格更好对比理解)

Java-servlet&#xff08;十&#xff09;使用过滤器&#xff0c;请求调度程序和Servlet线程 前言一、Servlet 间通信&#xff08;了解即可&#xff09;二、Servlet 请求处理&#xff1a;getAttribute 和 getParameter 的区别与应用1.getAttribute 方法2.getParameter 方法 三、…

ue5 学习笔记 FPS游戏制作35 GameMode与GameInstance

文章目录 相似GameMode介绍声明设置生效调用 GameInstance介绍声明设置生效调用 相似 两者都用来保存公共的数据和方法 数据都在内存里&#xff0c;关闭程序后数据消失 GameMode 介绍 生命周期跟随关卡&#xff0c;关卡销毁GameMode也跟随销毁 内部可以定义属性和方法 声明…

山洪预警秒级响应-AI本地化部署在极端降雨短临预测中的技术突破。AI智能体开发与大语言模型的本地化部署、优化技术

极端降雨预测的技术痛点与边缘破局 ‌1. 传统预警系统的三重瓶颈‌ ‌延迟致命‌&#xff1a;WRF模式在1km分辨率下3小时预报耗时>45分钟&#xff0c;错过山洪黄金响应期 ‌地形干扰大‌&#xff1a;复杂地形区&#xff08;如横断山脉&#xff09;降水预测误差超50% ‌数…

矿山自动化监测解决方案

1.行业现状 为贯彻落实《中共中央国务院关于推进安全生产领域改革发展的意见》《“十四五”矿山安全生产规划》&#xff08;应急〔2022〕64号&#xff09;、《国务院安委会办公室关于加强矿山安全生产工作的紧急通知》&#xff08;安委办〔2021〕3号&#xff09;等有关工作部署…

pytorch学习(b站小土堆学习)

1 环境配置 参考链接 2. dir 和 help函数 dir()&#xff1a;用于查看某一模块函数的方法 help()&#xff1a; 用于查看某方法的使用方法 3. dataset类实战 利用Image对象打开图片&#xff0c;利用os模块的地址拼接组成图片路径 当我们用方括号访问元素对象时&#xff0c;…

【最新】探索CFD的未来:从OpenFOAM到深度学习,全面解析计算流体力学的顶级资源与前沿技术

计算流体力学(CFD)作为现代工程与科学研究的核心工具,正以前所未有的速度迈向智能化与多物理场耦合的新时代。本文全面梳理了在线学习CFD的顶级资源,涵盖了从传统数值模拟到深度学习驱动的物理信息模型的广泛领域,旨在为研究者、工程师和学生提供一站式参考指南。内容分为…

算法专题一:双指针算法(下)

书接上回 5.有效三角形个数 力扣&#xff1a; 有效三角形的个数 在做这道题前我们先讲一个数学知识&#xff1a;已知 a < b < c ,此时 ab>c 可以得出 有效三角形。 所以&#xff0c;我们做这道题时&#xff0c;可以不使用暴力算法。 可以优化为先排序&#xff…

咪咕MG101_晨星MSO9380芯片_安卓5.1.1_免拆卡刷固件包

咪咕MG101_晨星MSO9380芯片_安卓5.1.1_免拆卡刷固件包&#xff08;内有教程&#xff09; 刷机教程简单说明&#xff1a; 1、把下载好的刷机包&#xff0c;U盘里建立一个upgrade文件夹&#xff0c;固件放入此文件夹里&#xff0c;放入U盘中&#xff0c;注意升级包为压缩包不要对…

T11 TensorFlow入门实战——优化器对比实验

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習紀錄博客&#x1f356; 原作者&#xff1a;K同学啊 | 接輔導、項目定制 一、前期准备 1. 导入数据 # Import the required libraries import pathlib import matplotlib.pyplot as plt import tensorflow as t…

2023年3月全国计算机等级考试真题(二级C语言)

&#x1f600; 第1题 下列叙述中错误的是 A. 向量是线性结构 B. 非空线性结构中只有一个结点没有前件 C. 非空线性结构中只有一个结点没有后件 D. 只有一个根结点和一个叶子结点的结构必定是线性结构 概念澄清 首先&#xff0c;我们需要明确几个关键概念&#xf…

在MFC中使用Qt(四):使用属性表(Property Sheet)实现自动化Qt编译流程

前言 首先回顾下前面文章介绍的&#xff1a; 在MFC中使用Qt&#xff08;一&#xff09;&#xff1a;玩腻了MFC&#xff0c;试试在MFC中使用Qt&#xff01;&#xff08;手动配置编译Qt&#xff09; 在MFC中使用Qt&#xff08;二&#xff09;&#xff1a;实现Qt文件的自动编译流…

Python-八股总结

目录 1 python 垃圾处理机制2 yield3 python 多继承&#xff0c;两个父类有同名方法怎么办&#xff1f;4 python 多线程/多进程/协程4.1 多线程与GIL全局解释器锁4.2 多进程4.3 协程 5 乐观锁/悲观锁6 基本数据结构**1. 列表&#xff08;List&#xff09;****2. 元组&#xff0…

局域网数据同步软件,局域网数据备份的方法

局域网数据备份的方法&#xff1a; 局域网数据备份是确保数据安全性的重要措施&#xff0c;以下是一些常用的局域网数据备份方法&#xff1a; 1.使用NAS设备备份 特点&#xff1a;网络附加存储&#xff08;NAS&#xff09;设备提供了一种便捷的备份方式。 操作&#xff1a;…