第3节 C语言
推荐给好友
打印
加入收藏
更新于2008-08-24 09:12:32

C语言是一种通用的计算机程序设计语言,在国际上十分流行,它既可用来编写计算机的系统程序,也可用来编写一般的应用程序。以前计算机的系统软件主要使用汇编语言编写的,对于单片机应用系统来说更是如此。由于汇编语言程序的可读性和可移植性都较差,采用汇编语言编写单片机应用系统程序的周期长,而且调试和排错也比较困难。而一般效率高的高级语言难以实现汇编语言对于计算机硬件直接进行操作(如对内存地址的操作移位操作等)的功能。而C语言既具有一般高级语言的特点,又能直接对计算机的硬件进行操作,并且采用C语言编写的程序能够很容易地在不同类型的计算机之间进行移植,因此许多以前只能采用汇编语言来解决的问题现在可以改用C语言来解决。

3.3.1 C 语言的特点

C语言可以用来编写科学计算或其他应用程序,但它更适合于编写计算机的操作系统程序以及其他一些需要对机器硬件进行操作的场合,有的大型应用软件也采用C语言进行编写,这主要是因为C语言具有很好的可移植性和硬件控制能力,表达和运算能力也较强。
概括来说,C语言具有以下一些特点:

1.语言简洁紧凑,使用方便灵活
C语言一共只有32个关键字,9个控制语句,主要用小写字母表示,压缩了一切不必要的成分;C语言程序书写形式自由,可以用简单的方法构造除复杂的数据类型和程序结构。

2.运算符丰富
C语言把括号、赋值、强制类型转换等都作为运算符处理,从而使C的运算类型极其丰富,共有34 种运算符。C表达式类型多样化,灵活使用各种运算符可以实现其他高级语言难以实现的运算。

3.数据结构类型丰富
C的数据结构类型丰富,根据需要可以采用:整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类型等多种数据类型来实现复杂数据结构的运算,尤其是指针类型数据使用起来非常灵活多样。

4.可进行结构化程序设计
C语言是以函数作为程序设计的基本单位的,用函数作为程序模块以实现程序的模块化,是结构化的理想语言。

5.语法限制不严格,程序设计自由度大
C语言的语法规则不太严格,程序设计的自由度比较大,限制和灵活是一对矛盾。C语言放宽了语法检查,所以程序员应当仔细检查程序,而不要过分依赖C编译程序去查错。

6.C语言允许直接访问物理地址
C语言允许直接访问物理地址,能进行位bit 操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作,这样它可以对单片机的内部寄存器和I/O口进行操作,可以直接访问片内或片外存储器。

7.生成目标代码质量高
众所周知,汇编语言程序目标代码的效率是最高的,但统计表明,C语言编 写的程序生成代码的效率仅比汇编语言低10~20%。

8.程序可移植性好 汇编语言完全依赖于机器硬件,因而不具有可移植性,C语言是通过编译来得到可执行代码的。C语言的编译程序便于移植,基本上不作修改就能用于各种机器和操作系统。
尽管C语言具有许多的优点,但和其他任何一种程序设计语言一样,也有其自身的缺点。但总的来说,C语言的优点远远超过了它的缺点。

3.3.2 C 语言的程序结构

C 语言程序是由若干个函数单元组成的,每个函数都是完成某个特殊任务的子程序段。组成一个程序的若干个函数可以保存在一个源程序文件中,也可以保存在几个源程序文件中,最后再将它们连接在一起,C语言源程序文件的扩展名为“.c”。
一个C 语言程序必须有而且只能有一个名为main()的函数,它是一个特殊的函数,也称为该程序的主函数。程序的执行都是从main()函数开始的,下面我们先来看一个简单的程序例子。 [例] 求最大值
int max( int x,int y)
main                                       /* 主函数名 */
{                                               /* 主函数体开始 */
int a, b, c ;                   /* 主函数的内部变量类型说明 */
a=10; b=20;                   /* 变量赋值 */
c=max (a,b);              /* 调用max 函数将得到的值赋给c */
}
int max( x ,y)                /* 定义函数max 函数值为整型x y 为形式参数 */
int x, y ;                           /* 对形参x y 作类型定义 */
{
int z ;                                   /* max 函数中用到的变量z 也要加以定义 */
if( x>y) z=x;                 /* 计算最大值 */
else z=y;
return (z);                     /*将计算的最大值z 返回通过max 带回调用处 */
}

