学习C(十四)

段错误的概念

计算机为了防止某一些程序针对地址的操作,可能会引发系统的崩溃而做出的保护性指令,从而在有问题的代码处发出段错误型号,程序接收到段错误信号后,就会直接结束并显示段错误,核心已转储。
  引发段错误的原因:
    ① 访问了空指针上的值
    ② 修改了野指针地址上的值
    ③ 通过指针修改一个常量的值

#include<stdio.h>
//编译通过但是产生段错误的原因
/*
	常量指针:指向的地址上的值不一定是常量,所以他可以通过其他普通指针去修改地址上的值。
	而指向常量的指针,地址上的值肯定是个常量,没有任何指针可以去改变他,如果改变则段错误

	常量指针无量指向哪里,编译器会对该指针指向的数据做上强制保护措施,一旦试图修改,则编译错误
	而指向常量的指针,编译器不会得知该指针的指向,所以即使修改了,编译阶段不会发现,但是运行阶段发现错误后,直接段错误
*/
int main(){
	int a = 5;
	int* pa = NULL;
	*pa = a;//① 访问了空指针上的值,段错误
	
	int* pb;
	*pb = 15;//② 修改了野指针地址上的值,段错误

	char* str = "hello";//指向常量的指针
	*str = 'H';//③ 指针修改了一个常量的值,段错误
	
	return 0;
}

常量指针与指针常量

常量指针:
  常量指针:他是一个指针,什么样的指针?指向常量地址的指针。这个指针不是常量,所以指针可以随意更改指向,但是指针指向的内容无法改变
  注意:常量指针指向的数据不一定是一个常量,只不过不能通过该常量指针修改地址上的值
指针常量:
  指针常量:指针本身是一个常量。(指针的全称:指针变量)。指针指向的内容可以改变,但是指针的指向无法改变
  常量的表示方式:使用const修饰的数据就会变成常量
  常量指针p:const char* p
  指针常量p:char* const p

#include<stdio.h>

int main(){
	int a = 5;
	int b = 10;
	const int* pa = &a;//常量指针,指针指向的数据不能改变
                     //(简单理解就是这个常量指针只有读的功能)
	int* const pb = &b;//指针常量,指针的指向不能改变
                    //(简单理解就是这个指针不能移动,只能固定地指向一块内存)
	int* ppa = &a;//一般的指针变量
	*ppa = 10;//ppa为一般指针变量,可通过*ppa修改所指向的地址里的数据

    //常量指针可以移动,就是数据不能改
	pa = &b;
    //*pa = 20;//常量指针pa无法通过*pa来修改数据

    //指针常量可以改数据,就是不能移动指向
	*pb = a;
    //pb = &a;//不能移动pb的指向

	return 0;
}

输入的合法性

现在给到的所有标准输入函数,都是能够输入任意数据。
  很多时候,在拥有选项的情况下,选项外的输入其实是不合法的,这种情况通常使用default来处理。
  但是,我们有更好的合法输入方式:
  就是说,程序中设定好允许输入的数据的范围,当我输入的数据在这个范围之内,则输出到屏幕上
  分析:输出到屏幕上有2个过程
    ①单纯的输入到屏幕上
    ②将输出的数据保存到字符数组中
  为什么是这两个过程:
  因为要做到选择性的数据输出,只能将标准输入流的显示关闭,然后当键盘输入的内容符合设定数据范围,那么通过printf向终端输出刚才输入的内容。为了达到输入一个数据就判断一次是否输入,还需要将标准输入流的缓存关闭。
    缓存开启的时候:键盘输入数据,所有输入的数据都在缓存内,只有当输入回车后,缓存内的所有数据才会进入程序进行处理
    缓存关闭的时候;键盘输入的任何数据,一旦输入,就会直接进入程序进行处理
  既然如此,输入的数据就会变成若干个单一的字符,为了让这些输入的字符形成一个字符串,则需要每一次输入后,将输入的数据存储到字符数组中
  以上那么多要求具体形容就是:请输入密码,要求密码只能是英文字母或者数字构成。如果输入?则不显示和不存入数组
如果显示了‘?’却不存入数组,则和用户的直观体验相违背
如果不显示‘?’却存入了数组,则保存的密码是错误

密码隐藏

/*
	编写一个输入密码的函数,要求密码只能由英文字母和数组构成,并且在书函数中输出刚才输入的密码

	要求输入的密码用*隐藏,然后要求输入密码错误的情况下,可以按backspace回删密码
*/
#include<stdio.h>
#include<unistd.h>
#include<termios.h>
#include<assert.h>
#include<string.h>

//函数功能:从终端获取一个字符,并且返回这个字符
char getch(){  
		char c=0; 
		struct termios org_opts;//形容终端的结构体,这个结构体中能够完善的描述终端所有属性
		struct termios new_opts;
		int res=0;  
		res=tcgetattr(STDIN_FILENO, &org_opts);
		//获取终端所有数据信息后,存入org_opts中
		assert(res==0);
		memcpy(&new_opts, &org_opts, sizeof(org_opts));
		//将org_opts中所有内容拷贝给new_opts
		new_opts.c_lflag &=~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT     | ECHOKE | ICRNL); 
		//将new_opts中关于终端的缓存以及显示全都关闭
		tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
		//使用更改后的new_opts去设置标注输入流
		c=getchar();  
		res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
		//使用原来的终端信息恢复当前终端
		assert(res==0);  
		return c;  
}

void getpswd(char* pswd){
	char ch = 0;
	int i = 0;
	while(1){
		ch = getch();
		if(ch=='\n'){
			pswd[i] = 0;//不接收回车
			printf("\n");
			break;
		}else if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')||(ch>='0'&&ch<='9')){
			printf("*");
			fflush(stdout);//清空输出缓冲区,并把缓冲区内容输出。
			pswd[i] = ch;
			i++;
		}else if(ch == 127 && i>0){
			printf("\b \b");
			i--;
		}
	}
}

int main(){
	char pswd[50] = {0};
	getpswd(pswd);
	printf("%s\n",pswd);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值