2016-03-05 21:41:51 qq_20602929 阅读数 736
#if和if都是C和C++中的指令,但是二者区别很大。
1)#if属于条件编译技术,用于给编译器提供信息,控制有些代码变成机器语言;
2)if属于程序判断,在程序运行时控制语句是否被执行;
3)#if在程序编译前执行,if在程序运行时执行;
4)#if后面不能写变量,if后面可以写变量。
2011-12-02 14:18:54 iteye_4014 阅读数 289
[quote] 本源程序是每一个MCS-51系列单片机C语言程序的必备内容,包括程序开始处的说明信息、头文件、SFR定义、子函数、主函数和C语言常用的5大语句(if,while,do while,for,switch)模板。可将其模板下载到本地硬盘,在编写新程序的时候复制、粘贴到工程当中,然后根据情况写入具体内容。[/quote]

注意:使用本模板必须熟悉单片机C语言编程

[程序开始处的程序说明]

/*********************************************************************************************
程序名:   
编写人:     
编写时间:  年 月 日
硬件支持:  
接口说明:  
修改日志:  
  NO.1-
/*********************************************************************************************
说明:

/*********************************************************************************************/


[单片机SFR定义的头文件]

#include <REG51.h> //通用89C51头文件
#include <REG52.h> //通用89C52头文件
#include <STC11Fxx.H> //STC11Fxx或STC11Lxx系列单片机头文件
#include <STC12C2052AD.H> //STC12Cx052或STC12Cx052AD系列单片机头文件
#include <STC12C5A60S2.H> //STC12C5A60S2系列单片机头文件

[更多库函数头定义]

#include <assert.h> //设定插入点
#include <ctype.h> //字符处理
#include <errno.h> //定义错误码
#include <float.h> //浮点数处理
#include <fstream.h> //文件输入/输出
#include <iomanip.h> //参数化输入/输出
#include <iostream.h> //数据流输入/输出
#include <limits.h> //定义各种数据类型最值常量
#include <locale.h> //定义本地化函数
#include <math.h> //定义数学函数
#include <stdio.h> //定义输入/输出函数
#include <stdlib.h> //定义杂项函数及内存分配函数
#include <string.h> //字符串处理
#include <strstrea.h> //基于数组的输入/输出
#include <time.h> //定义关于时间的函数
#include <wchar.h> //宽字符处理及输入/输出
#include <wctype.h> //宽字符分类
#include <intrins.h> //51基本运算(包括_nop_空函数)


[常用定义声明]

sfr [自定义名] = [SFR地址] ; //按字节定义SFR中的存储器名。例:sfr P1 = 0x90;
sbit [自定义名] = [系统位名] ; //按位定义SFR中的存储器名。例:sbit Add_Key = P3 ^ 1;
bit [自定义名] ; //定义一个位(位的值只能是0或1)例:bit LED;
#define [代替名] [原名] //用代替名代替原名。例:#define LED P1 / #define TA 0x25

unsigned char [自定义名] ; //定义一个0~255的无符号字符型变量。例:unsigned char a;
unsigned int [自定义名] ; //定义一个0~65535的无符号整型变量。例:unsigned int a;


[定义常量和变量的存放位置的关键字]

data 字节寻址片内RAM,片内RAM的128字节(例:data unsigned char a;)
bdata 可位寻址片内RAM,16字节,从0x20到0x2F(例:bdata unsigned char a;)
idata 所有片内RAM,256字节,从0x00到0xFF(例:idata unsigned char a;)
pdata 片外RAM,256字节,从0x00到0xFF(例:pdata unsigned char a;)
xdata 片外RAM,64K字节,从0x00到0xFFFF(例:xdata unsigned char a;)
code ROM存储器,64K字节,从0x00到0xFFFF(例:code unsigned char a;)


[选择、循环语句]

if(1){

//为真时语句

}else{

//否则时语句

}

--------------------------

while(1){

//为真时内容

}

--------------------------

do{

//先执行内容

}while(1);

--------------------------

switch (a){
case 0x01:
//为真时语句
break;
case 0x02:
//为真时语句
break;
default:
//冗余语句
break;
}

--------------------------

for(;;){

//循环语句

}

--------------------------


[主函数模板]

/*********************************************************************************************
函数名:主函数
调 用:无
参 数:无
返回值:无
结 果:程序开始处,无限循环
备 注:
/**********************************************************************************************/
void main (void){

//初始程序

while(1){

//无限循环程序

}
}
/**********************************************************************************************/


[中断处理函数模板]
/*********************************************************************************************
函数名:中断处理函数
调 用:无
参 数:无
返回值:无
结 果:
备 注:
/**********************************************************************************************/
void name (void) interrupt 1 using 1{

//处理内容
}
/**********************************************************************************************/

[中断入口说明]

interrupt 0 外部中断0(ROM入口地址:0x03)
interrupt 1 定时/计数器中断0(ROM入口地址:0x0B)
interrupt 2 外部中断1(ROM入口地址:0x13)
interrupt 3 定时/计数器中断1(ROM入口地址:0x1B)
interrupt 4 UART串口中断(ROM入口地址:0x23)
(更多的中断依单片机型号而定,ROM中断入口均相差8个字节)

using 0 使用寄存器组0
using 1 使用寄存器组1
using 2 使用寄存器组2
using 3 使用寄存器组3


[普通函数框架]

/*********************************************************************************************
函数名:
调 用:
参 数:无
返回值:无
结 果:
备 注:
/**********************************************************************************************/
void name (void){

//函数内容

}
/**********************************************************************************************/


/*********************************************************************************************
函数名:
调 用:
参 数:0~65535 / 0~255
返回值:0~65535 / 0~255
结 果:
备 注:
/**********************************************************************************************/
unsigned int name (unsigned char a,unsigned int b){

//函数内容

return a; //返回值
}
/**********************************************************************************************/

2017-04-30 08:47:31 tian_maer 阅读数 671
1、一个完整的C语言程序是由若干条语句按一定的方式组合而成的。按C语言语句执行方式的不同,C程序可分为以下三种:顺序结构、选择结构、循环结构。
顺序结构:程序按语句的顺序逐条执行;
选择结构:程序根据条件选择相应的执行顺序;
循环结构:程序根据某种条件,只要满足这个条件,就重复执行某段程序,直到条件不满足为止。如果这个条件永远存在,就形成一个死循环。死循环在单片机的C语言程序中普遍存在,最典型的例子就是每个程序的主函数中都有一个死循环(也成无限循环)while(1);
 
一般的C程序可以包含全部以上三种结构,也可以只包含一种或两种结构。
 
2、要保证C语言程序能够按照预期目的运行,还需要用到一些特殊语句对程序进行控制。
1)控制语句
控制语句完成一定的控制功能,C语言中有9中控制语句
if()...else...语句:条件语句
for()...语句:循环语句
while()...语句:循环语句
do...while()语句:循环语句
continue语句:结束本次循环语句
break语句:中止循环执行语句
switch语句:多分支选择语句
goto语句:跳转语句
return语句:从函数返回语句
2)函数调用语句
函数调用语句调用已定义过的函数(注意,一定是调用已定义过的函数,没有定义过的函数不能调用)。
3)表达式语句
一个表达式后面加一个分号“;”就构成了表达式语句
4)空语句
空语句不执行任何操作,常用于消耗机器周期,延时等待等用途。
5)复合语句
用一对大括号“{}”把一些语句括起来就构成了复合语句。在上一节的实例中我们已经学习了这种语句的用法。
 
3、if语句
if语句用来判断所给定的条件是否满足,根据判定结果选择执行给出的操作。if语句有3种基本形式
1)if(表达式)
{
...
}
....
该if语句的执行情况为:先判断表达式的结果是否为真(非0值),如果为真,则执行大括号内的语句,否则不执行大括号内的语句。
语句示例如下:
if(i==0)
{
m=10;
}
...
这条语句所实现的功能是:如果变量i等于0,则给变量m赋值为10,否则m=10这个语句不会被执行
2)if(表达式)
     {
         语句1
      }
   else
     {
        语句2
     }
其含义是:若条件表达式的结果为真,则执行语句1,否则,如果条件表达式的结果为假,则执行语句2.这里的语句1和语句2均可以是单个语句,也可以是复合语句
3、if(表达式1)
   {
      语句1
    }
    else if(表达式2)
   {
       语句2
    }
    else if(表达式3)
   {
       语句3
    }
    ....
   else
   {
       语句n
    }
这种条件语句经常用来实现多种条件的判断,它是由if-else语句嵌套而成的,在这种结构里,else总是与距它最近的if相配对。
 