本例中除了main 函数之外,还用到了功能函数调用,函数max 是一个被调用的功能函数其作用是将变量x 和y 中较大者的值赋给变量z, 变量x 和y 在函数max 中是一种形式变量,它的实际值是通过main函数中的调用语句传送过来的。变量z 是函数max 要返回的值,return 语句将z 的值返回给main 函数的调用处。
Main 函数中第三行调用max 函数,在调用时,将实际参数a 和b 的值分别传送给max 函数中的形式参数x 和y 。经过执行max 函数得到一个返回值(即max函数中变量z 的值),把这个值赋给变量c。
通过以上的例子可以看到一般C 语言程序具有如下结构:
预处理命令            #include< >
函数说明                int fun1;
                                char fun2;
功能函数1              fun1 
                                {
函数体                   … 
                                }
主函数                     main
                                 {
主函数体                   … 
                                 }
功能函数2               fun2 
                                  {

函数体                       …
                                   }
                                    ….

C 语言的开头部分通常是预处理命令,#include 命令。它是通知编译器在对程序进行编译时,将所需要的头文件读入后再一起进行编译。
C 语言程序是由函数所组成的,一个程序至少应包含一个主函数main()。函数之间可以相互调用,但main()函数只能调用其他的功能函数,而不能被其他函数所调用,不管main 函数处于程序中的什么位置,程序总是从main()函数开始执行。

3.3.3 C 语言的基本语法

C 语言是用的最为广泛的高级语言,在单片机中也是很重要的,相较于汇编语言,C语言简单易懂,移植性好,少许修改后可以应用于不同的单片机。其基本语法主要包括以下几类:

1.数据类型
在C 语言中,每个变量在使用之前必须定义其数据类型,C语言有以下几种数据类型:整型(int),浮点型(float),字符型(char),指针型(*),无值型(void),数组类型(array)以及结构(struct)和联合(union)。

  • 整型(int)
    加上不同的修饰符, 整型数有以下几种类型:
    signed short int 有符号短整型数,简写为short 或int,字长为2 字节,共16位二进制数, 数的范围是-32768~32767
    signed long int 有符号长整型数,简写为long, 字长为4字节共32位二进制数, 数的范围是-2147483648~2147483647
    unsigned short int 无符号短整型数,简写为unsigned int,字长为2 字节共16 位二进制数, 数的范围是0~65535
    unsigned long int 无符号长整型数,简写为unsigned long,字长为4 字节共32 位二进制数, 数的范围是0~4294967295
  • 浮点型(float)
    C 中有以下两种类型的浮点数:
    Float: 单浮点数,字长为4 个字节,数的范围是3.4x10-38E~3.4x10+38E
    Double:双浮点数,字长为 8 个字节,数的范围是1.7x10-308E~1.7x10+308E
    说明:
    浮点数均为有符号浮点数, 没有无符号浮点数
  • 字符型(char)
    字符型分为有符号和无符号两种类型,例如:
    char a;                   /*a 被定义为有符号字符变量*/
    unsigned char l; /*l 被定义为无符号字符变量*/
    在计算机中以其ASCII 码方式表示, 其长度为1 个字节, 有符号字符型数取值范围为-128~127, 无符号字符型数到值范围是0~255 因此在C 语言中, 字符型数据在操作时将按整型数处理, 如果某个变量定义成char, 则表明该变量是有符号的, 即它将转换成有符号的整型数。C中规定对ASCII 码值大于0x80 的字符将被认为是负数,例如ASCII值为0x8c的字符, 定义成char 时, 被转换成十六进制的整数0xff8c 这是因当ASCII 码值大于0x80时, 该字节的最高位为1, 计算机会认为该数为负数, 对于0x8c 表示的数实际上是-74(8c的各位取反再加1), 而-74 转换成两字节整型数,并在计算机中表示时就是0xff8c( 对0074 各位取反再加1),因此只有定义为unsigned char 0x8c 转换成整型数时才是8c。 这一点在处理大于0x80 的ASCII 码字符时(例如汉字码)要特别注意,一般汉字均定义为unsigned char。另外, 也可以定义一个字符型数组, 此时该数组表示一个字符串。
    例如: char str[10];计算机在编译时, 将留出连续10 个字符的空间, 即str[0]到str[9]共10 个变量, 但只有前9 个供用户使用,第10 个str[9]用来存放字符串终止符NULL,即"\0", 但终止符是编编译程序自动加上的, 这一点应特别注意。
  • 指针型(*)
    指针是一种特殊的数据类型, 在其它计算机语言中一般都没有定义这种数据类型。指针是指向变量的地址, 实质上指针就是存贮单元的地址,根据所指的变量类型不同, 可以是整型指针(int *),浮点型指针(float *),字符型指针(char *),结构指针(struct *)和联合指针(union *)等。
  • 无值型(void)
    无值型字节长度为0, 主要有两个用途: 一是明确地表示一个函数不返回任何值;一是产生一个同一类型指针(可根据需要动态分配给其内存)。
    例如:
    void *buffer; /*buffer 被定义为无值型指针*/


