1、常见问题:
main 函数,一般两种写法
(1)void mian (void) {}//不关心返回值状态,单片机开发中51,stm32,设计一个系统实现死循环,不断处理外部请求,触发事件;
单片机系统分为2个子系统,
系统启动加载程序bootloader;
系统运行主逻辑 main 死循环;c90的写法,老编译器的语法;stm32,51
(2)int main(){}//c99写法,新编译器,带os系统上的应用程序开发;
(3)int main (int argc,char *argv[]){}//带os系统上的应用程序开发,标准写法;具备了接收了用户从命令行中敲入的信息;
2、数据怎么表示(),怎么操作(运算),如何找到(指针),数据存放规则;
(1)数据存放的规则:
数据存在哪?用户的硬件资源不统一,
带操作系统的开发:()
解决方案:物理内存不一样 os上模拟一个虚拟内存的机制,让os的应用程序,只关注虚拟内存
虚拟内存 转换成物理内存 os提供了一个叫做mmu(内存管理单元)的模块来进行转换;
操作系统把虚拟内存的地址映射到物理地址不同位置,让物理地址分开访问,避免冲突
无操作系统的开发:(mcu工程师自行规划,)
系统位数:32bit 64bit:位数是操作系统提供的寻址能力,比如windows64bit操作系统,在vm(虚拟内存)的寻址能力是64bit;2^48位256T,操作系统实际只用了48位;
2^32为4gb,为了研究方便,选择2^32bit来研究
3、 linux虚拟内存分区图:
一共4g,
0xffffffff
1g os的内核空间(不可读,不可写)
-----------------------------------
0xc000000
----------------------------------------------
栈区 局部变量 函数维护 函数进入 函数返回
栈的指针始终指向待插入的位置
栈的特点:
所有函数的局部变量,都放在栈区内;
随着函数的调用的深度增多,栈的区域在不断的扩大,栈就有溢出的可能性,
函数一旦返回这个区域里的空间,这个区域就不受保护了出栈(不存在了);
堆区 程序员维护 malloc的函数来申请 靠free主动释放;
没有其他方法来释放
可以申请很大的空间
程序退出就不存在堆区;
申请了不释放则内存不足,申请一般是连续的;
--------------------------------------
数据区
(都是存放全局变量的,生命周期最长)加了static的局部变量会被放在数据区,而不是栈区;
在不同函数里定义相同的名称的局部变量,用了static后会升级其生命周期,并且不会冲突,在编译的时候会给这两个相同的变量不同的标识符;
如果函数前面放static 这个函数只在该文件中有效,如果在全局变量前加static则表示这个文件只在这个文件中可以使用;
.data区 初始化的变量
.bss区 未初始化的变量
只读数据区,rodata(常量存放的位置)字符串常量的双引号就是首地址传递;生命周期程序运行时到程序结束释放;
char *p = "hello";
printf("hello);
p[0] = 'a';
char buf[] = "hello";
c语言中,定义变量的时候,是在函数里定义的、
函数外面定义称之为全局变量
int a;//全局变量放在.bss区,自动加载为0,是一个确定值,bss(未初始化自动清空);
int main(){
int b;//未初始化的局部变量,在栈区,随机找的地址,b值不确定;
}
-------------------------------------
代码区.txt 区定义的函数就在这个区域,可读,不可写
0x08048000
---------------------------------
有一段较小的区域也是操作系统保护区不可读不可写;
0x00000000 NULL;
段错误:访问的段不合法;
4、系统不稳定:段错误,double free ;
系统逻辑出问题:数据unsigned signed
数据结构与算法:链表 AVL BST
Linux应用编程:fopen flose
知识点怎么做什么时候做为什么这么做;
例如:char *s = "hello";
s[0] = 'e';
printf(s);//段错误程序不会往后走,s指向的是常量区;
操作系统为了让应用程序只关注虚拟内存采用了内存分段管理保证了应用程序的编写更方便,为了更好管理各种属性的数据;
栈内不仅保存了函数的局部变量,也保存了函数的返回信息,如果越界访问并修改这些空间就会出现段错误(函数回调上一个函数会有一个保护现场的地址指针);
栈溢出:函数申请了太大空间,子函数调用的层级过多(递归函数);
堆区:有程序员执行malloc时产生,执行free时消失;必须用一个指针变量,把地址存入,不然就找不到了,但空间还在,被叫做内存泄露;
数据段:程序执行时诞生,程序退出时消失;全局变量,static修饰的变量
代码段:某些常量存放,比如字符串就在rodata段;只读
------------------------------------------------
函数返回地址:
1、返回一个地址,该地址不一定是局部变量
2、设计函数时,要求返回地址,不应返回一个局部变量
3、返回地址,只能返回
堆区 必须有释放(配对使用)
静态区 可以不释放(单独使用,这种函数不多(线程不安全函数))
设计一个功能
动态表 Table
Table *createTable(int n);//设计接口,返回地址
void releaseTable(Table *tab);释放该动态表结构;
函数返回的注意事项:
所有的函数的返回和输入都是拷贝:
返回原子类型(基本数据类型):
子函数里的局部变量会消失,那么返回原子类型会有什么问题吗?
返回地址类型
既然子函数里的局部变量会消失,那么返回地址,会有什么问题
堆区,静态区
函数的作用承上启下
承上:
接收上层空间,传递给他的信息
函数的输入值, 上层空间 拷贝给子函数空间
启下:
return 把子函数里的区域拷贝到上层空间
上层空间,不接收,白拷贝
如果接收,继续使用
直接变量名作为输入参数传递时,保护性访问;
如果子函数想修改上层函数空间里的值,就必须对这个要修改的空间的地址进行传递;
xxx a;
fun (a);a不变
fun(&a);50%a可以变,a的地址公布出去,子函数拿到这个地址,可以通过这个地址间接访问上层空间 修改;
模板:
void fun(xxx *p){
*p = zzz;
}
int main() {
xxx a;
fun (&a);
}
1、float
void fun (float *p){
*p =zzz;}
int main(){
float a;
fun(&a);
2、int *
void fun(int * *p){ //这种情况是要对上级函数地址里的值进行修改;
*p = zzz; //(int *相当于函数返回值)来修改上级函数的值,
} //int * *p中,int* 存了a的地址,*p存了a的指针变量里存的地址;
int main() {
int * a;
fun (&a);
}