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的更多相关文章

  1. 听翁恺老师mooc笔记(8)--字符串2

    字符串的赋值 字符串的输入与输出 对C语言的基础类型,比如int.double等类型,scanf.printf有专门的格式转换,而对字符串,scanf.printf使用%s格式字符进行输入与输出.当使 ...

  2. 听翁恺老师mooc笔记(15)--文件的输入与输出

    <>重定向 如果使用标准的printf输出,有一个比较简便的方法,可以将程序的结果写入一个文件.使用<和>符号,将程序运行结果重定向到文件中去,具体使用到的代码如下: ./te ...

  3. 听翁恺老师mooc笔记(14)--格式化的输入与输出

    关于C语言如何做文件和底层操作: 文件操作,从根本上说,和C语言无关.这部分的内容,是教你如何使用C语言的标准库所提供的一系列函数来操作文件,最基本的最原始的文件操作.你需要理解,我们在这部分所学习的 ...

  4. 听翁恺老师mooc笔记(13)--类型定义和联合

    typedef 虽然我们知道使用struct这个关键字定义一个结构类型,然后可以使用该结构类型定义变量.但是每次要使用的时候都需要带着struct这个关键字,那么如何摆脱这个关键字哪?C语言提供了一个 ...

  5. 听翁恺老师mooc笔记(2)-第一个程序--&运算符

    使用devC++写hello world 第一步:文件-新建-源代码.然后输入"输出hello world"程序: 注意:写程序时关闭中文输入法,否则语句输入的分号可能会被识别为错 ...

  6. 听翁恺老师mooc笔记(16)--程序设计与C语言

    问题1:计算机遍布生活的各个方面,若你需要一个功能可以下载APP,我们需要的大部分功能都可以找到对应的APP,如果没有可以自己写一个软件,但是很少人需要这么做,那么我们为什么学习计算机编程语言? 学习 ...

  7. 听翁恺老师mooc笔记(12)--结构中的结构

    结构数组: 和C语言中的int,double一样,一旦我们做出一个结构类型,就可以定义这个结构类型的变量,也可以定义这个结构类型的数组.比如下面这个例子: struct date dates[100] ...

  8. 听翁恺老师mooc笔记(11)--结构和函数

    结构作为函数参数: 声明了一个结构就有了一种自定义的数据类型,这个数据类型和int.float.double一样,int等基本类型可以作为函数的参数,那么这种个自定义的结构类型也应该可以作为函数参数, ...

  9. 听翁恺老师mooc笔记(10)--结构

    定义结构: 在程序里,如果想要表达一个数据就需要一个变量,而每个变量又都需要一个类型,之前学过C语言中有int.double.float.char等这些基础类型,还有指针.数组等.如果你要表达的数据比 ...

随机推荐

  1. window.load 和$(document).ready() 区别

    1.执行时间 window.onload必须等到页面内包括图片的所有元素加载完毕后才能执行. $(document).ready()是DOM结构绘制完毕后就执行,不必等到加载完毕.2.编写个数不同 w ...

  2. 【UVa11426】GCD - Extreme (II)(莫比乌斯反演)

    [UVa11426]GCD - Extreme (II)(莫比乌斯反演) 题面 Vjudge 题解 这.. 直接套路的莫比乌斯反演 我连式子都不想写了 默认推到这里把.. 然后把\(ans\)写一下 ...

  3. [HNOI2008]水平可见直线

    按斜率排序后画个图,用单调栈维护这个半平面交 # include <bits/stdc++.h> # define IL inline # define RG register # def ...

  4. 在windows10上配置Android的环境变量

    一, 首先右击"我的计算机"或"此电脑"图标,在弹出来的下拉列表中点击"属性(R)",进入到"系统"属性面板,点击左侧的 ...

  5. Entity Framework Core 之数据库迁移

    前言 最近打算用.NET Core写一份开源的简易CMS系统,来练练手 所以又去深入研究了一下Entity Framework Core 发现其实有些细节园子里还是很少讲到. 特意整理了几个细节. 正 ...

  6. SpringMVC【校验器、统一处理异常、RESTful、拦截器】

    前言 本博文主要讲解的知识点如下: 校验器 统一处理异常 RESTful 拦截器 Validation 在我们的Struts2中,我们是继承ActionSupport来实现校验的...它有两种方式来实 ...

  7. APNS IOS 消息推送JSON格式介绍

    在开发向苹果Apns推送消息服务功能,我们需要根据Apns接受的数据格式进行推送.下面积累了我在进行apns推送时候总结的 apns服务接受的Json数据格式 示例 1: 以下负载包含哦一个简单的 a ...

  8. Lucene就是这么简单

    什么是Lucene?? Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写,它是一个全文检索引擎的架构,提供了完整的创建索引 ...

  9. 封装Jquery 合并table中任何同列数据

    封装代码: jQuery.fn.rowspan = function (colIdx) { //封装JQuery同列值相同合并小插件 return this.each(function () { va ...

  10. python学习-字符串前面添加u,r,b的含义

    引用:https://www.cnblogs.com/cq90/p/6959567.html u/U:表示unicode字符串 不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unico ...