字符串的操作

2023-05-16

一、字符串的初始化
1、定长字符数组

1>

char buf1[128] = {'a', 'b', 'c', 'd'};
printf("sizeof(buf1) = %d\n", sizeof(buf1));    //128
printf("strlen(buf1) = %d\n", strlen(buf1));    //4
printf("buf1[66] : %d\n", buf1[66]);    //0
printf("buf1 : %s----\n", buf1);    //abcd----

定义一个128长度的字符数组,只初始化前4个字符,后面字符默认都是0。

2>

char buf5[128] = "abcd";
    printf("sizeof(buf5) = %d\n", sizeof(buf5));    //128
    printf("strlen(buf5) = %d\n", strlen(buf5));    //4
    printf("buf5[66] : %d\n", buf5[66]);    //0
    printf("buf5 : %s----\n", buf5);    //abcd----
    printf("-------------------------\n");

2、不定长的字符数组

1>

char buf2[] = { 'a', 'b', 'c', 'd' };
printf("sizeof(buf2) = %d\n", sizeof(buf2));    //4
printf("strlen(buf2) = %d\n", strlen(buf2));    //16(不确定长度,直到遇到0结束符为止)
printf("buf1 : %s----\n", buf2);     //abcd烫烫烫烫abcd----

不定长字符数组不会在最后一个字符的后面自动添加0,因为打印出来会出现乱码,直到内存空间遇到一个0为止。

2>

char buf3[] = "abcd";
printf("sizeof(buf3) = %d\n", sizeof(buf3));    //5
printf("strlen(buf3) = %d\n", strlen(buf3));    //4
printf("buf1 : %s----\n", buf3);    //abcd

字符串常量会默认结尾自动带一个0结束符,因此buf3的内存空间包括字符长度加一个结束符长度。
其中内存四区图是这样的:
这里写图片描述

3、字符指针初始化

char *buf4 = "abcd";    //abcd
printf("sizeof(buf4) = %d\n", sizeof(buf4));    //4
printf("strlen(buf4) = %d\n", strlen(buf4));    //4
printf("buf1 : %s----\n", buf4);

其内存四区图:
这里写图片描述

二、通过字符数组和指针操作指针

void test2() {
    char buf[128] = "abcdefg";
    char *p = NULL;
    for (int i = 0; i < strlen(buf); i++)
        printf("%c ", buf[i]);
    printf("\n");
    p = &buf;
    for (int i = 0; i < strlen(buf); i++)
        printf("%c ", *(p + i));
    printf("\n");
    for (int i = 0; i < strlen(buf); i++)
        printf("%c ", *(buf + i));
    printf("\n");
}

输出结果:

这里写图片描述

通过数组下标的方式本质是指针操作的方式是一致的,只不过数组下标的方式更符合程序员的编程习惯:

buf[i] ==》 buf[0 + i] ==》  *(buf + i)

编译器做的工作就是这样的。

buf相当于是一个常量指针,实质是一个内存首地址,不能改变buf指向的内存首地址。也就是说不能进行buf++;这种操作:

这里写图片描述

因为出了函数,系统要释放buf的内存空间,是根据buf指向的首地址即buf的空间大小进行释放的,如果buf可以向上++,改变其指向,比如buf现在指向字符‘e’所在的内存首地址,那么abcd到时候就无法释放内存了,因此,干脆不让修改buf的指向,这也是程序设计的原因,为了保证内存的正确释放。

这也是内存首地址(buf)和普通指针(p)的区别

普通指针可以进行下面的操作:

p = p + i;

内存首地址不可以。

三、字符串一级指针内存模型

void test3() {
    char buf1[20] = "aaaa";
    char buf2[] = "bbbb";
    char *p1 = "1111111";
    char *p2 = (char*)malloc(100);
    strcpy(p2, "33333");
}

内存模型图:

这里写图片描述

strcpy的过程就是把全局区的”33333”拷贝到堆区。

四、字符串的copy操作演变

1、

void copy1() {
    char a[] = "i am a student";
    char buf[64];
    int i = 0;
    for (i = 0; a[i] != '\0'; i++)
        buf[i] = a[i];
    buf[i] = '\0';
    printf("a : %s\n", a);
    printf("buf : %s\n", buf);

}

输出结果:

这里写图片描述

