C语言第十章字符串
2021-03-27 21:25
标签:网络 现在 字符 函数 不用 internet 动态 传递 scan 上节回顾 指针:特殊的数据类型 指针变量:指向变量的指针 函数指针 可以通过解引用来引用指针变量所指向的变量的值、函数指针指向的函数 正确使用指针的两个原则: 1、一定要明确指向哪里---初始化 2、 指向的内存单元中的内容是什么 ---- 基类型 指针最重要的应用: 1、作为函数参数:指针变量作为形参,使被调函数可以访问修改主调函数中的变量 2、函数指针作为形参,可以编写通用的函数,实现不同的功能 1、字符串常量 2、字符数组和字符指针 3、字符串处理函数 4、向函数传递字符串 5、从函数返回字符串指针 ‘\0‘为转义字符,代表ascii码值为0的字符 10.2字符串的存储 C语言没有提供专门的字符串数据类型,使用字符数组和字符指针来处理字符串。 字符数组 每个元素都是字符类型的数组 如果没有字符串结束标志的话,只能算是字符数组,可能会读出后面的乱码。需要明确的加上字符串结束标志 ‘\0‘ "a" --占用两个字节,省略了一个字符串结束符‘\0‘ 字符数组的初始化 用字符常量的初始化列表对数组初始化 char str[6] = {‘c‘, ‘h‘, ‘i‘, ‘n‘, ‘a‘, ‘\0‘}; 用字符串常量直接对数组初始化 char str[6] = {"China"}; char str[6] = "China"; char str[ ] = "China"; 10.3字符指针 还可以用字符指针来指向一个字符串,如果让字符指针指向一个字符串常量 char *pStr = "Hello China"; 字符串在内存中的存储位置,字符串保存在只读的常量存储区, 常量存储区和代码段都是只读的 两个误区:一是不能认为是把字符串存到*pStr;二是不能认为把字符串存到pStr; 正确理解:定义了一个指针变量,用字符串的首地址对指针变量进行了初始化,将字符串的首地址赋值给了指针变量。 可以修改pStr的值,但是不能通过解引用方式对它指向的存储单元中的内容,为什么? 因为它指向的是字符串常量,存储在只读的常量存储区,不能改写字符串内容。 但是,如果将字符指针指向的不是一个字符串常量而是字符数组中的内容 那么,数组保存在哪里呢? 如果数组定义实在函数内,则保存在动态存储区。如果定义在了函数外部,或者定义为静态数组,字符串保存在静态存储区。 现在数组的内容可以修改,但是数组名不能修改,数组名指向数组的首地址。 *pStr = ‘W‘;----指针的解引用 等价于 str[0] = ‘W‘; 也等价于pStr[0] = ‘W‘; 怎么正确的使用和区分字符数组和字符指针?几个使用原则: 1、明确字符串指向的内容保存在哪里,常量存储区还是静态存储区 2、明确字符指针指向了哪里 10.4字符串的访问和输入/输出 char str[10]; 按字符逐个输入/输出 一般不用字符串长度控制,如i
第二种方法 按字符串整体输入/输出 scanf("%s",str);为什么不加取地址运算符,因为str是一个数组名,已经代表了地址 printf("%s",str); s格式符会判断是否有字符串结束标志 gets(str); 输入一个字符串 puts(str); 输出一个字符串 scanf输入只能输入不带空格的字符串,因为它遇到空白字符就会输入结束了。包括空格、tab、回车等 但是gets是可以输入带空格的字符串的,它是已回车结束的。 scanf和gets使用中的细微差别!! 运行下面的程序。 将scanf换成gets 再试试。 此外,这两个函数对回车符的处理也不相同。运行下面的程序:
gets将回车读走了,但是不作为字符串的一部分。输入hello world\n 第一次输出hello,被scanf读走,第二次输出world,是被gets读走 修改一下程序
getchar 是能够将空格和回车作为有效字符读走的。 再修改下程序 进一步的再修改下程序,看一下运行结果 检查scanf的返回值,检查正确是否正确读入字符。 应该在下一次提示重新输入之前将缓冲区中的非法字符读走 再进一步看下,不用getchar。 用scanf(" ")读走了留在缓冲区中的回车,这两条语句连接起来用 这里还可以将scanf(" ")换成getchar(); 再修改下,把前面的scanf换成gets,这个时候就不用对读入的回车符进行处理了,后面的scanf前面就不需要加空格了 例10.1 从键盘输入一个人名把它显示在屏幕上 修改一下程序: 再进一步修改,将scanf 改成gets 再进一步修改: 例10.3 从键盘输入一个带有空格的人名,然后在显示人名的前面显示"Hello",I said to 再进一步修改一下程序: \" ----这是一个转义字符,就是双引号 fgets---同样也是可以读取带空格的, 13章文件中会讲到,这个函数更安全,可以控制输入长度,防止发生越界 上节回顾: scanf 和gets输入字符串的区别: 1、 scanf不能输入空格,gets能够输入空格 2、对回车的处理也不一样 ,gets可以将回车读走,scanf不能将回车读走 fgets :能够限制输入字符串的长度,比gets更加安全 两个输出函数,遇到字符串结束标志就结束。 printf %s puts() 字符指针,看下面的例子 10.5字符串处理函数 5个常用的字符串处理函数 求字符串长度--strlen(字符串) 不包含字符串结束标志,指的是改字符串实际的长度。 字符串输出的长度控制,使用strlen来控制,不过这种方法也不常用,更为常用的还是使用字符串结束标志 字符串复制--strcpy(目的字符串,源字符串) 答案是不能,因为str1和str2都是地址值。 那么字符串怎么来赋值呢?要么就是编写一个函数,一个字符一个字符的赋值,要么就是使用字符串处理函数。 字符串连接--strcat(目的字符串,源字符串); 把str1连接到str2的后面,那就意味着str2要足够大 字符串比较--strcmp(字符串1,字符串2) 字符是根据ascii值来比较大小,那么字符串大小怎么比呢? 不能使用关系运算符来比较,因为代表的是地址。那么字符串比较函数是怎么比较大小的呢? 返回的是ascii值相减的结果。 例10.4 按奥运会参赛国国名在字典中的顺序对其入场次序进行排序 二维字符数组可以存储多个字符串 char name[N][10] 表示可以存储N个实际长度最大为9的字符串。 排序函数SortString() 本章补充内容:缓冲区溢出攻击 网络黑客常常针对系统和程序自身存在的漏洞,编写相应的攻击程序 其中,最常见的就是对缓冲区溢出漏洞的攻击 几乎占到了网络攻击次数的一半以上 世界上第一个缓冲区溢出攻击 internet蠕虫,曾造成全球多台网络服务器瘫痪 何谓缓冲区溢出攻击? 利用缓冲区溢出漏洞进行的攻击 易引起缓冲区溢出攻击、不安全的函数 gets()、scanf()、strcpy()等不限制字符串长度,不对数组越界进行检查和限制,导致有用的堆栈数据被覆盖,给黑客攻击以可乘之机 对缓冲区溢出漏洞进行攻击的后果。 程序运行失败,系统崩溃 精确设置一些数据,让程序执行非授权的指令,进行各种非法的操作 怎么防止和检测缓冲区溢出呢? 缓冲区溢出攻击实例 通过缓冲区溢出将密码改成了me,当然了一般系统不一定这么容易就破解,主要是让大家理解缓冲区溢出的危害。 字符串的安全输入方法----修改下程序: 但是这种方法也有弊端,就是要字符串的长度变了后,就要修改数字8,比较麻烦。 再次修改程序: fgets限制输入字符串的长度,更加灵活 前面三个不安全,但是下面的n族的相对安全,限制了处理的字符串长度。 10.6 向函数传递字符串 向函数传递字符串时 既可用字符数组作函数参数 也可用字符指针作函数参数 两种方法都属于传地址调用,传递字符串的首地址相对于传递整个字符串的内容来说,效率更高。 下面来看几个例子。尝试自己编写字符串处理函数。 1、编写字符串复制函数(用字符数组) 2、字符串拷贝---使用字符指针 例、10.6计算实际字符个数 分别用字符数组和字符指针来编写程序。 两种方式不同之处: 字符数组作为形参:通过下标来控制循环 字符指针作为形参:直接用字符指针的自增运算来遍历 例10.7编写字符串连接函数实现strcat()的功能,返回连接后的字符串首地址 看一下连接函数的功能: 首先要将目的字符串的指针移动到末尾‘\0‘位置 接下来要完成字符串复制的操作 总结一下就是两个步骤:一是将前一个字符串指针移动到末尾,二是将源字符串从第一个字符串从目的字符串的末尾开始拷贝 但是仍然并不能实现返回连接后的字符串首地址的功能,因此需要一个指针变量记录下目的字符串原来的首地址(循环开始之前)。 通过上面的例子可以知道,字符指针既可以作为函数的形参,也可以作为函数的返回值 本章小结 如果一个字符指针指向了一个字符串常量,字符串常量存储在常量存储区,是只读的,因此不能修改字符串内容 但如果指向的是一个字符数组,并且这个字符数组是在函数内部定义的,那么这个字符数组是保存在动态存储区的。但如果改字符数组是静态的,那么保存在静态存储区,但无论是动态还是静态,保存的字符串内容和指向该字符数组的指针都是可以修改的(修改字符指针是修改的字符数组的地址)。所以一定要明确两条(1和2): 1、明确字符串被保存到了哪里 2、明确字符指针指向了哪里 指向字符串常量的字符指针 指向字符数组的字符指针 3、向函数传递字符串 向函数传递字符数组 向函数传递字符指针 字符数组和字符指针都能够达到向函数传递字符串的目的,但是传递的不是字符串的内容,而是字符串的首地址,采用的是传地址调用的方法,被调函数通过字符串的地址来间接访问字符串的内容,还可以从函数返回字符串的地址,只要将返回值类型定义为字符指针类型。 4、字符串处理函数 拷贝、连接、比较、长度计算等等,不常用的还有很多,可以查手册 记住,数组名赋值给另一个数组名方式不能完成字符串拷贝(赋值的是地址) 并且,字符串比较的时候不能直接用关系运算符,但是字符的比较大小可以使用关系运算符。 C语言第十章字符串 标签:网络 现在 字符 函数 不用 internet 动态 传递 scan 原文地址:https://www.cnblogs.com/west20180522/p/13644760.html字符串
本章内容
10.1字符串常量
1 for( i=0; str[i] != ‘\0‘; i++){
2 putchar(str[i]);
3 }
4 putchar(‘\n‘);
对于这个来说,指针变量的使用意义并不大。