2.常量与变量
C 语言的数据有常量和变量之分: 常量:在程序运行过程中其值不能改变的量称为常量,常量可以有不同的数据类型; 变量:在程序运行中其值可以改变的量称为变量,变量是由变量名和变量值组成的。 每个变量都有一个变量名,在内存中占据一定的存储单元地址,并在该内存单元存放
该变量的值。
对于单片机来说,变量类型或数据类型的选择是值得一提的,对于C 这样的高级语言,不管使用何种数据类型,从字面上看其操作都非常简单,然而,实际上编译器需要一系列机器指令对其进行复杂的变量类型、数据类型处理,特别是浮点运算,明显增加运算时间和程序的长度。所以应该慎重进行变量和数据类型的选择。不要使用大量不必要的变量类型。

3.运算符和表达式
C 语言中运算符和表达式数量之多在高级语言中是少见的,运算符按其在表达式中所起的作用可分为赋值运算符、算术运算符、关系运算符、逻辑运算符、位运算符、逗号运算符、条件运算符、指针和地址运算符、强制类型转换运算符和sizeof 运算符等。根据运算符在表达式中与运算对象的关系又可分为单目运算符、双目运算符和三目运算符。单目运算符是只需要一个运算对象,双目运算符则要求有两个运算对象。

4.表达式语句
表达式语句有如下几种:

  • 基本表达式语句
    表达式语句是最基本的一种语句,在表达式的后边加一个分号;就构成了表达式语句。表达式语句也可以仅有一个分号组成这种语句称为空语句空语句是表达式语句的一个特例它在程序设计中有时是很有用的
  • 复合语句
    复合语句是由若干条语句组合而成的一种语句,它是用一个大括号{}将若干条语句组合在一起而形成的一种功能块复合语句,它不需要以分号结束,但内部的各条单语句仍需以分号结束。
    复合语句的一般形式为
    {
    局部变量定义;
    语句1;
    语句2;
    语句n;
    }
    复合语句在执行时,其中的各条单语句依次顺序执行,整个复合语句在语法上等价于一条单语句,因此在C 语言程序中可以将复合语句视为一条单语句,复合语句允许嵌套。