这里是最简单的遍历拷贝,只需要注意:由于循环中当a[i] = ‘\0’时跳出循环,故buf并没有把‘\0’烤进去,要手动把buf最后一个字符后面的字符设为0,否则打印buf时,会直到遇到’\0’才会停止打印,缺省

buf[i] = '\0';

这句的输出结果为:

这里写图片描述

便会出现乱码。

2、下面都是通过接口实现的,正式公司编写代码中需求的

void copy2(char *from, char *to) {
    for (; *from != '\0'; from++, to++) {
        *to = *from;
    }
    *to = '\0';
}
int main(){
    char *from = "abcdefg";
    char buf[64];
    copy2(from, buf);
    printf("buf : %s\n", buf);
    system("pause");
    return 0;
}

输出结果:

这里写图片描述

这里用到指针间接修改实参的应用,画一个内存四区图:

这里写图片描述

开始main函数和copy2函数中的from指针都指向全局区“abcdefg”的首地址,to指针指向main函数buf的首地址,循环过程中,copy2函数中的from和to的指向不断改变,直到from所指向的内存中存放的是0位置,这个时候跳出循环,并没有把0拷贝进to所指向的内存中,因此仍然要手动加入0表示字符串结束。

3、

void copy3(char *from, char *to) {
    for (; *from != '\0';) {
        *to++ = *from++;
    }
    *to = '\0';
}
int main(){
    char *from = "abcdefg";
    char buf[64];
    copy3(from, buf);
    printf("buf : %s\n", buf);
    system("pause");
    return 0;
}

输出结果同上,这里把赋值和++操作合体了:

*to++ = *from++;

++操作的优先级大于*操作符,因此先执行++操作,但由于是后++,故上面实际执行顺序是:

*to = *from;
from++;
to++;

4、继续演变

void copy4(char *from, char *to) {
    while ((*to = *from) != '\0') {
        from++;
        to++;
    }
}
int main(){
    char *from = "abcdefg";
    char buf[64];
    copy4(from, buf);
    printf("buf : %s\n", buf);
    system("pause");
    return 0;
}

输出结果还是同上,此时就不需要手动添加结束符了,while循环中会自动添加。

5、继续演变

void copy5(char *from, char *to) {
    while (*to++ = *from++) {
    }
}
int main(){
    char *from = "abcdefg";
    char buf[64];
    copy5(from, buf);
    printf("buf : %s\n", buf);
    system("pause");
    return 0;
}

输出结果仍然同上。

可以看到代码越来越简洁,这就是整个字符串copy的演变过程。

五、字符串的copy操作强化

1、不能往NULL内存空间中拷贝

void copy5(char *from, char *to) {
    while (*to++ = *from++) {
    }
}

int main(){
    char *from = "abcdefg";
    char buf[64];
    {
        char* to = NULL;
        copy5(from, to);
    }
    system("pause");
    return 0;
}

执行这一段代码,会报错:

这里写图片描述

NULL主要用来避免野指针问题,NULL内存空间是由系统来保护的内存空间,是不允许往里面拷贝任何东西的。所以一旦执行:

*to = *from;

这样的拷贝语句,程序就会down掉。

此时就要对代码进行优化:

int copy6(char *from, char *to) {
    if (to == NULL || from == NULL)
        return - 1;
    while (*to++ = *from++) {
    }

    return 0;
}
int main(){
    int ret = 0;
    char *from = "abcdefg";
    char buf[64];
    {
        char* to = NULL;
        ret = copy6(from, to);
        if (ret != 0)
            printf("func copy6 err: %d\n", ret);
    }
    system("pause");
    return 0;
}

输出结果:

这里写图片描述

要对传来的指针进行判断,判断是否指向NULL,并通过函数返回值告诉被调用函数是否执行成功。

2、函数调用中通过指针遍历时,借助中间指针变量

对于没有中间指针变量的函数:

int copy7_err(char *from, char *to) {
    if (to == NULL || from == NULL)
        return -1;
    while (*to++ = *from++) {
    }
    printf("from : %s\n", from);
    return 0;
}
int main(){
    int ret = 0;
    char *from = "abcd";
    char buf[64];
    printf("copy7 begin\n");
    copy7_err(from, buf);
    printf("copy7 end\n");

    system("pause");
    return 0;
}

输出结果:
这里写图片描述

