大家新年好呀,过年这段时间太忙了,一直没有写文章,这两天才想起来应该搞搞学习了,就把年前写的atm项目中遇到的一些问题整理出来,供需要的人参考学习,也供自己回顾温习。
1. void*指针类型
一开始学习C语言时,对void*
这个类型没有过多的了解与关注,只知道是"没有类型的指针",没有用过,在写atm这个项目的时候,才真正去了解它。
void*
类型是一个指向未知类型的指针,不能直接用于访问指向的数据。
其实,各种类型之间没有本质的区别,void*
也同样,只是解释内存中的数据方式不同,int*
指向的内存中存储着数字,char*
指向的内存中存储着字符串。如果需要转换为特定的数据类型,需要根据具体的数据类型进行强制类型转换,才能正确的访问和操作数据。但需要注意的是,在进行void*
向特定类型的数据转换时,需要确保原始的void*
指针指向的内存确实包含了有效的转换指针数据,否则可能会导致未定义行行为。
- 比如,
void*
类型转换为char*
类型,确保void*转化时指向的内存中存储的就是char
类型数据即可。 - 特定类型的数据也可以转换为
void*
类型。因为void*
是一种通用的指针类型,可以接受任何类型的指针。比如,char*
类型转换为void*
类型,只需要强制转换,插入类型转换为void*
类型不会丢失任何信息,只需要确保在转换之前正确地分配内存。
在很多通用的函数接口中,给的都是参数类型都是void*。
比如:void* memcpy(void* dest,const void* src,size_t n);
其中,dest
是指向目标内存地址的指针,src
是指向源内存地址的指针,n
是要被复制的字节数。这个函数返回一个指向目标内存地址的指针。使用memcpy
函数可以方便的将一段内存中的数据复制到另一段内存中,而不需要手动逐个字节地复制。
这样设计的目的是,因为你不知道用户的数据类型是什么 ,但是你必须能够,处理用户的各种数据类型,所以会使用void*
,void*
能包容的接受各种类型的指针。如果你期望接口能够接受任何类型的参数,你可以使用void*
类型,但在具体使用的时候,你必须转换为具体的指针类型。
在使用void*
需要特别注意的是,你必须清楚原始传入的是什么类型,然后转换成对应的类型。
void*
很强大,但是一定要在合适的时候使用;
2. 读取/写入位置发生访问冲突
遇到 C/C++程序运行时提示"读取/写入位置发生访问冲突"。
产生原因:一般都是由于发生异常处的代码中,涉及到数据的读取或写入,并且访问数据时使用的是指针,而该指针并未得到合适的初始化,导致其所指向内存为NULL。
解决方法:
- 写入冲突:初始化变量;
- 读取冲突:输出类型写正确;
3. while循环中有Switch,如何跳出while循环
3.1. while循环用boolean变量控制
switch
中的break
只能终止switch
循环,无法终止while
循环,如果将break
改为return
,虽然能终止循环,但是也会用力过猛,整个方法都会终止。
如何做到精确的终止掉当前while循环,我们可以在外面定义一个boolean
变量flag
来控制while
循环,通过改变flag
的值来控制while
循环。
如下是atm系统中"修改用户信息"函数的部分代码:
arduino
//修改用户信息
void updateInfo(){
int i=0;
int flag = 1;
Customer *user = (Customer*)getData(hashmap,custCurrent->accountCard);
while(flag){
scanf("%d",&i);
switch(i){
case 1:printf("请输入账户名称:\n");scanf("%s", &user->accountName);break;
case 2:printf("请输入电话号码:\n");scanf("%s", &user->mobile);break;
case 3:printf("请输入要修改的密码:\n");scanf("%s", &user->password);break;
case 0:flag = 0;break;
default:printf("输入有误,请输入对应的值");
}
}
printf("修改用户信息成功! ");
return;
}
在修改用户信息函数中,一开始我并没有使用flag
变量,导致循环总是出现提前终止或陷入死循环的问题,后面巧妙的使用了一个flag
变量去控制循环的终止就可以完美的实现啦。
4. static关键字
4.1. 修饰局部变量
使用static
修饰的局部变量不会在函数重新进入时再次赋初值 ,不会在函数结束时而释放(存储在全局区),也不会在循环中多次赋初值。这种变量的作用域为局部作用域,当定义它的函数结束时,其作用域随之结束。使用static
修饰的局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
4.2. 修饰全局变量
使用static
修饰的全局变量仅对当前文件可见 ,其他文件不可访问,其他文件可以定义与其同名的变量,两者相互不影响。在定义不需要与其他文件共享全局变量时,加上static
关键字能够有效地降低程序模块化之间的耦合,避免不同文件同名变量的冲突,且不会误使用。
4.3. 修饰函数
使用static
修饰的函数只能在本文件中调用 ,不能在其他文件中调用。这种函数没有this
指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。出现在类体外的函数定义不能指定关键字static
。