标签:网络 现在 字符 函数 不用 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]; 按字符逐个输入/输出
1 for( i=0; str[i] != ‘\0‘; i++){ 2 putchar(str[i]); 3 } 4 putchar(‘\n‘);
一般不用字符串长度控制,如i<10,这个字符数组的长度虽然是10,但是字符串的长度不一定是10。这是字符数组与其他数组的一个区别。
第二种方法
按字符串整体输入/输出
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、字符串处理函数
拷贝、连接、比较、长度计算等等,不常用的还有很多,可以查手册
记住,数组名赋值给另一个数组名方式不能完成字符串拷贝(赋值的是地址)
并且,字符串比较的时候不能直接用关系运算符,但是字符的比较大小可以使用关系运算符。
标签:网络 现在 字符 函数 不用 internet 动态 传递 scan
原文地址:https://www.cnblogs.com/west20180522/p/13644760.html