可以看到打印from什么都没有,在函数copy7_err中不断改变from的指向,最终指向了0,必然打印的时候打印0,就会什么都没有,这就造成有些时候在函数后面需要打印from的情况,这时候就需要中间指针变量:

int copy7_good(char *from, char *to) {
    if (to == NULL || from == NULL)
        return -1;
    char *fromtemp = from;
    char *totemp = to;
    while (*totemp++ = *fromtemp++) {
    }
    printf("from : %s\n", from);
    return 0;
}
int main(){
    int ret = 0;
    char *from = "abcd";
    char buf[64];
    printf("copy7 begin\n");
    copy7_good(from, buf);
    printf("copy7 end\n");

    system("pause");
    return 0;
}

输出结果:

这里写图片描述

借助中间指针变量,到时候打印from就不会出现问题,from仍然还是指向内存首地址,没有改变指向。这是编程过程中经常出现的错误,切记,切记。

六、字符串项目开发模型

1、字符串查找(strstr)

int strstr_interface(char* mystr /*in*/, char* substr /*in*/, int *count /*out*/) {
    int ret = 0;
    if (mystr == NULL || substr == NULL || substr == NULL) {
        ret = -1;
        printf("func strstr_interface() mystr == NULL || substr == NULL || substr == NULL err : %d\n", ret);
        return ret;
    }
    char *mystrtemp = mystr;
    int counttemp = 0;
    while (mystrtemp = strstr(mystrtemp, substr)) {
        mystrtemp += strlen(substr);
        counttemp++;
        if (counttemp == '\0')
            break;
    }
    *count = counttemp;
    return ret;
}
int main(){
    int ret = 0;
    char* mystr = "11abcd2117732abcd093902shabcd239sdfjajqqabcd";
    char* substr = "abcd";
    int count = 0;
    ret = strstr_interface(mystr, substr, &count);
    if (ret != 0) {
        printf("func strstr_interface() err: %d\n", ret);
    }
    printf("count : %d\n", count);
    system("pause");
    return 0;
}

输出结果为:

这里写图片描述

strstr函数实现子字符串在母字符串中出现的第一个位置,这段代码的精髓不在于如何实现,而是如何实现健壮的代码

1、count不作为返回值返回给调用函数,而是作为形参传递值,充分利用指针间接赋值的特性(正是指针可以实现返回多个参数的情况,利用形参进行返回)

2、要对传入的指针判断是否为空,检测错误情况,并将错误返回给调用函数;

