Chinaunix首页 | 论坛 | 博客
  • 博客访问: 89737
  • 博文数量: 22
  • 博客积分: 275
  • 博客等级: 二等列兵
  • 技术积分: 261
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:22
文章分类

全部博文(22)

文章存档

2017年(3)

2016年(11)

2015年(8)

我的朋友

分类: C/C++

2015-09-25 12:49:58


点击(此处)折叠或打开

  1.   凡所有相,皆是虚妄。若见诸相非相,则见如来。本文所述皆指常例,凡所常例即有异处。学者当知:细节中有魔鬼,这魔鬼就是无知。C语言就是相,如来就是汇编是机器码。无论是什么语言,最后都要落实到汇编来编译,到机器码去执行。机器码难于读,汇编难于写,C对汇编语言进行了抽象。实例:
  2.     最常见的符号 C++ ,用伪代码来表示 C=C+1 。为何要有++出现?因为C是中级语言,汇编中INC指令可把代码精简。
  3.     在实际 C++ 也不一定就是加一,如下表
  4.     int c ; c++ 是 inc 把变量c的值加一,细节:可能会溢出变成负数
  5.     char c ; c++ 是 inc 把变量c的值加一,细节:可能会溢出变成负值
  6.     char* c ; c++ 是 inc 把变量c的值加一,细节:指针操作可能会在*c时得到的是脏数据。
  7.     int * c ; c++ 这里就有点复杂了,变成下面的汇编
  8.         MOV EAX , C ; 将变量C的值取出来,不是把c指向的单元内容取出来
  9.         ADD EAX , sizeof(int) 这个数值依赖于机器字长编译器
  10.      在 16位地址的处理器,比如8051单片机上 = 16/8 =2
  11.      在 32位地址的处理器上,比如80486 = 32/8 =4
  12.      在 64位地址的处理器上,比如IA64 = 64/8 =8
  13.         MOVE C , EAX ; 计算后的结果存到C变量的实际内存中去。在逻辑上就是跳过了int型大小的地址空间。
  14.     这里还有魔鬼么?是的,在实际中这里还会有很多的魔鬼,例:
  15.         魔鬼1:硬件中断,如果中断处理程序不好,那么在计算完成后,再存的时候可能值已经被修改。
  16.         魔鬼2:虚拟内存的对换,结果就是存的时间会很长,需要页换进换出才能执行完。
  17.     当然了这些魔鬼在使用时上极少会遇到,比如内存对换有操作系统来完成了。
  18.         
  19. 开宗明义:程序 = 数据 + 算法 学习编程语言,说到底,就是学习数据和算法,用算法来操作数据,即是程序。    
  20.         
  21. C语言之数据
  22.     数据与数据类型的区别:类型是名,数据是实。名实相符,就是正确的数据,名实不符就是脏数据。CPU不管数据是正确的还是脏的只管根据指令去执行。
  23.     
  24.     数据类型就是相,数值是如来是本质。计算机世界中只有数据,没有类型。所有的类型都是从人类可理解的角度才产生的。这个字节和那个字节对于计算机来说,没有任何本质上的区别。以下均以32位机为例:
  25.     char型就是一个字节
  26.     short型就是两个字节
  27.     int型就是四个字节
  28.     float型就是四个字节
  29.     double型就是八个字节
  30.     各类指针都是四个字节
  31.     内存数据 01 02 03 04 05 06 07
  32.     char 读出来的是 0x01
  33.     short 读出来是 0x0201 <-- little endian
  34.      0x0102 <-- big endian
  35.     int 读出来 0x04030201 <-- little endian     
  36.     普通变量,名实相符,用其名就是取其实。 而&操作则是取地址,对应汇编LEA指令
  37.     指针变量,只有名无实。需要用*操作即 (*指针变量) 取其实。指针变量是用来来挑选数据的。

  38. C语言之算法
  39.     算法基础是指令。顺序执行就是简单指令的堆砌。跳转则打乱原有的顺序,产生了变化。
  40.     在汇编中有多种跳转
  41.         无条件跳转 JMP
  42.         为零跳转 JZ
  43.         非零跳转 JNZ
  44.         等等
  45.     跳转是基于标志的CPU有很多标志位比如:JZ JNZ指令就是利用Z标志位决定如何跳转CMP EAX,0执行后,如果EAX = 0 那么Z标志位为真。用 JZ就跳转。在C语言中不需要考虑太多这么细的,只要按人类逻辑就可以了。if( a == 0 ){} if( 0 ){}
  46.     那么接下来的所有程序控制都是跳转的变形。switch , if ,while, for 都是对汇编进行了可读化的整合。
  47.     WHILE循环
  48.         while( a == 0 ){code_a}
  49.         L_WHILE:                ;标号
  50.          CMP A , 0             ;比较A的值
  51.          JNZ L_WHILE_END    ;不是0就跳
  52.          code_a                ;执行代码块
  53.          JMP L_WHILE            ;跳转到前面去再次执行
  54.         L_WHILE_END
  55.         
  56.         do{code_a}while(a==0);
  57.         L_WHILE:
  58.             code_a                ;先执行代码 code_a
  59.             CMP A , 0            ;再比较
  60.             JZ L_WHILE            ;是0就跳回前面
  61.         L_WHILE_END:            ;不是0就继续向下执行
  62.         
  63.     条件执行        
  64.         if( a == 0 ){code_a}else{code_b}
  65.         L_IF_BEGIN:
  66.             CMP A, 0            ;比较
  67.             JZ L_IF                ;是0跳到L_IF做code_a
  68.             code_b;                ;非0执行code_b
  69.         L_ENDIF:
  70.          后继指令
  71.         L_IF:
  72.             code_a;
  73.             JMP L_END_IF        ;回到L_ENDIF往下执行后继指令
  74.             
  75.     FOR循环        
  76.         for( code_a ; a==0 ; code_b){
  77.             code_c;
  78.         }
  79.         L_FOR_BEGIN:
  80.             code_a                ;先做code_a 通常是循环变量的初始化
  81.         L_FOR_TEST:                ;先做条件判断
  82.             CMP A,0
  83.             JNZ L_FOR_END        ;不成立就退出
  84.             code_c;
  85.             code_b;
  86.             JMP L_FOR_TEST        ;跳转再判断
  87.         L_FOR_END

  88.     SWITCH结构
  89.         switch(A){
  90.             case 1:
  91.                 code_1;
  92.             case 2:
  93.                 code_2;
  94.                 break;
  95.             default:
  96.                 code_default;
  97.                 break;
  98.         }
  99.         静态数据
  100.         MAP:                    ;构造一个静态跳转表
  101.             .L_CASE1
  102.             .L_CASE2
  103.                                 ;执行期
  104.         L_SWITCH_BEGIN:
  105.             MOVE EAX , A        ;取A的值
  106.             CMP EAX,2            
  107.             JG L_SWITCH_DEFAULT    ;大于2做default
  108.             CMP EAX,0
  109.             JL L_SWITCH_DEFAULT ;小于0也做default
  110.             DEC EAX                ;EAX--
  111.             LEA EBX,MAP            ;取得跳转表的地址
  112.             SHIFT EAX,2;        ;左移2位,也就是乘以4,因为一个地址指针占4个字节
  113.             ADD EBX,EAX            ;合成最终的地址        
  114.             JMP EBX                ;跳转到L_CASE1 也可能是 L_CASE2
  115.         L_CASE1:
  116.             code_1                ;没有break,所以做完继续向下
  117.         L_CASE2:
  118.             code_2                
  119.             JMP L_SWITCH_END    ;break转换成JMP
  120.         L_SWITCH_DEFAULT:
  121.             code_default
  122.             JMP L_SWITCH_END    ;break转换成JMP,汇编优化时,这条会被去掉
  123.         L_SWITCH_END:            ;switch结构结束
  124.         说明:case 1 中没有break,所以做完code_1 会接着做code_2 ,这只是通常。如果有特别的优化,也可能做完code1 以后就跑飞了,到不确定的哪个地方了,具体的地方,要看编译器如何生成汇编程序。在常见的环境下都是接着做code_2。

  125. C语言之指针:
  126.     指针的本质就是变量中存储的是其他数据的地址。
  127.     地址:一号楼205室,这只是"名""实"可以是会议室也可以是办公室也可能根本不存在。
  128.     数据指针,指向的是数据,如何理解数据,由其类型决定。
  129.     函数指针,指向的是代码。可通过 变量名() 去执行相应的代码。
  130.         形式:type (*f)(参数列表);
  131.             其中的type表示运行后会有什么样的返回值。
  132.             参数列表,则明确了调用时需要什么参数。
  133.         实质:四字节的内存空间    
  134.             DWORD    f ;
  135.         运行时:
  136.             多条PUSH或MOV携带好传入的参数
  137.             MOV EAX , f ; 获得运行时的指针的值
  138.             CALL EAX ; 调用子程序
  139.     数组指针:指向一个数组的首成员的地址。
  140.         int * a , b[10];
  141.         a=b ; 等效于 a= &b[0];
  142.     指针数组:一个数组,里面放的数据是指针。可以理解成一本电话本。里面的每一个数据都是其他实体的地址。
  143.         int * a[3] , b[10];
  144.         a[0] = &b[0] ; 等效 a[0] = b ;

  145. C语言之数组
  146.     形式: 类型 名称 [ 大小 ]
  147.     实质: 产生了 sizeof(类型) * 大小 的内存块
  148.     特例: 大小为0,产生一个地址标号,即为某个地址取了一个名字。
  149.         int a[0] , b[20];
  150.         因为C语言不对越界进行检查,所以在普通的编译器下,用 a[15] 和 b[15] 是一样的。
  151.         在运行时,没有为a分配内存空间,因为a和b连续分配,所以a和b是一样的。

  152. C语言之结构、联合和枚举
  153.     结构struct:串行分配空间
  154.     联合union :并行共用空间
  155.     枚举enum : 对整型限制值域
  156.         struct S{        union U{            enum E{
  157.             int a ;            int a;                a,b=2
  158.             char b ;        char b;            }ea,*eb;
  159.         }sa,*sb;        }ua,*ub;
  160.     定义结构S及实例 sa; 定义联合U及实例ua;    定义枚举E和实例ea
  161.     sizeof(sa) 至少是 sizeof(int)+sizeof(char) 字节,实际在编译时有字节对齐的要求,暂不表。
  162.     sizoof(ua) 至少是 sizeof(int)>sizeof(char)?sizeof(int):sizeof(char),实际分配的空间也跟字节对齐有关。
  163.     sizeof(ea) 由编译器的字长决定,通常可以理解为 int 但其取值只能是 a 和 b 也就是0 和 2
  164.     在结构中a,b是独立的成员互不影响。
  165.     在联合中b,则是a的第一个字节的内容
  166.     +-----+-----+-----+-----+-----+
  167.     |byte1|byte2|byte3|byte4|byte5|
  168.     +-----+-----+-----+-----+-----+
  169.     |----------a------------|--b--| 联合
  170.     |--b--+-------a---------|        枚举
  171.     如上图示,ua.b 就是存取的图中的byte1 ,改变了ua.b的值,那么读取ua.a也变化了。
  172.     sa.b 就是图中的byte5;
  173.     成员的引用固定变量中的成员用.来取得,指针指向的成员用 ->
  174.     sa.a sb->a ua.a ub->b


  175. 附一:浮点型数据的解读    
  176.     float和double的范围是由指数的位数来决定的。
  177.       float的指数位有8位,而double的指数位有11位,分布如下:
  178.       float:
  179.           1bit(符号位) 8bits(指数位) 23bits(尾数位)
  180.       double:
  181.           1bit(符号位) 11bits(指数位) 52bits(尾数位)
  182.       于是,float的指数范围为-127~+128,而double的指数范围为-1023~+1024,并且指数位是按补码的形式来划分的。
  183.       其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。
  184.       float的范围为-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。

  185.     float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。
  186.       float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
  187.       double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。

 

阅读(1449) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~