5.判断选取控制语句

  • If 语句的简化形式
    if (条件表达式) 语句
    其含义为:如果条件表达式的结果为真(1),就执行后面的语句,反之若条件表达式的结果为假(0), 就不执行后面的语句,这里的语句也可以是复合语句。
  • if 语句的基本形式
    if 语句的基本形式为: if(条件表达式) 语句1 else 语句2 上述结构表示:如果条件表达式的值为1(即真),则执行语句1,反之如果条件表达式的值为0(即假),则执行语句2,这里语句1和语句2都可以是复合语句。
  • if语句的多分支形式
    if 语句的多分支形式为:
    if (条件表达式1) 语句1
    else if(条件表达式2) 语句2
    ......
    else if(条件表达式m) 语句m
    else 语句n
    这种条件语句常用来实现多方向条件分支
  • 开关语句————switch语句
    switch 语句是多分支选择语句,相比较if 语句的多分支形式,switch 语句可以直接处理多分支选择,使程序结构清晰,使用方便。
    switch 语句的一般形式如下:
    switch 表达式
    {
    case 常量表达式1: 语句1 break;
    case 常量表达式2; 语句2 break;
    ......
    case 常量表达式n; 语句n break
    default: 语句d
    }
    开关语句的执行过程是:将switch 后面表达式的值与case 后面各个常量表达式的值逐个进行比较,若遇到相等时,就执行相应的case 后面的语句,再执行break语句,跳出switch语句,若无相等的情况,则只执行语句d。break 语句又称间断语句,它的功能是中止当前语句的执行,使程序跳出switch 语句。如果在case 语句中遗忘了break,则程序在执行了本行case 选择之后,将执行后续的case 语句,而不会按规定的跳出switch 语句。


6.循环语句 在许多实际问题中,需要进行有规律的重复操作,如累加求和,数据块的搬移等等。循环结构是结构化程序的三种基本结构之一,C语言中用来构成循环控制的语句分述如下: 

  • while语句
    while语句的一般形式为:
    while(条件表达式) 语句
    while 循环表示:当条件表达式的结果为真(1)时,程序便执行后面的语句,直到条件表达式的结果变化为假(0)时,才结束循环,并继续执行循环程序外的后续语句。这种循环结构是先检查后执行,如果条件表达式的结果一开始就为假,则后面的语句一次也不会被执行。
  • do-while语句
    do-while构成循环结构的一般形式如下:
    do 语句
    while (条件表达式);
    do-while 语句先执行循环体中的语句,然后再判断条件表达式的值是否为真,如果为真,则重复执行循环体语句,直到条件表达式的值变为假时为止。这种循环结构的特点是先执行再检查,因此do-while语句即使条件表达式的值 一开始就为假,循环体语句也会被执行一次。
  • for语句
    for 语句的一般形式如下:
    for (<初始化表达式>;<循环条件表达式>;<更新表达式> ) 语句
    初始化表达式一般是一个赋值语句,它用来给循环控制变量赋初值;循环条件表达式决定什么时候退出循环;更新表达式定义循环控制变量每循环一次后按什么方式变化。这三个部分之间用分;隔开。
    for 语句的执行过程是:先计算出初始化表达式的值作为循环控制变量的初值,再检查循环条件表达式的结果,当满足循环条件时,就执行循环体语句,并计算更新表达式,然后再根据更新表达式的计算结果来判断循环条件是否满足,一直进行到循环条件表达式的结果为假时退出循环体。
  • goto语句
    goto 语句是一个无条件转向语句,它的一般形式为:
    goto 语句标号;
    goto 语句中的语句标号是一个带冒号的标识符,将goto 语句和if 语句一起使用,可以构成一个循环结构。下面的程序可以用来累加1~100 求和
    main
    {
    int a,sum=0;
    a=1;
    loop: if (a<=100)
    { sum=sum+a;
    a++;
    goto loop;
    }
    }
    除此之外,goto 语句还可以在多重循环中从内层循环跳到外层循环,但是结构化程序设计方法主张限制使用goto 语句,因为滥用goto 语句会使程序流程无规律,可读性差,因此在程序设计中应尽量少用goto 语句。
  • continue语句
    continue 语句是一种中断语句,它一般用在循环结构中,其作用是结束本次循环,即跳过循环体中下面尚未执行的语句,把程序流程转移到当前循环语句的下一个循环周期,接着进行下一次循环控制条件的判断。continue 语句的一般形式为:
    continue;
    continue 语句只用在for、 while 、do-while 等循环体中,它也是一种具有特殊功能的无条件转移语句,但与break 语句不同,continue 语句并不跳出循环体,而只是结束本次循环。
  • 返回语句
    返回语句用于终止函数的执行,并控制程序返回到调用该函数时所处的位置。返回语句有两种形式:
    return (表达式);
    return;
    如果是第一种return 语句,后边带有表达式,则要计算表达式的值,并将表达式的值作为该函数的返回值。若使用不带表达式的第2种形式。则被调用函数返回主调用函数时,函数值不确定。
    一个函数的内部可以含有多个return 语句,但程序仅执行其中的一个return 语句而返回主调用函数。一个函数的内部也可以没有return 语句,在这种情况下当程序执行到最后一个界限符} 处时就自动返回主调用函数。