未完,待续!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字符串的操作 的相关文章

  • ubuntu安装cmake的三种方法(超方便!)

    Ubuntu安装cmake的三种方法 xff08 超方便 xff01 xff09 第一种方法 xff08 不推荐 xff09 直接使用apt安装 xff0c 但是安装的版本很老 xff0c 不推荐这种方法 span class token
  • ubuntu中eigen库的安装(所有历史版本均可安装)

    ubuntu中eigen库的安装 所有历史版本均可安装 xff09 目前官网以及一些文章给出的wget网址出现错误 xff0c 下载不了 The requested repository either does not exist or y
  • Ubuntu安装opencv及问题解决(所有版本通用)

    Ubuntu安装opencv及问题解决 xff08 所有版本通用 xff09 安装编译 获取源码 span class token function git span clone https github com opencv opencv
  • 华为云端服务器使用ModelArts跑MindSpore框架Yolov5

    华为云端服务器使用Modlearts跑MindSpore框架Yolov5 环境准备 云平台登录 xff0c 用于购买服务器https uconsole xckpjs com 我这里用的是昇腾Ascend 910点击ModelArts打开控制
  • git 环境搭建以及常用指令笔记

    目录 1 Git 环境搭建1 1 Git 配置正常显示中文文件名 2 Git 常用指令2 1 Git 生成差异文件补丁包2 2 Git 移除未纳入跟踪的文件2 3 Git 选择性合并分支 1 Git 环境搭建 1 1 Git 配置正常显示中
  • ROS命令 catkin_init_workspace 分析

    ros版本 xff1a Ubuntu18 04 Melodic 之前在调试ros工作空间环境变量的问题的时候修改了工作空间src文件夹下的CMakeLists txt以进行打印消息分析出错的原因 在我新建另外一个工作空间 xff0c 执行c
  • BundleFusion复现手册——Win10+VS2013+Cuda8.0+KinectV2实时重建

    文章目录 一 摘要二 操作环境三 工程环境配置3 1 装VS2013和CUDA8 03 2 DirectX SDK 安装3 3 Kinect SDK 2 0 四 VS2013代码生成 xff08 以离线数据为例 xff09 4 1 依赖配置
  • C++基础入门

    C 43 43 基础入门 C 43 43 初识HelloWord变量 常量和关键字 数据类型整型实型 xff08 浮点型 xff09 字符串 C 43 43 初识 HelloWord 打开Visual Studio xff0c 创建新项目
  • 二(两)视图多视图点云配准

    二视图多视图点云配准综述 多视图视图点云配准多视图点云粗配准基于生成树的粗配准基于形状生成的多视图粗配准算法 多视图点云精配准 点云配准 xff08 Point Cloud Registration xff09 是将两个或多个点云数据集对齐
  • 点云传统滤波算法

    点云滤波 分割功能滤波直通滤波条件滤波提取索引滤波 下采样类滤波体素滤波均匀采样滤波 去除噪声点类滤波统计滤波半径滤波高斯滤波双边滤波随机采样一致滤波投影滤波 本文介绍了各种滤波算法的原理以及给出了相关实现方法 分割功能滤波 分割功能滤波是
  • K-d树和八叉树

    K d树 xff1a 解决KNN近邻问题查找效率 对于一个k维的超平面 xff0c 在KD树每一层的构建中都选择一个维度来进行划分 xff0c 将k维的数据空间分为两部分 xff0c 并使其尽量平衡 然后如此递归下去 也就是说假如我们要储存
  • C++突击面试

    目录 1 编译内存相关 1 1 C 43 43 程序编译过程 1 2 C 43 43 内存管理 1 3 栈和堆的区别 1 4 变量的区别 1 5 全局变量定义在头文件中有什么问题 xff1f 1 6 内存对齐 1 7 什么是内存泄露 1 8
  • 自动驾驶 规划综述

    Motion Planning What is motion planning 规划的本质是 xff1a 搜索问题 好 的规划就是一个目标函数 xff1a 求最优解 Motion Planning的三个领域 Robotic Fields 生
  • 数值最优化

    书 xff1a Numerical Optimization 文档 xff1a 安全验证 知乎 知乎 xff0c 中文互联网高质量的问答社区和创作者聚集的原创内容平台 xff0c 于 2011 年 1 月正式上线 xff0c 以 让人们更好
  • Qlist的用法

    QList是一种表示链表的模板类 QList是Qt的一种泛型容器类 它以链表方式存储一组值 xff0c 并能对这组数据进行快速索引 xff0c 还提供了快速插入和删除等操作 QList QLinkedList和QVector提供的操作极其相
  • QT QTableView 获取某行的所有信息

    1 信号槽 显示信息 connect ui gt tableView SIGNAL clicked QModelIndex this SLOT show list 2 获取当前行的行号 int row 61 ui gt tableView
  • ubuntu 开发环境配置记录

    目录 1 VIM及zsh配置记录2 Ubuntu 配置FTP服务器3 Ubuntu 配置SSH服务器4 Ubuntu 更换软件源 1 VIM及zsh配置记录 xvim tools rar 是别人已经搭建好的环境 xff0c 下文仅为对其配置
  • CMakeLists.txt 编辑语法学习

    已hello cpp为源文件 构建一个CMakeLists txt 1 2 3 cmake minimum required VERSION 2 8 project hello add executable hello hello cpp
  • CentOS yum有时出现“Could not retrieve mirrorlist ”的解决办法——resolv.conf的配置

    错误如下 xff1a root 64 localhost svn yum install gcc c 43 43 x86 64 已加载插件 xff1a fastestmirror langpacks Could span class hlj
  • JSP九大内置对象的作用和用法总结

    JSP全名为Java Server Pages xff0c 中文名叫java服务器页面 JSP中一共预先定义了9个这样的对象 xff0c 分别为 xff1a request response session application out