4、本例子用if语句控制p0口的8个led的点亮状态,要求程序利用if语句判断两个按键是否被按下了,当检测到其中一个按键按下时,利用LED灯的状态指示那个按键被按下了
5、在keil c51中新建工程ex17,编写如下程序代码,并编译生成ex17.hex文件
//实例17:if条件判断语句试验
#include <reg51.h>           //包含头文件
sbit S1 = P1^0;      // 将S1位定义为P1.0引脚
sbit S2 = P1^1;     //  将S2位定义为P1.1引脚
   
void main(void)

 
 while(1)     //
 {
   if(S1 ==0)   //判断S1按键是否按下
  {
    P0 = 0x0f;  //如果S1按键按下,P0口高四位LED点亮
  }
  if(S2 == 0)    // 判断S2按键是否按下
  {
   P0 = 0xf0;   //如果S2按键按下,P0口低四位LED点亮
  }   
 }
}
6、在proteus中新建仿真文件ex17.dsn,原理图如下所示。

 
7、将ex17.hex文件载入单片机at89c51,启动仿真,观察运行效果
当按键S2被按下时,执行结果如下图所示

当按键S1被按下时,执行结果如下图所示


2018-05-09 21:46:27 Liuzhu_shusheng_DH 阅读数 357

一,C语言中if语句的可注意点:
                if(flag_moshi==0)

                if(0==flag_moshi)
                if(flag_moshi=0)
                if(0=flag_moshi)
   首先,毫无疑问的是 :这四个if语句中,前三个均是正确的!第四个错在:赋值运算符“=”的作用是将右侧的常量或表达式计算所得的值赋给左边的变量;

  对于第一个和第二个if语句中:if语句的使用均是正确的,相比较而言,更优先选用第二种,因为由于数学符号的思维定势;我们普遍的会将数学符号的等于”=“与C语言中的赋值”=“相混淆,而我们进行判断是否相等时,第一种方法误写成
if(flag_moshi=0)时,编译环境是不会报错的,但是如果误写成 if(0=flag_moshi)时,编译环境是会报错的,再优秀的编程高手都有可能犯错,所以,我们可以利用这一点,尽量推荐使用if(0==flag_moshi),由于赋值语句只有一种合法的格式,故使用
 if(flag_moshi=0)
二:DS1302驱动的修改:

第一种修改:
#ifndef __DS1302_H
#define __DS1302_H