3.3.4 C 编译器

C 预处理器是Softune V3 C编译器的一个组成部分,在C 语言中,通过预处理指令可以为C 语言本身提供很多功能和符号等方面的扩充,可增强其灵活性和方便性。预处理指令只在程序编译时起作用,且通常是按行进行处理的,因此常又称为编译控制行。编译器在对整个程序进行编译之前,先对程序中的编译控制行进行预处理,然后再将预处理的结果与整个C 语言源程序一起进行编译,产生汇编文件。常用的预处理指令有:宏定义、文件包含和条件编译。预处理命令以符号“#”开头。
预处理与编译的结构如下图3-17所示:




1.宏定义

  • 宏定义――不带参数的宏定义
    不带参数的宏定义的一般格式为:
    #define 标识符待替换的字符串
    其中,“标识符”是所定义的宏符号名(或称宏名)。宏定义的作用是,在程序中用指定的宏名来替代指定的字符串。
    宏定义又称宏替换,预处理器每次在程序行中扫描到宏名就把宏名替换为指定的字串,但如果宏名出现在注释中和字符串中则是例外,如果宏名为预处理器的保留字也不能被替换。
    按习惯,通常将宏符号名用大写字母表示,以区别于其它的变量名。宏定义不是C 语言的语句,因此在宏定义行的末尾不要加分号,否则在编译时将连同分号一起进行替换,可能导致语法错误。如果在一个宏定义中包含另一个宏符号名,那么就形成宏定义嵌套,宏定义的嵌套深度最大可达255 级。一般宏定义指令#define 的行放在文件的开头,其作用范围是从被定义的地方开始,至本源文件结束。
  • 宏定义――带参数的宏定义
    带参数的宏定义的一般格式为:
    #define 宏符号名(参数列表) 表达式
    宏符号名和左括号必须紧紧相连,其间不能有空格注释及诸如此类的字符串。括号中参数表里的参数被称为形式参数,在以后的程序中它们将被实际参数所替代,实际参数的数目必须与形式参数的数目一样。如果未指定任何表达式,则宏符号名将替换空字符。带参数的宏定义也允许宏定义嵌套,宏定义的嵌套深度最多为255 级。
    【例】
    #define eq(a, b) a==b
    #define ne(a, b) a!=b
    ...
    int x,y,z;
    x=y=1;
    if( eq(x,y) )
    z=10; /* 这一行得到执行 */
    else z=20;; /* 这一行未得到执行 */
    宏定义指令#define 要求在一行内写完,如果一行内写不下时,可在行末加反斜杠“\” 进行续行。