随机推荐

  • 搭建vscode+vue环境

    一 安装vue js 1 简介 Vue js xff08 读音 vju 类似于 view xff09 是一套构建用户界面的 渐进式框架 与其他重量级框架不同的是 xff0c Vue 采用自底向上增量开发的设计 Vue 的核心库只关注视图层
  • 2G、3G、4G网络制式上下行频率、速率比较

    本文介绍第二代 xff08 2G xff09 移动通信系统和第三代 xff08 3G xff09 移动通信系统常见制式基础知识 xff0c 主要有2G xff08 GSM900 EGSM DCS1800 PCS1800 xff09 3G x
  • 23种设计模式UML图

  • lvs负载均衡基础知识梳理

    https www cnblogs com kevingrace p 6137975 html
  • 微内核和宏内核

    https zhuanlan zhihu com p 53612117
  • ARMv7-A/R/M系列 --- 简介

    1 简介在ARM11之后的处理器家族 xff0c 改采Cortex命名 xff0c 并针对高 中 低阶分别划分为A R M三大处理器 像是高阶手机用的Coretex A系列 xff0c 或者是微控制器所使用的Coretex M系列 xff0
  • ubuntu-C++Demo处理数据的一些小知识点

  • enum类型变量的使用和赋值

    enum 是枚举型 union 是共用体 xff0c 成员共用一个变量缓冲区 现在基本已经不用了 枚举类型 在实际问题中 xff0c 有些变量的取值被限定在一个有限的范围内 例如 xff0c 一个星期内只有七天 xff0c 一年只有十二个月
  • 单总线和多总线的区别

    在计算机系统的硬件组成中 xff0c 总线 xff08 Bus xff09 是不可缺少的一部分 xff0c 将各大基本部件按照一定的方式链接起来就构成了计算机硬件系统 就目前来讲 xff0c 许多微型计算机的各大部件之间都是用总线链接起来的
  • Win7系统下怎么扩大C盘容量 合并磁盘分

    C磁盘空间总是不够用 有没有想过把磁盘扩大点呢 下面小编就教你个办法 xff0c 把其他盘的空间转换成C盘的容量 xff0c 这要怎么弄呢 其实挺简单的 方法如下 xff1a 1 首先从电脑桌面左下脚开始里面进去找到 计算机 找到计算机名称
  • 操作系统与应用程序的关系

    操作系统与应用程序的关系 操作系统主要可以分为两大部分 xff1a 内核和内核之外的一些程序 内核就是直接控制最底层的硬件 xff0c 而我们日常所用到的软件 xff0c 大都是通过内核之外一些程序与内核之间的接口完成的 xff0c 例如W
  • GSM系统构成(NSS,OSS,BSS,MS)

    第一部分NSS EIR HLR AUC MSC VLR EIR Equipment identify register装备身份注册 HLR Home Location Register 归属位置寄存器 AUC Authentication
  • DSP指数编码器(EXP)工作原理

    例 xff1a 完成对累加器A的归一化处理 EXP A 多余符号位数 8 T寄存器 ST T EXPONENT 将保存在T寄存器中的指数存入指定的数据存储器中 NORM A 对累加器A进行归一化处理 40位累加器A中的定点数FF FFFF
  • VS C++调用Lua动态链接库

    这里使用的是VS2019 xff0c 其他版本类似 新建一个控制台应用 创建好工程后 xff0c 打开工程属性页 进入C C 43 43 gt 常规 gt 附加包含目录 xff0c 将lua的头文件目录包含进去 进入链接器 gt 输入 gt
  • Unity 如何判断GameObject是否为Prefab

    public static bool IsPrefabInstance UnityEngine GameObject obj var type 61 PrefabUtility GetPrefabAssetType obj var stat
  • Git 常用命令

    注册用户名和邮箱 xff1a git config global user name username git config global user email useremail 查看log的快捷定义 xff1a 在C Users 当前用
  • C++值赋值运算符重载

    C 43 43 值赋值运算符重载的格式是这样的 xff1a 类名 amp span class hljs keyword operator span xff08 span class hljs keyword const span 类名 a
  • 学习笔记:GDB 调试

    01 什么是GDB GDB 是由 GNU 软件系统社区提供的调试工具 xff0c 同 GCC 配套组成了一套完整的开发环境 xff0c GDB 是 Linux 和许多类 Unix 系统中的标准开发环境 一般来说 xff0c GDB 主要帮助
  • C++之类对象的返回与引用

    一 类对象的返回 在拷贝构造器中提到过 xff1a class span class hljs literal A span span class hljs comment span span class hljs literal A sp
  • 字符串的操作

    一 字符串的初始化 1 定长字符数组 1 gt span class hljs keyword char span buf1 span class hljs number 128 span 61 span class hljs string