1、计算机为什么需要编程?编程已经编了这么多年,已经写了很多程序,为什么还需要编程?
因为我们需要(希望)通过程序,达到一定的目的或者得到一定的结果。
编写新的程序是为了得到不同的结果。
计算机程序 = 代码 + 数据 (经过运行后得到一个结果,就是计算机程序运行的结果)
从宏观上理解:代码就是动作,就是加工数据指令。
数据就是数字,就是我们希望被代码加工的数字。
结论:计算机编程不外乎两种目的,结果或者过程;即加工数据得到结果。
可以用函数来类比:比如函数的形参就是待加工的数据(有可能函数内还需要一些临时数据,就是局部变量),函数体就是代码,函数体的执行就是加工过程,函数的返回值就是结果
2、程序就是有很多个函数组成的,函数是程序的重要组成部分。
程序需要运行才会得到我们需要的结果或者过程。
程序的本质就是函数,函数的本质是加工数据的动作。
3、冯诺依曼结构和哈佛结构
冯诺依曼结构是:数据和代码放在一起。
哈佛结构是:数据和代码分开存在。
什么是代码:函数
什么是数据:全局变量、局部变量
譬如在S5PV210中运行的linux系统上,运行应用程序时:这时候所有的应用程序的代码和数据都在DRAM,所以这种结构就是冯诺依曼结构;
在单片机中,我们把程序代码烧写到Flash(内置NorFlash,flash是只读的)中,然后程序在Flash中原地运行,程序中所涉及到的数据(全局变量、局部变量)不能放在Flash中,必须放在RAM(SRAM)中。这种就叫哈佛结构。
4、动态内存DRAM和静态内存SRAM
静态内存是系统分给一定的内存后不再变化了,也就是在程序一开始运行就分配内存(不需要初始化就可以直接使用的),直到程序结束了,内存才被释放;
动态内存是系统开始不分配内存,运行时根据需要分配(需要先进行初始化才能进行使用),也就是在程序调用在程序中定义的函数时才被分配,函数调用结束了,内存就释放
5、为什么需要内存?
总结:内存是用来存储可变数据的,数据在程序中表现为全局变量或局部变量等(在gcc中,常量也是存储在内存中;但在大部分单片机中,常量存储在flash中的,也就是代码段中)
很多编程的关键都是为了内存,譬如数据结构和算法
数据结构是研究数据如何组织的,而数据是放在内存中的。
算法是研究如何更优秀的更有效的加工数据,也离不开内存。衡量一个算法优秀与否,绝大部分与这个算法所占的内存大小有关
。
6、如何管理内存?
内存是程序的一种资源,不能过于或随便的浪费。所以管理内存对程序来说是一种很重要的技术。
在没有操作系统(也就是裸机程序)中,程序需要直接操作内存,编程者需要自己计算内存的使用和安排,如果不小心把内存用错了,后果需要自己承担。
从语言角度来讲:不同的语言提供了不同的内存操作接口;
譬如汇编:根本没有任何内存管理,内存管理全靠程序员自己,汇编中操作内存时直接使用内存地址(如0xd0020010),非常麻烦。
譬如C语言:C语言中编译器帮我们管理直接内存地址,我们都是通过编译器提供的变量名等来访问内存的。在操作系统中,如果需要大块内存,可以通过API(malloc分配内存free释放内存)来访问系统内存;在裸机系统中,如果需要大块的内存,就需要自己来定义数组等解决
总结:语言没有好坏,只有适应不适应;譬如当我们程序对性能(如操作系统内核)非常需要时就会使用C/C++,当我们对开发程序速度非常需要时,就会使用java/C#
7、什么是内存?
内存(RAM)从硬件上讲就是一个电脑配件(内存条)。
从逻辑上讲内存是一种这样的东西,可以随机访问,随时进行读写(也可以限制只读或者只写)。
内存在 编程中天生就是为存放变量的(也可以说就是有了内存,才有了C语言的变量,原来受限于硬件)
内存实际是有无数个内存单元格组成的,每一个单元格都有一个唯一的内存地址
用大楼来类比内存很合适,内存就像是一栋无限高的大楼,一个个内存单元格就像是大楼内的一个个小房子,内存地址就像是门牌号,存进去的内容就像是住进去的人
8、位,字节,半字,字的概念和内存位宽
内存单元的大小单位:位(1bit) 字节(8bit) 半字(一般是16bit) 字(一般是32bit)
在所有计算机,所有操作系统中,位永远是 1bit,字节永远是 8bit
历史上曾经出现过16位,32位,64位系统三种,而且系统还有windows,linux,ios等。半字和字等很多概念被混乱定义过
所以在工作中不要详细区分字,半字,双子的概念,只要知道这些单位具体占多少位是依赖于平台的,实际工作中先去搞清楚所在平台的定义,需要注意的是半字一定是字的一半,双字一定是字的2倍
编程一般用不到字的概念,区分主要是我们会在文档中用到字,如果不加区分,容易造成理解错误
在linux+ARM的平台上 字是32位的
内存芯片之间可以并联,即使是8位的内存芯片,通过并联可以也可以做出来16位,32位的内存芯片
9、内存编址和寻址、内存对齐
内存在逻辑上就是一个一个的格子(内存单元格),里面可以存储数据,每一个单元格都有一个内存地址,单元格与内存地址一一对应且永久绑定,这就是内存编址的方法
一个内存单元有两个概念:地址和空间(内存单元的两面性)
关键:内存编址是以字节为单位的
一个内存地址,对应的空间(内存单元)大小是一定的,就是一个字节(8bit)
内存对齐;对齐访问和硬件匹配,所以效率高,非对齐访问和硬件不搭配,所以效率低(因为系统兼容性的问题,非对齐访问也可以实现)
10、数据类型和内存的关系
数据类型是用来定义变量的,变量要存储、运算在内存中,所以内存就必须语相应的数据类型相匹配,否则就会造成效率低下或者不工作
int ×××(整数类型,整的意思就是说和cpu本身的数据位宽(32位或64位)是一样的)
C语言的数据类型决定一个内存单元的长度和解析方法
类型只是对后面符号或者数字(代表的内存地址)所表征的内存长度规定和解析方法规定而已
指针全名是指针变量,实质上和普通变量没有任何区别(包括内存分配)
11、内存管理之结构体
数据结构就是研究数据如何组织(在内存中分布)和加工的学问
最简单的数据结构就是数组(可以一次定义多个数据类型相同,意义相关的变量); //int a[10];
数组的优点:数组比较简单,可以随机访问,访问时用下标
数组的缺点:数组内所有元素的类型必须相同,且在定义时数组大小就确定,而且不能改变(没有伸缩性,灵活性差)
而在结构体中可以定义数据类型不同的变量
struct number { int age; char name[20]; double high; };
结构体可以说是数组的升级版;如果结构体中的数据类型全都相同就可以用数组来代替;
结构体内嵌指针可以实现面向对象;
总的来说C语言是面向程序的,但是C语言写出的linux系统是面向对象的
struct s { int age; //普通变量 void (*pFunc)(void); //函数指针,指向 void func(void) 这类的函数 };
使用这样的结构体就可以实现面向对象
12、内存管理之栈
什么是栈:栈是一种数据结构,C语言中用栈来存储局部变量;栈是用来管理内存的
栈:先进后出;入口即出口,有一个口是堵死的,所以先进去的必须后出来
队列:先进先出;入口和出口是分开的,必须从入口进,从出口出,所以先进去的先出来
栈内存是反复使用的,上次用完的值没有清零,所以定义局部变量时需要显式初始化,如果没有显式初始化,那么值就是随机的。
13、复杂数据结构
链表:最常用也是最重要的(在嵌入式开发领域)
14、位操作运算
运算种类和运算符:
位与:符号 a&b 全一为一,有零为零 用于对关心位清零(和零作位与)
位或:符号 a|b 全零为零,有一为一 用于对关心位置位(置一,和一作位或)
位异或:符号a^b 相同为零,不同为一 用于对关心位取反(和一作异或)
位取反:符号 ~a 一变零,零变一
位左移:符号a<<
位右移:符号a>> 对于有符号数,右移时左侧补符号位(如果正数就补0,负数就补1,叫算术移位)
15、逻辑操作运算
在C语言中,只有0为假,其他的全都是真
逻辑与:符号 a&&b 有假为假,全真为真
逻辑或:符号 a||b 有真为真,全假为假
逻辑取反:符号 !a 真变假,假变真
16、指针是C语言的精髓
指针变量和普通变量没有任何本质区别
标准用法: int a = 10, b = 0; int *p; p = &a; b = *p; //结果b = 10
16、野指针问题
野指针就是指定义后没有初始化或者赋值的指针
野指针指向的位置是随机的,是不可预知的,在运行时容易触发段错误(Sgmentation fault)
总结:指针变量和普通局部变量一样,都是存储在栈上,都需要在定义时,进行初始化,否则值就是随机的
17、如何避免野指针问题
一般注意以下四点就可以避免野指针问题
第一点:在定义指针变量时就初始化为NULL
第二点:在解引用前判断指针是否指向NULL(指向则不执行解引用,不指向则可以放心解引用)
即 if(NULL != p);
第三点:在解引用完成后,重新把指针赋值为NULL
第四点:在解引用前确保指针指向一个确定的内存地址(需要程序员自己把握)
NULL的实质就是数字0,0地址是不可访问的
18、const关键字和指针结合
const和指针常见的有四种方式:
第一种:int const *p; //p是一个指针,指向一个int型数据,p所指向的是个常量
第二种:const int *p; //p是一个指针,指向一个int型数据,p所指向的是个常量
第三种:int *const p; //p是一个指针,指向一个int型数据,p本身是常量,指向的是变量
第四种:const int *const p; //p是一个指针,指向一个int型数据,p本身是常量,指向的也是常量
关于指针变量,一共涉及到两个变量,一个是指针变量本身(p),一个是指针所指向的那个变量(*p),而一个const关键字只能修饰一个变量。
19、深入学习数组
从内存角度看,数组就是一次定义多个变量,而且在内存中的存储单元的内存地址是依次连续的
从编译器角度看,数组数组变量本质上和普通变量,指针变量没有区别;变量的本质就是一个地址,编译器把地址(在编译器中表现为具体的数值)和变量名绑定,变量类型决定了这个地址的延续长度
搞清楚:变量、变量名、变量类型的三个概念的含义和区别
//左值和右值:赋值运算符左边的就是左值,右边的就是右值,赋值就是把右值里面的东西给左值;所以右值里面必须有东西(如常量,变量),左值必须能接收东西(如变量)
20、指针和数组的天生关系
数组就是内存地址连续的、类型相同(也就是大小相同)的一连串元素(变量)。这就决定了数组天生就适合使用指针进行访问,数组访问有两种,一种是通过数组下标进行访问(也就是 a[0];);
一种是通过指针进行访问(也就是 *(a+2);)。
编译器实质都是通过指针访问数组,数组下标访问是C语言提供给编程者的一种方便方法。
定义一个数组 int a[6];
a 表示数组首元素首地址,类型是int *类型,意义和数值等同于&a[0],两者可以相互替换。
&a 表示整个数组的首地址,类型是数组指针。
指针变量 +1,表示指向数组的下一个元素,而不是真的数值+1;也就是加1*sizeof(数据类型);
在32位系统中,所有的指针变量所占的内存空间都是4字节
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。