2.文件包含(#include指令)
文件包含是指一个程序文件将另一个指定文件的全部内容包含进来。比如经常使用的文件包含命令 #include <stdio.h>,就是将C 编译器提供的输入输出库函数的说明文stdio.h 包含到自己的程序中去。文件包含命令的一般格式为:
#include <文件名> 或 #include “文件名”
文件包含命令#include 的功能是用指定文件的全部内容替换该预处理行。在进行较大规模程序设计时,文件包含命令是十分有用的。为了适应模块化编程的需要,可以将组成C语言程序的各个功能函数分散到多个程序文件中,分别由若干人员完成编程,最后再用#include 命令将它们嵌入到一个总的程序文件中去。需要注意的是一个#include 命令只能指定一个被包含文件,如果程序中需要包含多个文件则需要使用多个包含命令,还可以将一些常用的符号常量、带参数的宏以及构造类型的变量等定义在一个独立的文件中,当某个程序需要时再将其包含进来,这样将可以减少重复劳动,提高程序的编制效率。
文件包含命令#include 通常放在C 语言程序的开头,被包含的文件一般是一些公用的宏定义和外部变量说明,当它们出错或是由于某种原因需要修改其内容时,只需对相应的包含文件进行修改,而不必对使用它们的各个程序文件都作修改,这样有利于程序的维护和更新。当程序中需要调用C 编译器提供的各种库函数的时候,必须在程序的开头使用#include 命令将相应函数的说明文件包含进来,经常在程序开头使用的命令#include <stdio.h>就是为了这个目的。

3.条件编译
一般情况下对C 语言程序进行编译时所有的程序行都参加编译,但有时希望对其中一部分内容只在满足一定条件时才进行编译,这就是所谓的条件编译。条件编译可以选择不同的编译范围,从而产生不同的代码。Softune V3 C 编译器的预处理器提供以下编译命令#if、#elif、#else、#endif、#ifdef、#ifndef这些命令有三种使用格式,如下所述:

  • 条件编译命令格式一
    #ifdef 标识符
                  程序段1
    #else
                  程序段2
    #endif
    该命令格式的功能是:如果指定的标识符已被定义,则程序段1 参加编译并产生有效代码,而忽略掉程序段2,否则程序段2参加编译并产生有效代码而忽略程序段1。其中#else和程序段2可以没有,这里的程序段可以是单行或多行的 C 语言语句,这种条件编译对于提高 C 语言源程序的通用性是很有好处的。
    【例】
    #define DEBUG
    ...
    #ifdef DEBUG
                  printf(“We are debugging”);
    #endif
  • 条件编译命令格式二
    #ifndef 标识符
                 程序段1
    #else
                 程序段2
    #endif
    该命令格式与第一种命令格式只在第一行上不同,它的作用与第一种刚好相反,即如果指定的标识符未被定义,则程序段1 参加编译并产生有效代码而忽略程序段2,否则程序段2 参加编译,并产生有效代码而忽略程序段1。以上两种格式的用法也很相似,可根据实际情况视需要而定。
  • 条件编译命令格式三
    #if 常量表达式1 
                 程序段1
    #elif 常量表达式2
                程序段2
    ... ...
    #elif 常量表达式n-1 
                程序段n-1
    #else
                 程序段n
    #endif
    这种格式条件编译的功能是:如果常量表达式1 的值为真(非0),则程序段1参加编译,然后将控制传递给匹配的#endif 命令,结束本次条件编译,继续下面的编译处理。否则如果常量表达式1 的值为假(0),则忽略掉程序段1(不参加编译)而将控制传递给下面的一个#elif 命令,对常量表达式2 的值进行判断。如果常量表达式2 的值为假(0),则将控制再传递给下一个#elif 命令。如此进行,直到遇到#else 或#endif 命令为止,使用这种条件编译格式可以事先给定某一个条件,使程序在不同的条件下完成不同的功能。

3.3.5 C 编译器

C 编译器进行编译的结构如下图3-18所示:




1.C 编译器的数据调用协议

  • 内存模式(编译模式)
    内存模式是指如何在存储器中放置程序代码和数据,它们允许占用的存储 器大小,及如何存取它们。与PC 机上的编译器相似, Softune V3编译器允许用户使用4 种内存模式来进行C 语言编程,4 种可用的内存模式及其含义如下面的表格所示:

2.与汇编语言程序的接口
为了使汇编语言的源程序能够调用C 语言源程序,编译器必须按照某种规则来对C 语言源程序进行编译,生成的汇编输出文件中的变量名标号和函数名标号才能被其它汇编源程序引用,以达到汇编程序调用C 源程序的目的。如下表3-7所示:

                                           表3-7 C源程序与汇编语言程序的接口


注:“no”是C编译器内部产生的数字

3.中断函数调用接口
通过给函数加上“__interrupt” 类型限定符就成为中断函数,如果用普通的方法直接调用中断函数,而不是由于发生中断导致中断函数被调用,则无法保证这种直接调用(中断函数)的正确性和安全性。在中断函数里进行(普通)函数调用遵循标准链接规则。
中断函数调用接口有如下几方面内容:

  • 堆栈结构
    中断发生后,堆栈被切换为中断堆栈。
  • 函数参数
    中断函数不带任何参数。
  • 调用过程
    中断发生后,通过查中断向量表,而触发中断程序运行。
  • 寄存器的使用
    编译器保证,在中断函数里所有(使用到的)寄存器都被保护起来的;除非在中断函数里使用嵌入汇编语句而改变了寄存器的值。
  • 函数返回值
    中断函数没有返回值 
    中断调用的堆栈结构
    中断发生后,堆栈指针被切换为中断堆栈指针(中断函数以系统堆栈指针 SSP作为堆栈指针),下表3-8说明中断发生后,中断调用的堆栈结构:

                                                           3-8 中断调用的堆栈结构
    (低位地址)

    (高位地址)

    中断调用过程
    中断发生后,通过查中断向量表,而触发中断程序运行,如果用普通的方法直接调用中断函数,而不是由于发生中断导致中断函数被调用,则无法保证这种直接调用(中断函数)的正确性和安全性。

3.3.6 嵌入式C 语言的特殊之处

富士通8FX的集成开发环境Softune V3在启动文件的编译时,必须调用到启动文件Start8FX.asm,在这个启动文件内,主要完成了内存,堆栈的定义,分配和初始化,以及对机器时钟进行了配置,目标代码的执行从它开始,并调用主程序文件(main.c)。详细介绍见第六章。
另外,C 语言与汇编语言可以互相调用以及嵌入汇编,在C 语言中嵌入汇编的格式如下:

  • 使用asm 表达式
    asm(字符串序列);
    asm 表达式可以在函数内和函数外使用,当在函数内使用asm表达式时就在表达式的位置上直接展开为汇编语句,当在函数外使用asm 表达式时它被扩展成独立的SECTION, 因此如果在函数外使用asm表达式,一定要用SECTION 的预处理指令来为其单独定义一个SECTION,否则难以保证相应操作的正确性。 在函数内使用asm 表达式,必须由用户自己来保存和恢复寄存器,对于累加器是个例外,因为编译器会对累加器进行保存和恢复所以可以随意地使用累加器。
    如下所示:
    void wait(unsigned long j)
    {
    while(j--)
    asm(" \tNOP ");
    }
    在wait()函数内使用asm表达式,直接将其展开成NOP,即没有操作。 
  • 使用pragma 预处理指令
    使用pragma 预处理指令的一般格式为:
    #pragma asm
    汇编语句
    #pragma endasm
    pragma 预处理指令可以在函数内和函数外使用,当在函数内使用时,在#pragmaasm 和#pragma endasm 预处理指令之间的汇编语句直接被转换成同样的汇编代码。当在函数外使用时它被扩展成独立的SECTION,因此如果在函数外使用pragma 预处理指令,一定要用SECTION的预处理指令来为其单独定义一个SECTION,否则难以保证相应操作的正确性,在函数内使用pragma 预处理指令必须由用户自己来保存和恢复寄存器,对于累加器是个例外,因为编译器会对累加器进行保存和恢复,所以可以随意地使用累加器。
    如下所示:
    void wait(unsigned int j)
    {
    volatile unsigned int i;
    for (i = 0; i < j; i++)
    {
    #pragma asm
    NOP
    NOP
    NOP
    NOP
    #pragma endasm
    }
    }
    在#pragma asm 和#pragma endasm之间的汇编语句直接转换为四个连续的NOP。

 


上一节                    下一节









相关链接


 
关于我们 | 诚邀加盟 | 客户服务 | 相关法律 | 网站地图 | 友情链接 | 服务信箱:service@eefocus.com
© 2006 与非门科技信息咨询(北京)有限公司 All Rights Reserved.