当前位置: 首页 > news >正文

漫谈:C、C++字符串的困局

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


        由于历史的原因,C、C++字符串是个很让程序员头疼的东西。

目录

字符不是字符串,字符数组也不是字符串

字符串是字符数组,但字符串长度不是字符数组长度

void *不是char *,但你只能定义char[]

你以为char *就是字符串,其实不是

str函数不保证结束符

概念完整性


字符不是字符串,字符数组也不是字符串

        字符串被解读为字符数组,但是又不等价于字符数组,而是带有附加的结束符的字符数组。

    char c;//这是一个字符char cc[2] = {'a','b'};//这是两个字符的字符数组,但是不是字符串char ccc[3] = { 'a','b','\0' };//这是一个正确的字符串,但strlen长度是2不是3char str[2];//这是字符数组,长度为2,准备接收字符串strcpy(str, "ab");//这个字符串strlen长度为2,但是溢出了

字符串是字符数组,但字符串长度不是字符数组长度

        结束符‘\0’也是一个字符,但是又不计算在字符串长度里面(strlen)。

void *不是char *,但你只能定义char[]

        表达原始内存我们用void*,但是你不能定义void[],你只能定义char[]。现代语言解决了这个问题,把原始内存用BYTE来表达。

你以为char *就是字符串,其实不是

        由于这种混淆,我们自己写的函数的char*参数经常要求的并不是一个字符串,并不要求一定有结束符。系统库严谨一些,原始内存用void*做参数。

    char buf[256];char const* a = "1234567";memcpy(buf, a, 2);std::cout << buf << std::endl;//这里错了,buf里面没有字符串结束符

        内存复制函数一般也用char*做参数,经常会有人犯错,发现内存复制后的内容输出的时候后面多了一些乱码,这其实就是因为这个char*后面没有结束符。用memcpy复制字符串的strlen个字符,当然会失去最后的结束符。

str函数不保证结束符

        这个是比较过分的,str函数不保证结果是合法字符串,strncpy的n小于源字符串长度就不会追加结束符,于是得到了一个非法的字符串,经常会有人忘记了这一点,而这个问题并不总是导致BUG——如果原来的内存后面恰好是NULL,或者后面不长的数据里面就出现了NULL,导致虽然多了一些乱码,程序还是继续运行的。

        微软的描述:

        所以我们习惯上总是在后面手动设置一次结束符:

strncpy(buf,source,n);
buf[n]='\0';

概念完整性

        问题出在哪里呢?问题就出在最初设计C语言的时候偷懒了,字符串不是字符数组,是一种特殊的对象,不应该用字符数组代替字符串。(当然了,我们知道C语言设计之初更多考虑的是性能,用char*表达字符串是一个合理的设计)

        这是设计的一个基本原则:最重要的是概念完整性

        概念准确,没有歧义,不特化,不弱化,这是最高境界。设计失败的系统多多少少都跟概念设计错误有关。错误的概念会诱使程序员和用户犯错。

        高级编程语言没有指针,当然也没有这些混乱。不过话说回来,又是字符串对象、又是字节数组对象,互相转换又麻烦,char *多美妙啊。


(这里是结束)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【go从入门到精通】作用域,包详解
  • Macos 部署自己的privateGpt(2024-0404)
  • python 08Pandas
  • 想做产品经理,应该选择什么专业?
  • 【汇编】_Visual Studio2019写32位汇编
  • 解码AAC裸流为PCM写入文件
  • [数据结构]双向带头循环链表制作
  • rust-tokio发布考古
  • 少儿编程 2024年3月电子学会图形化编程等级考试Scratch二级真题解析(判断题)
  • 函数重载和引用【C++】
  • Unity类银河恶魔城学习记录12-7-2 p129 Craft UI - part 2源代码
  • git lfs如何使用
  • AI-漫画推文
  • 二维数组及其内存图解
  • 云手机提供私域流量变现方案
  • 【附node操作实例】redis简明入门系列—字符串类型
  • Asm.js的简单介绍
  • js操作时间(持续更新)
  • LeetCode18.四数之和 JavaScript
  • Redis 懒删除(lazy free)简史
  • spring security oauth2 password授权模式
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • Swift 中的尾递归和蹦床
  • underscore源码剖析之整体架构
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 技术:超级实用的电脑小技巧
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 前端面试之闭包
  • 我们雇佣了一只大猴子...
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #systemverilog# 之 event region 和 timeslot 仿真调度(十)高层次视角看仿真调度事件的发生
  • (2024,RWKV-5/6,RNN,矩阵值注意力状态,数据依赖线性插值,LoRA,多语言分词器)Eagle 和 Finch
  • (汇总)os模块以及shutil模块对文件的操作
  • (七)glDrawArry绘制
  • (转) 深度模型优化性能 调参
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • @requestBody写与不写的情况
  • @我的前任是个极品 微博分析
  • [20180129]bash显示path环境变量.txt
  • [CF]Codeforces Round #551 (Div. 2)
  • [github配置] 远程访问仓库以及问题解决
  • [HDCTF 2023]Welcome To HDCTF 2023
  • [iBOT] Image BERT Pre-Training with Online Tokenizer
  • [LeetCode]—Permutations II 求全排列(有重复值)
  • [linux] git lfs install 安装lfs
  • [linux运维] 利用zabbix监控linux高危命令并发送告警(基于Zabbix 6)
  • [Machine Learning] 领域适应和迁移学习
  • [MZ test.16]P1 评测
  • [PaddlePaddle飞桨] PaddleOCR-光学字符识别-小模型部署
  • [RoarCTF 2019]Easy Calc
  • [Script]采用Python创建当前日期文件夹
  • [Web安全架构] HTTP协议
  • [第二章—Spring MVC的高级技术] 2.1Spring MVC配置的替代方案
  • [翻译]TempDB剩余空间监视与纠错