/*
  程序说明: DS1302驱动程序
  软件环境: Keil uVision 4.10
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/
#include <intrins.h>
sbit SCK=P1^7;  
sbit SDA=P2^3;  
sbit RST = P1^3;   // DS1302复位   

void Write_Ds1302_Byte(unsigned  char temp);
void Write_Ds1302( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302 ( unsigned char address );
void ds1302init();
void ds1302get();
u8 str[]={59,50,11,07,05,01,18};
u8 shijian[7];
void Write_Ds1302_Byte(unsigned  char temp)
{
 unsigned char i;
 for (i=0;i<8;i++)      
 {
  SCK=0;
  SDA=temp&0x01;
  temp>>=1;
  SCK=1;
 }
}  
void Write_Ds1302( unsigned char address,unsigned char dat )    
{
  RST=0;
 _nop_();
  SCK=0;
 _nop_();
  RST=1; 
    _nop_(); 
  Write_Ds1302_Byte(address); 
  Write_Ds1302_Byte(dat/10<<4|(dat%10));            //十进制转BCD码
  RST=0;
}
unsigned char Read_Ds1302 ( unsigned char address )
{
  unsigned char i,temp=0x00,dat1,dat2;
  RST=0;
 _nop_();
  SCK=0;
 _nop_();
  RST=1;
 _nop_();
  Write_Ds1302_Byte(address);
  for (i=0;i<8;i++)  
  {  
  SCK=0;
  temp>>=1; 
   if(SDA)
   temp|=0x80; 
   SCK=1;
 }
  RST=0;
 _nop_();
  RST=0;
 SCK=0;
 _nop_();
 SCK=1;
 _nop_();
 SDA=0;
 _nop_();
 SDA=1;
 _nop_();
 dat1=temp/16;                        //取十位
 dat2=temp%16;                      //取个位
 temp=dat1*10+dat2;             //BCD码转十进制
 
 return (temp);   
}
void ds1302init()
{
 u8 i,adr;
 adr=0X80;
 Write_Ds1302(0X8E,0X00);
 for(i=0;i<7;i++)
 {
  Write_Ds1302(adr,str[i]);
  adr=adr+2;
 }
 Write_Ds1302(0X8E,0X80);
}
void ds1302get()
{
 u8 i,adr;
 adr=0X81;
 for(i=0;i<7;i++)
 {
    shijian[i]=Read_Ds1302 (adr);
  adr=adr+2;
 }
}

#endif
此种方法使用BCD码转十进制,以及十进制转BCD码,修改驱动程序较为繁琐,多了四行的修改。,但是,其意更大:相比较当您使用计数时,比如19,在往上加的话就是20,但如果利用的十六进制时,便是1A;这不利于数码管的显示!尤其在写真题的时候,毕竟十进制我们一直使用,更为亲近一些,方便使用一些吧!

第二种修改:
#ifndef __DS1302_H
#define __DS1302_H

/*
  程序说明: DS1302驱动程序
  软件环境: Keil uVision 4.10
  硬件环境: CT107单片机综合实训平台 8051,12MHz
  日    期: 2011-8-9
*/
#include <intrins.h>
sbit SCK=P1^7;  
sbit SDA=P2^3;  
sbit RST = P1^3;   // DS1302复位
u8 str[]={0X50,0X59,0X23,0X27,0X04,0X05,0X18};
u8 shijian[7];
void Write_Ds1302_Byte(unsigned  char temp);
void Write_Ds1302( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302 ( unsigned char address );
void ds1302init();
void ds1302get();
void Write_Ds1302_Byte(unsigned  char temp)
{
 unsigned char i;
 for (i=0;i<8;i++)      
 {
  SCK=0;
  SDA=temp&0x01;
  temp>>=1;
  SCK=1;
 }
}  
void Write_Ds1302( unsigned char address,unsigned char dat )    
{
  RST=0;
 _nop_();
  SCK=0;
 _nop_();
  RST=1; 
    _nop_(); 
  Write_Ds1302_Byte(address); 
  Write_Ds1302_Byte(dat);  
  RST=0;
}
unsigned char Read_Ds1302 ( unsigned char address )
{
  unsigned char i,temp=0x00;
  RST=0;
 _nop_();
  SCK=0;
 _nop_();
  RST=1;
 _nop_();
  Write_Ds1302_Byte(address);
  for (i=0;i<8;i++)  
  {  
  SCK=0;
  temp>>=1; 
   if(SDA)
   temp|=0x80; 
   SCK=1;
 }
  RST=0;
 _nop_();
  RST=0;
 SCK=0;
 _nop_();
 SCK=1;
 _nop_();
 SDA=0;
 _nop_();
 SDA=1;
 _nop_();
 return (temp);   
}
void ds1302init()
{
 u8 i,adr;
 adr=0X80;
 Write_Ds1302(0X8E,0X00);
 for(i=0;i<7;i++)
 {
   Write_Ds1302(adr,str[i]);
    adr=adr+2;   
 }
 Write_Ds1302(0X8E,0X80);
}
void ds1302get()
{
 u8 i,adr;
 adr=0X81;
 for(i=0;i<7;i++)
 {
    shijian[i]=Read_Ds1302 (adr);
    adr=adr+2;   
 }
}
#endif


2019-05-21 22:37:36 qq_35379989 阅读数 192

无论是51、arduino还是stm32,在大学期间常用的单片机都是以C语言为基础的,这里列出C语言基础的一些语句函数,希望可以帮助初学者。

目录

一、语言基础

1.1、C语言概述

1.2、C语言语法基础

1.3、 C语言的数据类型

1.4 C语言运算符与表达式

二、数据的输入和输出

2.1 字符输入输出函数

2.2 格式输入输出函数

三、选择结构程序设计语句

3.1 if语句

3.2 switch语句

四、循环结构程序设计

4.1 while语句

4.2 do...while语句

4.3 for语句

4.4 循环体结构的嵌套

4.5 break语句和continue语句在循环结构中的应用

4.6 综合实例:

五、函数与编译预处理

5.1 函数概述

5.2 函数的定义和说明

5.3 函数的调用

5.4 函数的参数

5.5 函数的嵌套调用和递归调用

5.6 变量的存储类型

5.7 内部函数和外部函数

5.8 编译预处理命令


一、语言基础

1.1、C语言概述

#include<stdio.h>
#include<stdlib.h>
int main()
{
int num1.num2;
num1=1;num2=2;
printf(“num1+num2=%d”,num1+num2)
}

(1)头文件: #include语句是预处理指令,并不是执行语句,制定了程序引用的头文件(调用库功能)。

(2)主函数:包括一个main()函数。int表示main()返回一个整数,void表示main不接受任何参数。

(3)函数结构:任何函数,包括主函数main(),都由函数说明和函数体,两部分组成

函数类型 函数名(参数表)
{
    函数体(执行语句)
}

符号//之后是程序的注释信息,仅在一行内有效

/*和*/可跨越多行进行注释

1.2、C语言语法基础

(1)字符集

C语言具有4类字符集:

英文字母:大小写各26,共52个;

阿拉伯数字:0-10,共10个;

下划线:_ ;

特俗符号:由1-2个字符组成,如:+、-、*、/、%、++、--、<、>、=、>=、<=、==、!=、&&、||、&、|、~、^、>>、<<、()、[]、{}、?:、.、,、:

除字符串和注释外,C语言程序只能用字符集中的字符来书写。

(2)标识符:标识变量名、符号常量名、函数名、类型名、文件名等的有效字符序列,

C语言标识符只能有=由字母、数字、和下划线组成,且第一个字符必须为字母或者下划线。

(3)关键字:具有特定含义的标识符,C语言中的关键字都用小写字母表示,共有32个;

auto、break、case、char、const、continue、default、do、double、else、enum、extern、float、for、goto、if、int、long、register、return、short、signed、sizeof、static、struct、switch、typedef、union、unsigned、void、volatile、while

(4)常量:程序运行过程中,其值不能改变的量

(5)变量:程序运行过程中其值可以改变的量

1.3、 C语言的数据类型

1.3.1、 C语言的数据类型

 1、整型变量:不含小数部分的数值型数据;由机器中数据的长度分为基本整型(int)、短整型(short)和长整型(long);同样整型长度的数据又分为无符号(unsigned)和有符号(signed)

其中long int、short int、unsigned int中的关键字int可以省略。

关键字

字节数

取值范围

char

1

-128~127      即-2^7~(2^7-1)

unsigned char

1

0-255         即0~(2^8-1)

short[int]

2

-32768~32767  即-2^15~(2^15-1)

unsigned short[int]

2

0~65535       即0~(2^16-1)

int

4

-2147483648~2147483647 即-2^31~(2^31-1)

unsigned [int]

4

0~4294967295  即0~(2^32-1)

long [int]

4

-2147483648~2147483647 即-2^31~(2^31-1)

unsigned [int]

4

0~4294967295  即0~(2^32-1)

2、整形常量:十进制数值表示方法与数学上相同;八进制数值在数码前面加数字0;十六进制在数码前面加0x

1.3.2 实型数据

     1、实型变量:带有小数点的数,也称为浮点数。实行数据有:单精度实数(float)、双精度实数(double)、长双精度实数(long double)

关键字

字节数

精度

float

4

6~7      即(-3.4x10^-38)~3.4x10^38

double

8

15~16    即(-1.7x10^-308)~1.7x10^308

long double

16

18~19    即(-1.2x10^-4932)~1.2x10^4932

double型变量的值的最大有效位数通常是float型的两倍。

2、实型常量:一般不分float和double,任何一个实型数据既可以赋给float变量,又可以赋给double变量

 (1)十进制小数形式:由数字和小数点组成,如3.1415、-6.5、.3

 (2)指数形式:3.12E-6表示3.12x10^-6(十进制小数+e或E+十进制整数三部分组成);e或E前必须有数字,e或E后必须是整数;精度又称为有效位由于float的精度为7,前7位有效,0.1234567E+8

1.3.3 字符型数据

    1、字符型变量:字符型变量用来存放一个字符,占用一个字节,

如:char c1,c2;

     2、字符型常量:一种是一对单引号括起来的一个字符如’A’表示大写字母A,’ ‘表空格;另一种是单引号括起来的由反斜杠(\)引导的一个字符或一个数字序列

字符形式

功能

\n

换行

\t

制表字符

\b

退格

\r

回车

\\

反斜杠字符

\’

单引号字符

\”

双引号字符

\ddd

1~3位八进制数表示的字符

\xhh

1~2位16进制表示的字符

   3、字符串常量:用双引号括起来的字符序列,“guoqing”C语言中每个字符串都是以’\0’为结束标记的;不能将字符串常量赋给一个字符变量。每一个字符均以ASCII码存放,最后一个为空字符(二进制00000000,纪委null或\0,”a”为两个字节)

1.4 C语言运算符与表达式

类型

示例

算术运算符

+、-、*、/、%

关系运算符

>、<、==、>=、<=、!=

逻辑运算符

!、&&、||

位运算符

<<、>>、~、|、^、&

赋值运算符

=

条件运算符

?、:

逗号运算符

指针运算符

*、&

求字节数运算符

sizeof

强制类型转换运算符

(类型)

分量运算符

.(点)、->

下标运算符

[ ]

其它

函数调用运算符()

 1.4.1 算术运算符和算术表达式

  1、算术运算符:+、-、*、/、%(其中+、*、/为双目符即要求两个操作数,-为单目符可仅需一个操作数取负值)

   (1)两个操作数都是整型运算结果为整型,只要有一个是实型结果为实型。如果被除数或除数有一个是负值舍入方向不确定,多数机器结果向零取整,如-5/3=-1(余-2),少数结果为-2(余1)

   (2)求余运算要求两个操作数都是整数

   (3)*、/、%优先级高于+、-运算,算术运算符的结合顺序为从左至右

  2、自增运算符与自减运算符:++、--(自增与自减操作数只能是变量,且只能是整型或字符型也可用于指针变量)

   (1)将自增自减运算符放在变量前面,其作用先给变量加一或减一,然后再使用该变量值:x=++a为a=a+1,;x=a;

   (2)将自增或自减放在变量的后面,其作用是先使用该变量的值,然后再给变量加一或减一:x=a++为 x=a; a=a+1;

   (3)自增或自减的结合方向为从右向左,这与运算符的运算方向相反,如-i++意为-(i++)

1.4.2 赋值运算

 1、赋值运算符:”=”,将一个数据或一个表达式的值赋给一个变量;

 2、赋值类型转换:左右两边变量类型不一致,但都是数值型或字符型时,全将赋值运算符右边的值类型自动转换成与左边相同的类型;

 3、必须把复合赋值符右边的表达式看成一个整体,先求右边表达式值,再和左边的变量做相关运算:a+=x+y*3为a=a+(x+y*3);

1.4.3 关系运算符

 1、关系运算符(六个):<(小于) <=(小于等于) >(大于) >=(大于等于) ==(等于) !=(不等于);

 2、优先级:==和=!同级,前四个优秀级高于后两个,算术运算符的优先级高于关系运算符的优先级,关系运算符高于赋值运算符:a=b

二、数据的输入和输出

C语言本身没有提供输入和输出语句,C语言编译系统中的stdio.h头文件包含了标准输入和输出有关变量的定义及相应的宏定义

2.1 字符输入输出函数

字符输入函数是getchar,函数原型:int getchar(void); 函数功能:从设备输入一个字符,函数的返回值是该字符的ASCII码值

字符输出函数putchar 函数原型:int puchar(int);

示例程序:

#include<stdio.h>
main()
{
 int i=97,j;
 char ch=’a’;
 j=getchar();
 putchar(i);
 putchar(j);
putchar(‘\n’);
 putchar(ch); 
}  

输入b,结果为

ab
a

字符串输入函数gets(s);  字符串输出函数:puts(s);

 #include<stduio.h>
 int main(void)
 {
  char s[5];
  gets(s);
  puts(s);
 }  

输入abcde 输出abcde

2.2 格式输入输出函数

scanf是具有格式控制输入的函数,函数格式:scanf(“格式控制字符串”,地址表);

格式控制字符串必须用英文状态的双引号括起来,主要由%和格式字符组成,作用是将输入的数据转换成指定的格式后存入地址表所指向的变量中。

d 用来输入十进制整数、c用来输入单个字符、s用来输入字符串、f用来输入实数

需注意:

(1)格式控制字符串中除格式字符外没有其他字符,两个数据之间允许以一个或多个空格隔开,也可以按回车键、跳格键隔开;

如:scanf(“%d%d%d”,&a,&b,&c);

 下面输入数据的方式都是正确的

13  1  23
13
1
23

 对于:scanf(“x=%d,y=%d,z=%d”,&x,&y,&z);

输入为:x=123,y=456,z=789

(2)可以指定scanf函数输入数据所占的宽度,系统将自动按指定宽度来截取数据。

   scanf(“%3d%4d%3d”,&x,&y,&z)  输入1234567890,则x为123,y为4567,z为890

(3)格式字符’%’后面使用‘*’时,表示该对应的数据被禁止使用

      scanf(“%3d%*4d%3d”,&x,&y,&z); 输入1234567890 x为123,4567呗跳过,890赋给y

  (4)用scanf()输入实数,格式说明符为%f,但不能规定精度

  (5)格式字符串必须在地址表中有一个变量与之对应,并且格式格式字符必须与相应变量一致,如果输入不一致,scanf()将停止处理,其返回值为0,若为%c格式,空格字符和转义字符都作为有效字符输入

  格式输出函数printf可用来输出任何类型数据,而且可以同时输出多个不同类型的数据,一般调用形式为: printf(“格式控制字符串”,输出表);

三、选择结构程序设计语句

3.1 if语句

1、单分支if语句 格式:

 if(表达式)
   语句;

表达式为真,执行语句,表达式为假,不执行语句。

2、双分支if else语句:一般格式:

if(表达式) 语句1;
else(表达式) 语句2;

如果表达式的值为真(非0)执行表达式1,如果表达式值为假(0),执行语句2;

#include<stdio.h>
main()
{
 float score;
 printf(“input score\n”);
 scanf(“%f”,&score);
 if(score>=60)
  printf(“pass\n”);
 else
  printf(“fall!\n”);
}

3、多分支选择语句
         一般格式:

 if(表达式) 语句1;
 else if(表达式2) 语句2;
  else if 
     ....
       else 语句n+1;

首先求表达式1的值,如果值为真(非0),则执行语句1,后面的语句不再执行,if语句结束,否则再求表达式2的值,如果为真,则执行语句2

#include<stdio.h>
main()
{
 float socre;
 printf(“input score:”);
 scanf(“%f”,&score);
 if(score>=90)
  printf(“A\n”);
   else if(score>=80)
   printf(“B\n”);
else if(score>=70)
printf(“C\n”);
 else if(score>=60)
  printf(“D\n”);
}

4、if语句嵌套:如果if(表达式)或else后面的语句又包含一个或多个if语句,就称为if语句的嵌套。

 if语句两层嵌套结构如下:

if(表达式1) 
 if(表达式1_1)  语句1_1;
 else           语句1_2;
else
 if(表达式2_1) 语句2_1;
 else          语句2_2;
#include<stdio.h>
main()
{
 int x,y;
 printf(“input x:”);
 scanf(“%d”,&x);
 if(x<=0)
  if(x<=-10)
   y=x+2;
  else
   y=x-2;
else
 if(x<=10)
  y=x*2;
 else
  y=x/2;
printf(“y=%d”,y);
}

 注意:

 (1)if后面的表达式,一般为逻辑表达式或关系表达式,可以为任何数据类型(整型、实型、字符型、指针数据型)

 (2)对于双分支if else语句,else语句不能单独使用,它必须和if子句配合使用

 (3)如果if或else后面包括多条语句,需要将这多条语句用{ }括起来构成复合语句

 (4)if与else配对原则:从最内层开始,else总是与它上面相距最近且尚未配对的if配对。

3.2 switch语句

 3.2.1、switch一般形式:

switch(表达式)
{
 case 常量表达式1: 语句1;
 case 常量表达式2: 语句2;
 ......
 case 常量表达式3: 语句3
 default          : 语句n+1;
}

计算表达式的值,并逐个与其后的常量表达式值做比较,当值与某个常量表达式的值相等时,即执行其后的语句,然后不再进行判断,继续执行后面所有case后的语句。如果表达式的值与所有case后的常量表达式均不相同,则执行default后的语句。

#include<stdio.h>
main()
{
 int x;
 scanf(“%d”,&x);
 switch(x)
 { case 1:printf(“Spring\n”);
  case 2:printf(“Summer\n”);
  case 3:printf(“Autumn\n”);
  case 4:printf(“Winter\n”);
 }
}

在程序中输入1,则输出结果为

Spring
Summer
Autumn
Winter

3.2.2、break语句

为了实现在执行满足条件的语句后就使流程跳出switch结构,break语句可以达到这个目的。

switch(表达式)
{ case 常量表达式1: 语句1; break;
 case 常量表达式2: 语句2; break;
 .......
 case 常量表达式n: 语句n; break;
 default          ; 语句n+1;
}
#include<stdio.h>
main()
{
 int x;
 switch(x)
 { case 1:printf(“Spring\n”); break;
  case 2:printf(“Summer\n”); break;
  case 3:printf(“Autumn\n”); break;
  default:printf(“Winter\n”); break;
 }
}

例程:输入两个整数和运算符,要求计算结果。

#include<stdio.h>
void main()
{
 int num1,num2;
 char sign;
 printf(“input expression:\n”);
 scanf(“%d%c%d”,&&num1,&sign,&num2);
 switch(sign)
 {
  case ‘+’:printf(“%d\n”,num1+num2); break;
  case ‘-’:printf(“%d\n”,num1-num2); break;
  case ‘*’:printf(“%d\n”,num1*num2); break;
  case ‘/’:printf(“%d\n”,num1/num2); break;
  default:print(“input error\n”);
 }
}

注意:

 (1)case后的各常量必须各不相同,且必须是整数或者字符型;

 (2)case后允许有多个语句,可以不用{ }括起来;

 (3)case子句出现的顺序不会影响运行结果;

 (4)如果多种情况需公用一组执行语句,可用case的常量表达式将多种情况列出,在最后一种情况之后安排需要执行的语句。

 switch(grade)
 { case 9:
  case 8: printf(“Good\n”);
  case 7: 
  case 6: printf(“pass\n”);
  default: printf(“Fail\n”);
}

grade的值为9或8时,输出good!;grade的值为7或6时,输出Pass!;grade的值不是9/8/7/6中的任何一个时,输出Fail!

四、循环结构程序设计

4.1 while语句

一般表达式:

while(表达式)

循环体结构;

如果表达式的值非0,就执行循环体语句,然后再计算表达式的值,由表达式的值决定是否再次执行循环体语句,直到表达式的值为0时推出循环。

例程:

#include<stdio.h>
main()
{
 float score,sum=0,ave=0;
 int count=0;
 scanf(“%f”,&score);
 while(score>=0)
 { sum=sum+score;
  count++;
  scanf(“%f”,score);
 }
 if(count!=0) ave=sum/count;
 printf(“ave=%.2f\n”,ave);
}

编写程序,判断一个数是否为素数

#include<stdio.h>
main()
{
 int m,i=2;
 scanf(“%d”,&m);
 while(m%i!=0&&i<=m-1)
   i++;
 if(i==m)
   printf(“%d is prime\n”,m);
 else
   printf(“%d isn’t prime\n”,m);
}

4.2 do...while语句

一般格式:

do

  循环体结构;

while (表达式);

先执行一次循环体语句,然后计算表达式,如果表达式的值非0,则重复执行一次循环体语句,直到表达式的值为0时推出循环,语句结束。do...while的特点是先执行再判断。

例程:

#include<stdio.h>
main()
{
 float score,sum=0,ave=0;
 int count=0;
 scanf(“%f”,&score);
 if(score>=0)
  do
  { sum=sum+score;
   count++;
   scanf(“%f”,score);
  } while(score>=0);
 if(count!=0) ave=sum/count;
 printf(“ave=%.2f\n”,ave);
}

注意:

(1)while中的表达式通常是关系表达式或逻辑表达式,表达式的值可以为任意值;

(2)循环体中必须要有能使循环体结束的语句,否则形成无限循环;

(3)while语句先判断表达式的值,若表达式的值一开始就为0,循环体语句一次都不执行;do...whilw语句的循环体至少执行一次。

例程:根据公式s=1=1/(1+2)+1/(1+2+3)+1/(1+2+3+4)+...+1/(1+2+3+4+..+n)

#include<stdio.h>
main()
{
 int n,k,t=0;
 double s=0.0;
 printf(“\n please enter n:”);
 scanf(“%d”,&n);
 k=1;
 do
 { t=t+k;
  s=s+1.0/t;
  k++;
 } while(K<=n);
 printf(“\n The result is %lf\n”,s);
}

4.3 for语句

for语句不仅适用于循环次数确定的情况,还适用于循环次数不确定而只给出循环结束条件的情况。

一般格式:

for(表达式1;表达式2;表达式3;)

  循环体语句;

首先求表达式1的值,然后求表达式2的值,如果表达式2的值为真(非0),则执行循环体语句,接着求表达式3的值,然后再求表达式2的值,如此反复,直达表达式2的值为0;

例程:

#include<stdio.h>
main()
{
 int i,n;
 long fac;
 scanf(“%d”,&n);
 fac=1;
 for(i=1;i<=n;i++)
  fac=fac*i;
 printf(“\n%d!=%ld\n”,n,fac);
}

说明:

(1)如果在for语句之前给循环体变量赋了初值,则表达式1可以省略,但其后的分号不能省略,如果3个表达式都省略,循环体中的语句就会无限次循环,即死循环;

(2)逗号表达式在for语句中的运用。for语句中的表达式1和表达式3都可以使用逗号表达式,特别是在两个循环变量控制控制循环的情况下,例如:

int i,j,sum=0;
for(i=1,j=100;i<=j;i++,j--)
sum=sum+i+j;

例程:输入一个整数n,计算n(包括n)以内能被5或7整除的所有自然数的倒数之后并输出。

#include<stdio.h>
main()
{
 int i,n;
 double sum=0.0;
 printf(“\nInput n: “);
 scanf(“%d”,&n);
 for(i=1;i<=5;i++)
  if(i%5==0||i%7==0)
  sum=sum+1.0/i;
 printf(“sum=%lf\n”,sum);
}

4.4 循环体结构的嵌套

在循环体语句中又包含一个完整的循环体结构,称为循环体结构的嵌套。

例程:编写程序输出如下图形:

*
**
***
****
*****
#include<stdio.h>
main()
{
 int i,j;
 for(i=1;i<=5;i++)
   {
for(j=1;j<=1;j++)
 printf(“*”);
printf(“\n”);
}
}

例程:编写程序求100~1000之间素数的个数。

#include<stdio.h>
main()
{
 int m,i,k,count=0;
 for(m=101;m<1000;m+=2)
 {
  i=2;
  while(m%i!=0&&i<=m-1)
   i++;
  if(i==m)
   count++
  }
 printf(“The prime number is %d\n”);
}

4.5 break语句和continue语句在循环结构中的应用

  1. break语句

一般格式:、break;

break语句是限定转向语句。break语句常在switch语句和循环结构中出现。在循环结构中通常与if语句在一起使用,一旦瞒住条件就使程序立即退出循环体结构,转而执行循环结构后面的语句。

例程:从键盘上连续输入数据,如果是正数,则累加;如果是负数,则程序结束。

#include<stdio.h>
main()
{
 int x;
 long sum=0;
 for( ; ; )
 { scanf(“%d”,&x);
  if(x>=0) sum+=x;
  else break;
 }
 printf(“sum=%ld\n”,sum);
}

2、continue语句

一般格式:

continue;

continue语句被称为继续语句。continue一般也与if语句一起使用,一旦条件成立,就跳过循环体中continue语句之后的语句段,提前结束本次循环体的执行,接着进行下一次循环条件的判断。

例程:编写程序输出100~200之间能被7整除的数。

#include<stdio.h>
main()
{ int n;
 for(n=100;n<=200;n++)
 { if(n%7!=0;
  continue;
  printf(“%5d”,n);
 }
 printf(“\n”);
}

break语句和continue语句的区别是,一旦条件满足,break语句则结束整个循环,不再进行循环条件的判断;而continue语句只是结束本次循环,接着进行下一次循环条件的判断。

break语句:

for(n=1;n<=10;n++)
{
 if(n==5)
  break;
 printf(“%3d”,n);
}

运行结果:1 2 3 4

continue语句:

for(n=1;n<=10;n++)
{
 if(n==5)
  continue;
 printf(“%3d”,n);
}

运行结果:1 2 3 4 6 7 8 9 10

4.6 综合实例:

编写程序输出所有的水仙花数:水仙花数是指3位数的各位数字的立方和等于这个数本身

#include<stdio.h>

main()

{ int unit,ten,hundred,n;

 for(n=100;n<1000;n++)

 {

  hundred=n/100;

  ten=n/10-hundred*10;

  unit=n%10;

  if(n==unit*unit*unit+ten**ten*ten+hundred*hundred*hundred)

   printf(“%6d”,n);

 }

}

编写程序求解百钱买百鸡问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。

设变量a、b分别代表鸡翁、母鸡的个数,鸡雏的个数为100-a-b。a在0~19,b在0~33。

#inlcude<stdio.h>
main()
{
 int a,b,c;
 for(a=0;a<=19;a++)
  for(b=0;b<=33;b++)
  { c=100-a-b;
   if(5.0*a+3.0*b+c/3.0==100)
   printf(“a=%d,b=%d,c=%d\n”,a,b,c);
  }
}

 

五、函数与编译预处理

C语言源程序是由函数组成的。前面的程序大都只有一个主函数main(),在C语言程序设计中,通常将一个较大程序分成几个功能单一的子程序模块,用函数来实现每个子程序的1功能。C语言程序由一个或多个函数构成,其中有且只有一个名为main()的主函数,C语言总是从main()函数开始执行,最后在main()函数结束整个程序的运行。

5.1 函数概述

(1)一个C源程序必须有且只有一个主函数main()。C程序总是从main()函数开始执行,调用其他函数后总是回到main()函数,最后在main()函数中结束整个程序;

(2)一个C程序由一个或多个源文件组成——可分别编写、编译和调试;

(3) 一个源文件由一个或多个函数组成,可为多个C程序公用;

(4)C语言是以源文件为单位而不是以函数为单位编译的;

(5) 在C语言中,所有函数(包括主函数)。一个函数的定义,可以放在程序中的任意位置,主函数main()之前或之后。但在一个函数的函数体内,只能调用其他函数,不能再定义另一个函数,即不能嵌套定义;

(6)主函数名main()是系统定义的,是运行时首先调用的函数,它可以调用其他函数,但不能被其他函数调用了其他函数间可以相互调用,也允许嵌套调用。习惯上把调用者称为主调函数。函数可以自己调用自己,称为递归调用;

(7) 函数定义上看可分为库函数和用户定义函数两种:
                 1 库函数:由C系统提供,用户无需定义,只需在程序前写出该函数原型的头文件即可;

      2 用户定义函数:用户按需编写的函数,不仅要在程序中定义函数本身,而且还必须在主函数模块中对该被调函数进行类型说明,才能使用;

 (8)从主调函数与被调函数之间数据传送角度来看,函数又分为无参函数和有参函数两种。

对库函数一般调用形式

函数名(参数表)

例程:

#include<math.h>
#include<stdio.h>
main()
{
 double a,b;
 scanf(“%lf”,&a);
 b=sin(a);
 printf(“6.4lf”,b);
}

include命令必须以#开头,系统提供的文件以.h为后缀,文件名用一对双引号或者一对尖括号<>括起来,二者的区别是:<math.h>表示编译时只按系统标准方式检索文件目录,而用”math.h”时,编译系统先从目标文件所在的子目录中找math.h文件,若找不到在用尖括号方法搜索

注意:include是命令,不是语句,结尾没有分号。

5.2 函数的定义和说明

5.2.1 函数的定义格式

[函数返回值的类型名] 函数名([类型名 形式参数1,类型名 形式参数2,...])
/*函数首部*/
{
 [说明部分;]  /*函数体*/
 [语句部分;]
}

其中[]内为可选项。

注意:函数名、一对圆括号和花括号不能省略。

1、无参数的一般形式

无参函数的一般形式为:

函数返回值的类型名 函数名(void)
{ [说明语句部分;]
 [可执行语句部分;]
}

新标准中,函数不可以默认参数表,如果不需要参数,则用void表示,主函数main()除外。

例程:构造一个输出一行“*”的函数

void printstar()
{  printf(“***********\n”);
}

2、有参函数的一般形式

有参函数的一般形式为:

函数返回值的类型名 函数名(数据类型 参数[数据类型 参数2...])
{ [说明语句部分;]
 [可执行语句部分;]
}

有参比无参多了一个参数表。将函数定义中的参数表称为形式参数表

例程:构造一个求两个双精度数之和的函数。

double add(double x,double y)
{ double s;
 s=x+y;
 return s;
}

注意:

  (1) 函数名和形式参数都是用户命名的标识符。同一程序中,函数名必须唯一;形式参数只要在同一函数中唯一即可,可以与其他函数的变量同名;

  (2)C语言规定,不能在一个函数的内部再定义函数

 对函数类型名的说明,必须与return语句中返回值表达式的类型一致。如果不一致,则以函数类型为准,由系统自动进行转换,如果省略函数标准类型,系统一律按照int类型处理。

例程:求两个数中的大数(例中函数调用时使用了类型转换)

#include<stdio.h>
max(float x,float y)
{
 float z;
 z=x>y?x:y;
 return z;
}

main()
{
 float a=1.5,b=0.5;
 float c;
 c=max(a,b);
 printf(“max is %f\n”,c);
}

运行结果: max is 1.000000

C语言中函数补充内容:

  (1) 空函数:无参数且函数体为空的函数,一般形式为:

[函数类型] 函数名(void) {}

例如:

dump() {}

 (2) 带参数的形式参数表中类型和变量必须成对出现,如下定义是错误的:

  double add(double x,y)

5.2.2 函数的返回值

函数的返回值就是return返回语句中的表达式的值。

return语句格式:

return(表达式); 或  return 表达式;  或  return;

功能:

 (1) 把return后面的表达式的值返回给调用函数

 (2)把控制转向调用函数

例如:return(x);  return x+y;  return; 都是合法的

注意:void型的函数中不能包括带值得return语句;主体函数体内不能出现return语句。

 (1) 当函数没有return语句时,以结束函数的大括号作为返回点。这时的返回值是系统给的不确定值,】。假如为了明确表示不返回值,可以用void定义成无(空)类型;

 (2)在同一函数中可根据需要在多处出现return语句,但函数第一次遇到return时就立即停止执行,并返回到主调函数。

例程:编写程序使两个字符s1和s2相等时,返回1,否则返回-1

int find_char(char s1,char s2)
{
 if (s1==s2)
  return 1;
 return -1;
}

以下程序是合法的

#include<stdio.h>
double add(double x,double y)
{
 double s;
 s=x+y;
 return s;
}

main()
{
 double a,b,c;
 a=10;b=20;
 c=add(a,b)                   /*1*/
 printf(“%lf”,add(a,b));      /*2*/
 add(a,b);                    /*3*/
}

在/*1*/中,add的返回值被赋予c,在/*2*/行中,返回值实际上没有赋给任何变量,单被printf所使用。在/*3*/行中,返回值被丢弃不用

5.2.3 函数的说明

 1、函数说明也称为函数声明,是函数调用前的准备。在首次调用某函数前,使用函数说明语句使C语言编程程序了解函数返回值类型,这个信息对于程序能否正确运行影响极大,因为不同类型的数据有不同的长度和内部表示方法,在ANSI新标准中,采用函数原型方式,对被调函数进行说明,格式如下:

函数类型 函数名(数据类型[参数名1][,数据类型[参数名2]...]);

其中[]内为可选项。若没有参数,可用void明确说明。

函数说明语句就是函数定义中的函数首部加上分号,这些内容称为函数原型,例如:

float max(float x,float y);

等价于

float max(float,float);

函数说明的例子如下:

void srand(unsigned int seed);

函数srand 无返回值,仅有一个unsigned int类型的参数

说明:

 (1) 函数说明可以是一条独立的语句,也可以与普通变量一起出现在同一个说明语句中,如:

float x,max(float,float);

  (2)在函数名前没有说明函数类型时,默认为int类型,如int dump(int); 等价于dump(int);

2、函数说明的位置

(1)C语言规定,被调用函数的说明位置处在被调用前并且在所有函数的外部时,则后面所有外置上都可以对该函数进行调用。

(2)函数说明放在调用函数内的说明部分,这时其作用只在本调用函数内部。

例程:

#include<stdio.h>
main()
{
 float a=1.5,b=0.5;
 float max(float,float);
 float c;
 c=max(a,b);
 printf(“max is %f”,c);
}

float max(float x,float y)
{
 float z;
 z=x>y?x:y;
 return z;
}

运行结果 max is 1.500000

5.3 函数的调用

一般形式为:

函数名([实际参数表])

[]内为可选项。当实际参数多于一个时,各实际参数间以逗号隔开。实际参数可以是常量、变量或表达式,如max(float x,float y)的调用可为max(2,3)、max(3+4.2,7*a)、max(a,b)等

 调用时,首先计算实参表达式的值,然后把这些值按顺序一一传给对应的形参。当函数无参数时,实参列表就位空。

说明:

 (1)调用函数时,函数名必须与具有该功能的自定义函数名完全一致;

 (2) 实参的个数必须与所调用函数的形参个数相等;

 (3)实参表中包括多个参数,对参数的求值顺序因系统而异,有的系统自左向右,有的系统则相反,VC6.0、Turbo C 和MS C是按自右向左的顺序进行

#include<stdio.h>
main()
{
 int a=1,b,f(int,int);
 b=f(a,++a);
 printf(“%d”,b);
}

int f(int x,int y)
{
 int z;
 if(x>y) z=1;
 else
   if(x==y) z=0;
   else z=-1;
 return(z);
}

运行结果0

在一个函数中调用另一个函数需要具备的条件

(1)被调函数已经存在(库函数或用户自定义)

(2)使用库函数或其他文件中的函数,应在文件开头用#include命令将有关编译处理信息包含到文本中。

(3)对被调用的函数进行说明

5.4 函数的参数

函数中的参数分为形参和实参

(1)形参与实参:调用函数时形参才分配内存单元,同时实参将数据传给实参,调用结束后形参所占内存单元被释放,实参单元保持原值;

(2)实参可以是变量、常量、表达式

(3)实参和形参应个数相等、类型系统

(4)调用函数时,主调函数和被调函数之间有数据传递关系,实参对形参的数据传递是单向的,只能由实参传给形参,反之则不行,即形参的改变不会影响实参

 例程:编写程序实现从两整数中求较大数

#include<stdio.h>
float max(float x,float y);
main()
{
 float a,b;
 float c;
 scanf(“%f”,&a,&b);
 c=max(a,b);
 printf(“a=%f,b=%f,c=%f\n”,a,b,c);
 }

float max(float x,float y)
{
 float z;
 z=x>y?x:y;
 printf(“x=%f,y=%f\nz=%f\n”,x,y,x);
 return(z);
}

输入:2,3

运行结果:

x=2.000000 y=3.000000
z=3.000000
a=2.000000 b=3.000000
max=3.000000

(1)在上面的max函数中,假若把z=x>y?x:y;改为z=++x>++y?x:y;若执行时输入2,3,执行后结果为:

x=3.000000,y=4.000000
z=4.000000
a=2.000000,b=3.000000
max=4.000000

(2)多次调用时,形参分配到的内存单元已经被收回,故形参分配到的内存单元不一定系统

(3)形参和实参分配存储单元的时刻与作用范围不同,分配到的存储单元也不同,故形参和实参可以同名

例程:

#include<stdio.h>
main()
{
 int x=10,y=30;
 printf(“Before swap:x=%d y=%d\n”,x,y);
 swap(x,y);
 printf(“After swap :x=%d y=%d\n”,x,y);
}

int swap(int x,int y)
{
 int t;
 t=x;x=y;y=t;
 printf(“In swap function:x=%d y=%d\n”,x,y);
}

运行结果:

Before swap:x=10 y=30
In swap function:x=30 y=10
After swap:x=10 y=30

程序执行到语句swap(x,y)时,进行参数传递,形参变量x,y进行了交换,执行完毕收回形参x,y的存储单元,返回到主函数,主函数中实参x,y没有变化

5.5 函数的嵌套调用和递归调用

函数的嵌套调用是指,在执行被调用函数时,被调用函数又调用了其他函数。

例程:

#include<stdio.h>
int dif(int x,int z);
int max(int x,int y,int z);
int min(int x,int y,int z);
void main()
{
 int a,b,c,d;
 scanf(“%d%d%d”,&a,&b,&c);
 d=dif(a,b,c);
 printf(“Max-Min=%d\n”,d);
}

int dif(int x,int y,int z);
{
 return max(x,y,z)-min(x,y,z);
}

int max(int x,int y,int z)
{
 int r;
 r=x>y?x:y
 return(r>z?r:z);
}

int min(int x,int y,int z)
{
 int r;
 r=x<y?x:y;
 return(r<z?r:z);
}

运行结果:输入 5 9 7后

Max-Min=4

5.5.2 函数的递归调用

1、递归的概念:一个函数在它的函数体内,直接或间接地调用自己,满足以下三个条件,可使用递归法:
(1) 可以把求解问题转化成一个新问题,这个新问题与原来问题的解法相同,只是处理的对象的值有规律地递增或递减;

(2)可以应用这个转化过程使问题得到解决;

(3)必定有一个明确的结束递归的条件(一定要有递归出口)

例程:用递归法求n!

#include<stdio.h>
main()
{
 int n;
 float s;
 float f(int);
 printf(“Input n=”);
 scanf(“%d”,&n);
 s=f(n);
 printf(“%d!=%.0f”,n,s);
}

float f(int x)
{
 int t;
 if(x==0) t=1;
 else t=f(x-1)*x;
 return t;
}

运行结果:

Input n=3
3!=6

在求n!中,既可以使用递推法也可以使用递归法:

 递推方法:0!=1->1!=0!X1->2!=1!x2.....n!=(n-1)!xn

 递归方法:首先回推:欲求n! 先求(n-1)! .... 0!;

           再递推:直到1!->2!...n!

递归和迭代之间的选择,一般情况,当一个问题既可以选择递推又可以使用递归应该避免使用递归,因为迭代避免了一些列函数调用和返回中所涉及的参数传递的时间耗费和返回值的额外开销,故比递归方法快,占用空间小。但有些问题很难建立一个迭代方法,用递归方法却很容易,如汉诺塔问题:

汉诺塔问题:有三个塔,每个都放若干个盘子。开始所有盘子都在A上,盘子从上到下,按直径增大,设计一个盘子移动号顺序使得A上的所有盘子借助于塔B移动到C上

(若只有一个盘子直接从A到C,若有一个以上:1、把n-1个盘子依照题目中的规则从A借助于塔C移动到塔B,2、将剩下一只盘直接从A到C,3、再次将B塔上的n-1个盘子借助于塔A搬到塔C)

#include<stdio.h>
void hanoi(int m,int a,int b,int c)
{
 if(n==1)
  printf(“%d->%d”,a,c);
 else
 {
  hanoi(n-1,a,c,b);
  printf(“%d->%d”,a,c);
  hanoi(n-1,b,a,c);
 }
}

main()
{
 int n;
 printf(“input n:”);
 scanf(“%d”,&n);
 hanoi(n,1,2,3);
}

例程:反向输出一个整数

反向输出一个正整数的算法归纳为:

if(n为一位整数)
 输出n;
else
 { 输出n的个位上的数字;
   对剩余数字组成的新整数重复”反向输出操作”;
 }
#include<stdio.h>
void main()
{
 void printn(int x);
 int n;
 printf(“Input n=”);
 scanf(“%d”,&n);
 if(n<0)
 {
  n=-n;putchar(‘-’);
 }
 printn(n);
}

void printn(int n)
{
 if(x>=0&&x<=9)
 printd(“%d”,x);
else
 {
  printf(“%d”,x%10);
  printfn(x/10);
 }
}

5.6 变量的存储类型

5.6.1 变量的存储类型

C语言中的变量必须先定义后使用,变量的类型决定了计算机为变量预留多少存储空间。C语言中变量分为全局有效、局部有效和在复合语句内有效3中。

一个完整的变量说明格式如下:

[存储类型] 数据类型 变量表; 或 数据类型 [存储类型] 变量表;

C语言中变量有4中存储类型:自动变量(auto)、寄存器变量(register)、静态变量(static)、外部变量(extern)。

根据它们在内存中的位置,又分为自动和静态两类。其中auto、register描述的时自动,存储在动态存储区;static、extern描述的时静态类,存储在静态存储区。

用户程序的存储一般分为三个区,如下表:

动态存储区(堆栈)

静态存储区

程序代码区

静态存储区存放的变量在编译时分配存储单元,程序结束才收回存储单元。动态存储区的变量会在程序运行期间根据需要随时动态分配存储空间。

C语言中所有的变量都有自己的作用域。变量说明的位置不同,其作用域也不同据此将C语言中的变量分为局部变量(内部变量)和全局变量(外部变量)

5.6.2 局部变量

在函数内部或复合语句内部定义的变量称为局部变量。函数的形参也属于局部变量。局部变量的作用域时本函数内部或复合语句内部。所以局部变量也称“内部变量”。

局部变量分为:自动变量、寄存器变量、静态局部变量。

主函数定义的局部变量只能在主函数中使用,其他函数不能使用。同时,主函数也不能使用其他函数定义的局部变量,这一点是与其他语言不同的。

 1. 自动变量

在函数内部或复合语句内部定义的变量,如果没有写明存储类,或使用了auto说明符,系统就认为所定义的变量属于自动变量类别,有时也称为(动态)局部变量、

形参是被调用函数的局部变量。形参默认的关键字是auto,但不能将auto直接加在形参前面。

自动变量生存期为:函数调用时,分配存储单元,函数返回时,收回存储单元;复合语句执行前分配存储单元,复合语句执行后收回存储单元。自动变量的初始化是:每一次调用,形参都以实参为初值。所以未赋初值的自动变量“无定义”,其值不定。

注意:复合语句内部,所在函数的同名局部变量被屏蔽掉,只有复合语句内部的同名变量有效,如下程序:

#include<stdio.h>
main()
{
 int x=1;
 { int x=2;
  { int x=3;
   printf(“%d”,x);
  }
  printf(“%d”,x);
 }
 printf(“%d”,x);
}

运行结果:

3
2
1

3个x各有自己的存储空间和作用域,互不冲突,其中内层x屏蔽外层x的访问。

2 寄存器变量

register类型变量存放在CPU的寄存器中,它们属于自动变量。运行程序中,访问位于寄存器中的值比访问内层中的值快很多。

例程:计算的值。i=0,1,2...9

#include<stdio.h>
double power(int x,register int n)
{
 register int i;
 int p=1;
 for(i=0;i<n;i++)
  p=p*x;
 return(p);
}

main()
{
 register int i;
 for(i=1;i<10;i++)
  printf(“i=%d:%.0lf\t,%.0lf\n”,i,power(2,i),power(-3,i));
}

程序说明:

 a、只有自动变量和形参可以被定义为register变量,寄存器变量只能是int、char和指针类型的变量;

 b、 register变量没有地址,不能对它进行求址运算,因为它放在寄存器中而不是内存中;

 c、 register只是对编译程序的一种建议,当没有足够的寄存器空间编译程序将自动按auto处理

3 静态局部变量

在函数体(复合语句)内部,用以下定义格式定义的变量称为静态局部变量:

static 数据类型 变量表

例如:

static int a=8;

关键字static使得变量存储在内存的静态存储区,编译程序为之生成永久存储单元,其生存期为整个程序的一次运行。

静态局部变量的初始化只在编译时进行一次,每次调用它所在的函数时,不再重新赋初值,只是保留上次调用结束时的值。若定义但不初始化,则自动赋以0(数值型)或‘\0’(字符型)

例程:

#include<stdio.h>
main()
{
 int f(void);
 int j;
 for(j=0;j<3;j++)
  printf(“%d\n”,f());
}

int f(void)
{
 static int x=1;
 x++;
 return x;
}

运行结果:

2
3
4

函数f被调用3次,由于x是静态局部变量,编译时分配存储空间,故每次调用函数f时,变量x不再重新初始化,保留加1的值,得到上面的输出结果。

5.6.3 全局变量和静态全局变量

当变量定义在函数体外时,该变量就称为全局变量,全局变量也称为外部变量。

对于全局变量,可用extern和static两种说明符,用static修饰的全局变量称为静态全局变量(静态外部变量)。全局变量和静态全局变量都属于静态存储类。生存期都是程序的一次执行,定义和初始化都是在程序编译时进行的,初始化只有一次。若没有初始化,则自动赋以’0’或’\0’。

  1. 全局变量

全局变量不属于任何一个函数,其作用域:从全局变量的定义位置开始,到本文件结束为止。全局变量可为各函数所共享,所以函数之间数据交流可以使用全局变量,但并不提倡使用。

一个C语言程序可以由一个或多个源程序文件组成,每个源程序文件作为一个编译单位单独进行编译,然后连接成一个可执行文件exe。假如每个文件都定义了一个同名全局变量,则分别编译时将分别给每个变量分配一个存储空间,连接时就会出现同一个变量名“重复定义”的错误。此时可通过外部变量说明实现在其他文件中无需再次定义而直接使用。

外部变量说明的一般形式:

extern 数据类型 全局变量1[全局变量2...]

其作用是声明这些变量是在别处已经定义的全局变量,通知编译程序无需再给它分配存储单元,这时该变量的作用域进行了延伸。若外部变量出现引用它的函数中,则该变量的作用域从extern说明处起,延伸到了该函数末尾。函数外的extern变量延伸到整个文件结束。

例程:

#include<stdio.h>
void try_1(void)
{
 extern int i;          /*先用extern进行说明,说明i是在后面定义的外部变量*/
                        /*i的作用域延伸为从此处到整个文件的结束*/
 i=i+5;
}

int i;
main()
{
 try_1();
 printf(“i=%d\n”,i);
}

运行结果:i=5

如果将全局变量的作用范围扩展至整个源文件,可以将变量说明放在文件头部。

例程:

/*FILE1*/
#include<stdio.h>
int i;
void func();
main()
{
 i=5;
 printf(“FILE1:%d\n”,i);
 func();
}

/*FILE2*/
extern int i;      /*外部变量说明,说明文件FILE2中的变量i是引用File1中定义的外部变量i*/
void func()
{
 printf(“FILE2:%d\n”,i);
}

运行结果:

FILE1:5
FILE2:5

说明:

a当程序中多个函数使用同一数据时,全局变量很有效,可加强函数模块之间的联系,但又使这些函数依赖全局变量,使得独立性降低。在编制大型程序时变量值可能在程序其他地点被改变;

b在同一源文件中,容许全局变量和局部变量同名。此时,在局部变量的作用域内,全局变量将被屏蔽而不起作用。

例程:

#include<stdio.h>
void num()
{
 extern int x,y;    /*外部变量说明,说明num函数中的x,y是后面定义的全局变量
 int a=15,b=10;    /*变量a,b是局部变量,其作用域只在num函数内部*/
 x=a-b;
 y=a+b;
}

int x,y;            /*全局变量定义*/
main()
{
 int a=7,b=5;      /*变量a,b是局部变量,其作用域只在main函数的内部*/
 x=a+b;
 y=a-b;
 num();
 printf(“%d,%d\n”,x,y);
}

运行结果: 5,25

注意:

a、如果extern x,y;不加上extern,则函数内定义了局部变量x,y,其作用范围为函数num内部,与main函数中用的全局变量x,y无关,输出结果为12,2

b、如果程序内extern int x,y;语句不加上extern,并且全局变量定义int x,y;位于程序文件顶部,输出结果仍是12,2,因为此时虽然全局变量x,y的作用域是整个文件,但局部变量的作用域内,同名全局变量将被屏蔽而不起作用。

  1. 静态全局变量
  2. 当用static说明符说明全局变量时,此变量就称为静态全局变量或静态外部全局变量。此时static的作用不是把全局变量改为静态存储,因为全局变量本身就是静态存储类,而是限制了它的作用域只能是本文件内,不能用extern说明符扩展到其他文件中,这有利于管理大型复杂程序,有效消除了副作用。

例如,把上方两个例子中FILE1的外部变量定义int i;改为static int i;,则分别编译两个文件时一切正常,但是把这两个文件连接在一起时将产生FILE2中符号’i’无定义的错误。

5.7 内部函数和外部函数

当一个源程序由多个源文件组成时,C语言根据函数能否被其他源文件中的函数调用,将函数分为内部函数和外部函数。

5.7.1 内部函数

如果一个函数只能被本文件中的函数调用,不能被同一源程序其他文件调用,这种函数称为内部函数(又称为静态函数)。

定义一个内部函数,只需在函数类型前再加一个static关键字即可,定义格式如下:

static 函数类型 函数名(函数参数表)

{......}

例如:

static int fun(int a,int b,int c) {......}

使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数是否会与其他文件中的函数同名。

5.7.2 外部函数

在定义函数时如果没有加关键字static或冠以extern,则表示此函数是外部函数,其定义格式为:

[extern] 函数类型 函数名(函数参数表)

{......}

其中[ ]内为可选项。如:

int fun(int a,int b,int c) {......} 或 extern int fun(int a,int b,int c) {......}

若函数定义在其他文件内,或定义在调用点之前,在调用外部函数时,必须对其进行说明:

[extern] 函数类型 函数名(参数类型表[函数名1[,参数类型表2,...])

例程

a文件mainf.c中定义外部函数示例。

main()
{ extern void input(...),process(...),output(...);   /*说明本文件要用到其他文件中定义的函数*/
 input(...);process(...);output(...);             /*函数调用*/
}

b文件subf1.c中定义外部函数示例
...
extern void input(...)                       /*定义外部函数input*/
{...}

c文件subf2.c中定义外部函数示例
...
extern void process(...)                      /*定义外部函数process*/
...

d文件subf3.c中定义外部函数示例
...
extern void output(...)                       /*定义外部函数output*/
{...}

5.8 编译预处理命令

在C语言中,凡是以字符”#”开头的行都称为:编译预处理”命令行。他们实际上不是C语言的一部分,却扩展了C语言程序设计环境。

所谓”编译预处理”就是在对C源程序进行编译前,由编译预处理程序对这些编译处理行进行处理的过程。

C语言预处理命令有:#define、#undef、#include、#if、#else、#elif、#endif、#ifdef、#indef、#line、#pragma、#error。这些命令均以”#”开头,一行只写一个命令,并且末尾不能加分号,以区别于其他C语句。他们可根据需要出现在程序中的任何一行的开始位置,但通常都写在程序的开始位置。

5.8.1 宏替换

  1. 不带参数的宏定义

不带参数的宏定义形式为:

#define 宏名 替换文本

#define 宏名

后一种形式不包含”替换文本”,仅说明标识符”宏名”被定义。

#define PI 3.14

标识符PI称为”宏名”,是用户定义的标识符,通常大写,不得与程序中的其他名字相同;”3.14”是替换文本,也称”宏体”,编译时,在此命令行之后,预处理程序对源程序中的所有名为PI的标识符用字符串”3.14”来替换,这个替换过程称为”宏替换”。

替换文本可以包含已定义的宏名

#define PI 3.14
#define JAPI (PI+1)
#define JAJAPI (2*JAPI)

说明:

a、宏名一般习惯用大写字母表示,也可以用小写字母

b、使用宏名替换一个字符串,可增加可读性,也便于修改,当发生改变时,可以只改#define命令行,从而做到一改全改;

c、宏定义是用宏名代替一个字符串,简单的置换,不做正确性检查

d、宏定义不是C语句,结尾不能有分号,加了分号,会连分号一起进行置换

#define PI 3.14;

则对程序中的语句

printf(“%f”,PI*10*10);

预处理后悔被替换为:

printf(“%f”,3,14;*10*10);

e对于程序中双括号括起来的字符串内的字符,即使宏名相同,也不进行置换。

#define R 3.0
#define PI 3.14
#define L 2*PI*R

则对程序中的语句:

printf(“L=%f\n”,L);

预处理后只会替换后面的L,前面双括号的L不会替换

f宏定义是一个专门预处理命令的专用名词,只做字符替换,不分配空间;

g宏替换不占运行时间,只占编译时间,宏替换是在编译时进行的,而函数调用是在程序运行时处理的

六、数组

6.1 一维数组

1、一维数组:当数组中每一个元素只有一个下标时,称为一维数组。C语言中,数组必须显示地说明。定义一个一维数组一般形式为:

类型名 数组名[常量表达式];

例如:

int a[5];  //int时类型名,a[5]就是一维数组说明符

(1)方括号中的5规定a[5]含有5个元素,由于C语言固定每个数组第一个月元素下标总为0,所以5个数组元素表示为a[0]、a[1]、a[2]、a[3]、a[4]。此处不能使用a[5]。

(2)类型名int规定了a数组中的每一个元素都是整型,即每个元素只能存放整数。

(3)定义数组类型时可以是int、char、float、double、unsigned等;

(4)数组名后是用方括号括起来的常量表达式,不能用圆括号。常量表达式是数组的长度,即数组中所包含元素的个数。

2、一维数组的引用:C语言规定只能逐个引用数组元素,不能一次引用一个数组。一维数组元素的引用形式如下:

数组名

编程将数字0~4放入一个整数组并输出

#include<stdio.h>
#include<stdlib.h>

main()
{
	int i, x[5];
	for (i = 0; i < 5; i++)
		x[i] = i;
	for (i = 0; i < 5; i++)
		printf("%d\n", x[i]);
	system("pause");
}

(1)一个数组元素实质上是一个变量名,代表内存中一个存储单元;

(2)C语言中,一个数组不能整体引用,如数组x[4]不能用x代表x[0]到x[4]这5个元素。数组名中存放的是一个地址常量,它代表整个数组的首地址;

(3)C语言不检查数组元素下标是否越界。因此数组的两端都有可能越界进而破坏其它存储单元的数据。

3、一维数组的初始化

数组的初始化:定义数组时对数组各元素指定初值。

(1)在定义数组时对数组元素赋初值

int a[5]={1,2,3,4,5};

(2)给部分元素赋初值

int a[5]={5,4};

表明只给前两个元素赋初值,即a[0]=5,a[1]=4,其它元素自动赋0值。

(3)对全部元素赋初值,可以不指定数组长度,系统自动根据数值个数来决定数组长度

int a[]={1,2,3,4,5};

系统a定义为有5个元素的数组。

C语言if语句

阅读数 785

没有更多推荐了,返回首页