听翁恺老师mooc笔记(7)--字符串1
C语言中字符串的定义
如果定义一个字符数组word,并使用大括号对其初始化,如下图所示:
但是这个不是C语言的字符串,只是字符数组,不是字符串,因为不能使用字符串的方式进行计算。那么C语言的字符串长什么样呢?如下图所示:
那么上述两个定义有何区别呢?区别就是C语言字符数组初始化时的最后一个字符加入了一个特殊的东西,就是‘\0’,这个符号的意思就是整数0,这里去掉单引号和斜杠,只放0也是可以的。这个字符数组总共有7个元素,最后的这个0使得这个数组就是C语言的字符串,那么这个字符串就可以使用c语言字符串的运算。
所以,对于C语言来说,字符串是指以0结尾的一串字符,结尾0和'\0'是一样的,某些情况下会强调使用'\0',因为整数0认为是int,一般占用4个字节,而'\0'一定是一个字节。但是不可以使用'0','0'是一个字符,这个字符是ascii码中的0,\0中的0是整数0,这两个是不一样的。
字符串末尾的0有什么含义呢,0标志字符串的结束,表示字符串到此为止,接下来的就不属于该字符串的内容,但是0本身并不是字符串的一部分。再接下来对字符串进行操作的时候就可以看到这个0的作用,字符串需要一个标志来表示说字符串结束了,这样程序才能写得出来。既然0不是字符串的一部分,那么计算字符串长度的时候不包含这个0。
C语言中字符串的存在形式
在C语言中字符串是以数组的形式存在的,而进行访问(读写)的时候可以是数组也可以是指针。在上一节课说数组和指针的关系时说过,指针和数组具有天然的联系,可以以指针的形式去访问或者遍历一个数组,也可以以数组的形式去访问指针所代表的那一大片连续的地址空间。数组和指针有着这种联系,字符串在内存里的表达形式是数组,那么访问字符串时可以使用指针也可以使用数组,指针居多一些。
C语言中字符串变量
定义字符串变量有下列几种不同的写法,但是首先要理解的是字符串是数组,那么这些变量都是字符数组的变量,只不过有不同的表现形式:
char *Str = ”hello“;
char word[] = "hello";
char line[] = "hello";
char *str=“hello";表达的是一个名为str的指针,该指针指向的了一个字符数组,这个字符数组的内容是hello,char word[]="hello"很直白是说字符数组,内容是hello。char line[10]=”hello“定义了一个字符数组,字符数组的大小是10个字节,该数组初始化为hello,hello总共5个字符,这5个字符在line里占用6个字节的空间,包括最后有一个0的位置,当你在定义字符串的时候,编译器会默认在最后加一个结尾的0.
C语言中字符串常量
在C语言中使用双引号括起来的东西是字符串常量,编译器看到它就会编译成一个字符数组,放在内存的某一片存储空间中,并且编译器还会分配一个0放在该字符数组的末尾。那么如果写了两个相邻的字符串常量,结果会被自动连接起来,比如printf("hello""world"),如果两个字符串中间没有任何东西,那么c语言编译器会将这两个字符串连接起来。所以我们说C语言中字符串是以字符数组的形态存在的,可以通过数组的方式访问遍历字符串,唯一特殊的地方是字符串可以使用双引号括起来的字符串字面量来初始化字符数组。
字符串定义使用指针形式和数组形式的区别:
如果定义char *s=”hello,world“,通常我们的理解是定义了一个字符串s,这个字符串的初值是”hello,world“,既然是字符串,那么访问(读或者写)这个字符串试一下,我们说过定义一个字符串,实际上就是定义了一个字符数组,那么就可以做s[0]=‘B’,将字符串的第一个字符换为B,然后试一下输出s[0]查看是否替换成功,编译下上述代码有一个警告:
#include <stdio.h> int main(void){
char *s={"hello,world"};
s[]='B'; printf("here!s[0]=%c\n",s[]);
}
先不管警告运行发现结果出错误,printf函数完全没输出,在printf函数之前程序就错误了,那么错误肯定出在前两行代码,那么试着注释掉任意一行看是否运行成功,第一行是定义应该不能注释,那么注释掉第二行s[0]='B',再次编辑运行,没有出现错误,结果正常输出字符串首字符号”h“。
再对当前程序进行修改,新建一个整数变量i、字符串s2,给s和s2同样的初值,输出变量i、s和s2的地址:
#include <stdio.h> int main(void){
int i;
char *s = "hello,world";
//s[0]='B';
char *s2 = "hello,world";
printf("&i=%p\n",&i);
printf("&s=%p\n",&s);
printf("&s2=%p\n",&s2); printf("s =%p\n",s);
printf("s2=%p\n",s2);
printf("s[0]=%c\n",s[]);
}
编译可能还是会有警告,先不管警告,运行后发现结果如下:
&i=000000000062FE4C
&s=000000000062FE40
&s2=000000000062FE38
s =0000000000404000
s2=0000000000404000
s[0]=h
--------------------------------
Process exited after 0.01247 seconds with return value 0
请按任意键继续. . .
从结果可以看出,作为变量i、s、s2地址是相邻的,且先定义地址大,后定义的变量小。而s和s2的结果是一样的,s和&s结果不一致,变量i、s、s2地址是一个很大的数,而s和s2的值比较小,主要是因为“hello,world”在程序的代码段,地址一般比较小且所在区域一般不允许修改,如果程序修改该区域,那么操作系统会有个保护机制,会让你的代码运行错误,说你在做坏事不让你写,如果该操作系统允许你写,那么这个操作系统不够安全。
写好程序编译的时候,编译器会对编译时就已经有值(hello,world)的东西,将该值放在一个只读不能写的位置(也就是代码区),然后让你的指针指向这个值的位置,如果有两个指针的值都是这个值,那么就需要将两个指针都指向这个值的位置,也就因为允许多个指针指向这个位置,所以才不允许修改,如果允许修改s[0]为B,那么s2怎么办?
实际上,s是一个指针,初始化为指向一个字符串常量,由于这个常量在只读不可写的位置,所以实际上s是const char *s,但是由于历史的原因,编译器接收不带const的写法,或者是省略const的写法,但是试图对这个字符串常量做写入会导致严重的后果。所以如果你想要定义一个可修改的字符串,那么就应该用字符数组来定义字符串,例如:char s[] = "hello,world"。
指针形式定义字符串和数组形式定义字符串区别在什么地方呢,区别就是指针形式定义字符串时,编译后指针指向某个区域,而使用数组形式定义字符串,那么字符串就在当前位置,也就是字符串不会在地址比较小的代码区,而是就在定义当前变量的位置。为了验证这一点,在上述代码的基础上添加字符串s3:
#include <stdio.h> int main(void){
int i;
char *s = "hello,world";
char *s2 = "hello,world";
char s3[] = "hello,world";
printf("&i=%p\n",&i);
printf("&s=%p\n",&s);
printf("&s2=%p\n",&s2);
printf("&s3=%p\n",&s3);
printf("s3=%p\n",s3); printf("s =%p\n",s);
printf("s2=%p\n",s2); s3[]='B';
printf("s3[0]=%c\n",s3[]);
}
结果如下,发现s3的地址和s3是同一个位置,和i、s、s2都在同一个位置:
&i=000000000062FE4C
&s=000000000062FE40
&s2=000000000062FE38
&s3=000000000062FE20
s3=000000000062FE20
s =0000000000404000
s2=0000000000404000
s3[0]=B --------------------------------
Process exited after 0.03045 seconds with return value 0
请按任意键继续. . .
那么当程序需要一个字符串的时候,那么我们是将字符串写成数组的形式还是指针的形式呢?如果是作为数组,那么表示这个字符串就在这里,作为本地变量空间自动被释放。而如果作为指针,那么这个字符串不知道在哪里,所以通常我们使用指针定义字符串完成下列事情:1)如果需要一个只读不写的字符串;2)如果该字符串需要作为函数参数时,在学指针和数组时我们知道,将数组作为函数参数时,传递到函数内的就是指针;3)如果需要动态分配空间,那就只能使用指针了。综上所述:选择指针和数组的基本原则:如果要构造一个字符串,使用数组形式定义字符串,如果要处理一个字符串,使用指针形式定义字符串。
另外有些书上说“char*是字符串”,这种观点是不对的,字符串可以表达为char*的形式,但char*不一定是字符串,char*是指向字符的指针,可能指向的是字符的数组(就像int*一样),只有当字符所指的字符数组有结尾的/0,才能说它所指的是字符串。
听翁恺老师mooc笔记(7)--字符串1的更多相关文章
- 听翁恺老师mooc笔记(8)--字符串2
字符串的赋值 字符串的输入与输出 对C语言的基础类型,比如int.double等类型,scanf.printf有专门的格式转换,而对字符串,scanf.printf使用%s格式字符进行输入与输出.当使 ...
- 听翁恺老师mooc笔记(15)--文件的输入与输出
<>重定向 如果使用标准的printf输出,有一个比较简便的方法,可以将程序的结果写入一个文件.使用<和>符号,将程序运行结果重定向到文件中去,具体使用到的代码如下: ./te ...
- 听翁恺老师mooc笔记(14)--格式化的输入与输出
关于C语言如何做文件和底层操作: 文件操作,从根本上说,和C语言无关.这部分的内容,是教你如何使用C语言的标准库所提供的一系列函数来操作文件,最基本的最原始的文件操作.你需要理解,我们在这部分所学习的 ...
- 听翁恺老师mooc笔记(13)--类型定义和联合
typedef 虽然我们知道使用struct这个关键字定义一个结构类型,然后可以使用该结构类型定义变量.但是每次要使用的时候都需要带着struct这个关键字,那么如何摆脱这个关键字哪?C语言提供了一个 ...
- 听翁恺老师mooc笔记(2)-第一个程序--&运算符
使用devC++写hello world 第一步:文件-新建-源代码.然后输入"输出hello world"程序: 注意:写程序时关闭中文输入法,否则语句输入的分号可能会被识别为错 ...
- 听翁恺老师mooc笔记(16)--程序设计与C语言
问题1:计算机遍布生活的各个方面,若你需要一个功能可以下载APP,我们需要的大部分功能都可以找到对应的APP,如果没有可以自己写一个软件,但是很少人需要这么做,那么我们为什么学习计算机编程语言? 学习 ...
- 听翁恺老师mooc笔记(12)--结构中的结构
结构数组: 和C语言中的int,double一样,一旦我们做出一个结构类型,就可以定义这个结构类型的变量,也可以定义这个结构类型的数组.比如下面这个例子: struct date dates[100] ...
- 听翁恺老师mooc笔记(11)--结构和函数
结构作为函数参数: 声明了一个结构就有了一种自定义的数据类型,这个数据类型和int.float.double一样,int等基本类型可以作为函数的参数,那么这种个自定义的结构类型也应该可以作为函数参数, ...
- 听翁恺老师mooc笔记(10)--结构
定义结构: 在程序里,如果想要表达一个数据就需要一个变量,而每个变量又都需要一个类型,之前学过C语言中有int.double.float.char等这些基础类型,还有指针.数组等.如果你要表达的数据比 ...
随机推荐
- 通过回调函数的理解来进一步理解ajax及其注意的用法
一,再一次理解回调函数 (function($){ $.fn.shadow = function(opts){ //定义的默认的参数 var defaults = { copies: 5, opaci ...
- 消息队列mq的原理及实现方法
消息队列技术是分布式应用间交换信息的一种技术.消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走.通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置.或在继续执行前不需要等待 ...
- JavaScript设计模式(4)-桥接模式
桥接模式 在设计一个 Js API 时,可用来弱化它与使用它的类和对象之间的耦合 1. 事件监听器的回调函数 function getBeerById(id, callback) { asyncReq ...
- 【网络流24题】最长k可重区间集(费用流)
[网络流24题]最长k可重区间集(费用流) 题面 Cogs Loj 洛谷 题解 首先注意一下 这道题目里面 在Cogs上直接做就行了 洛谷和Loj上需要判断数据合法,如果\(l>r\)就要交换\ ...
- 微信小程序之实现页面缩放式侧滑效果
效果图: 实现原理:点击按钮,往需要动画的div中添加或移除拥有动画效果的class. 由于微信小程序中不能操作page这个根节点,所以,只有用一个div(view)来模仿page根节点. 1.结构 ...
- TP5 模型类和Db类的使用区别
原文:http://www.upwqy.com/details/3.html 总结 在控制器中 模型操作 get() 和 all() 只能单独使用来查询数据 想要链式操作查询数据 需要使用f ...
- Spring MVC简单原理
Spring MVC原理 针对有Java Web基础.Spring基础和Spring MVC使用经验者. 前言 目前基于Java的web后端,Spring生态应该是比较常见了.虽然现在流行前后端分离, ...
- 开启第一个Node.js的Express项目
手动创建一个Express.js的应用可大致分为以下步骤: 1.创建文件夹 a. 创建一个项目根文件夹,如helloWord b.在项目的根目录下创建项目的目录结构,依次创建{public,publi ...
- syskey——让你的电脑更加安全
我之前介绍过一个绕过系统登录密码的工具kon-boot,今天介绍的就是可以防止这个工具的方法,也能让你的电脑更加的安全. 这个方法也是我在Youtube上看见的一个方法,还是不错. 方法: win+R ...
- Unity3D 动画状态简单控制核心代码
状态机是这样的 包含静止.跑.攻击.死亡四个动画 下面是核心代码(PS:代码高亮插件出问题了,将就看一下吧..) AnimatorStateInfo stateInfo = m_ani.GetCurr ...