02_数组与函数

来源:互联网 发布:网络拓扑图怎么画好看 编辑:程序博客网 时间:2024/05/22 05:12
在前面我们学C语言的数据类型,比如int,假如我现在统计全班(30个人)入学测试的总成绩,我是不是要定义30个变量,分别存放每个同学的成绩。这样是不是很不可取。为了解决这样类似的问题,我们就要学习数组。比如:int score[30];
内容提要:
1. 数组的基本概念及应用
2. 函数的基本概念与应用
3. 变量的存储类别
4. 预编译命令

知识详解01:数组的基本概念

1. 在程序设计中,为了方便处理数据,把具有相同类型的若干变量按有序的形式组织起来,这些按序排列的相同类型数据元素的集合称为数组。(数组就像烤串一样,一个竹签上要么串鸡心,要么鸡肝,要么鸡翅,要么鸡腿,不会一串上既有鸡心又有鸡腿,那这个怎么收钱(鸡心的价格还是鸡腿的价格呢?))

2. 在C语言中,数组属于构造数据类型

* 一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或构造类型.

* 按数组元素类型的不同,数组可分为数值数组、字符数组、指针数组、结构数组等类别 

* char a[10] = {1,2,3,4,5,6};

* int b[4][2]={ {1,2},{3,4},{5,6},{7,8} };

* char string1[8] = {‘a', 'b', 'c’};

* char string2[8] = {“abcdefg”};

* char *q[10] = {&a[0],&a[1],&a[2]};

* char (*q)[10] = &a;

* struct student boy[5];

* int (*sum[10])(int x,int y);

3. 一维数组:
* int a[10];     //整型数组a,  有10个元素
* float b[10];   //实型数组b,  有10个元素
* char ch[20];   //字符数组ch, 有20个元素
4. 二维数组:
* int a[2][3]={ {80,75,78},{61,65,99} };
5. 字符数组:
* char c1[]={‘c’,’ ’,’p’,’r’,’o’,’g’};
* char c2[]=“c prog”;

知识详解02:一维数组(int , float,double,short)

1. 一维数组

* 同一个数组,所有元素的数据类型是相同的.

* 数组名的书写规则应符合标识符的书写规定. 

* 数组名不能与其它变量名相同,以下是错误的:

* void main( )

* {      

*int num;      

*float num[10];

* }

* 方括号中常量表达式表示数组元素的个数;如a[5]表示数组a有5个元素,其下标从0开始计算,因此5个元素分别为a[0], a[1], a[2], a[3], a[4]

* 不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式,例如:

* #define FD 5

* void main( )

* {    

*        int a[3+2],b[7+FD];

*        ……

* }

* 下述说明方式是错误的:

* void main( )

* {   

*        int n = 5;   

*        int a[n];   

*        ……

* }

2. 一维数组的初始化
* 在定义数组的同时对其进行赋值,称为初始化
* 可以只给部分元素赋初值
* 当{ }中数值少于元素个数时,只给前面部分元素赋值
* 例如:int a[10]={0,1,2,3,4};只给a[0]~a[4]5个元素赋值,后5个元素自动赋0
* 只能给元素逐个赋值,不能给数组整体赋值 
* 例如:给5个元素全部赋1值
* 正确:int a[5]={1,1,1,1,1};
* 错误:int a[5]=1;

* 给全部元素赋值时,可以不给出数组元素的个数

* 例如:   int a[5]={1,2,3,4,5};

* 可写为: int a[ ]={1,2,3,4,5};

* 全局数组若不初始化,编译器将其初始化为零.

* 局部数组若不初始化,内容为随机值

* C99中的指定初始化  int buf[5] = {[2]=1, [4]=1};

3. 一维数组的引用
* 数组元素也是一种变量,其标识方法为数组名后跟一个下标,在C语言中只能逐个地使用下标变量,而不能一次引用整个数组:
* void main( )
* {
*        int i,a[10];
*        for(i=0;i<10;i++)   
*            a[i]=2*i+1;         //对数组内容进行赋值  
*        
*        for(i=9;i>=0;i--)   
*            printf(“%d\n”,a[i]); //对数组内容打印输
* }

知识详解03:二维数组(在逻辑上二维数组是矩阵)

1. 二维数组
* 数组若只有一个下标,称为一维数组,其数组元素也称为单下标变量;
* 在实际问题中有很多量是二维的或多维的,例如代数中的矩阵、生产中的报表;因此C语言允许构造多维数;多维数组元素有多个下标,以标识它在数组中的位置,所以也称为多下标变量;
* 本小节将介绍二维数组,多维数组可由二维数组类推而得到
2. 二维数组的定义
* int a[3][4];
* 定义了一个三行四列的数组,数组名为a,其元素类型为整型;该数组的元素个数为3×4个,即:      
* a[0][0],a[0][1],a[0][2],a[0][3] 
* a[1][0],a[1][1],a[1][2],a[1][3]   
* a[2][0],a[2][1],a[2][2],a[2][3]

* 二维数组在概念上是二维的,即其下标在两个方向上变化.

* 实际的硬件存储器却是连续编址的,也就是说存储单元是按线性排列的,即放完一行之后顺次放入第二行(最好用画图来描述)

3. 二维数组的初始化
* 例如:数组a[2][3]
* 按行分段赋值可写为:
* int a[2][3]={ {80,75,92}, {61,65,71} };

* 按行连续赋值可写为:

* int a[2][3]={ 80,75,92,61,65,71 };

4. 二维数组的引用(讲清楚这个代码)
* void main( )
* {    
* int i,j,s=0,v[3];    
* int a[2][3]={ {80,75,92},{61,65,71} };
*  for(i=0;i<3;i++) //列
* {
*        for(j=0;j<2;j++) //行  
*        { 
*              s=s+a[j][i]; 
*        }  
*        v[i]=s/2;         //每科平均成绩  
*        s=0;      
* }    
* printf("%d %d %d\n",v[0],v[1],v[2]);
* }

知识详解04:字符数组

1. 字符数组的定义
* char c1[] = {‘c’,’ ’,’p’,’r’,’o’,’g’};//没有'\0'
* char c2[] = “c prog”;//有'\0' 
* 后面有单独的地方去讲printf、scanf
* char a[][5] = {  //一定要给出列数
*                            {‘B’,’A’,’S’,’I’,’C’},
*                            {‘d’,’B’,’A’,’S’,’E’} 
* };
* char a[][6] = { “hello” , “world” }
2. 字符数组的引用
* 用字符串方式赋值比用字符逐个赋值要多占1个字节,用于存放字符串结束标志‘\0’;
* 上面的数组c2在内存中的实际存放情况为:
* 注:'\0'是由C编译系统自动加上的
* 由于采用了'\0'标志,字符数组的输入输出将变得简单方便
3. 使用printf输出
* int main( )
* {    
* char buf[] = “hello world\n”;    
* printf("%s\n",buf);//%s遇到'\0'就结束
* }

4. 使用scanf输入
* int main( )
* {    
* char str[15];    
* printf(“input string:\n”);    
* scanf(“%s”,str);//输入的字符串别加空格,但是可以通过其他的格式 %[^\n] 进行输入空格
* printf(“%s\n”,str);
* }

5. C专门为字符数组的输入输出设置了一组函数
* #include <stdio.h>void main( )
* {   
* char str[15]="";   
* gets(str);   //很危险,容易造成内存污染,它会把我们键入的所有数据都放到buf中去,不管buf空间的大小
* puts(str); 
* }

6. gets(str)与scanf(“%s”,str)的区别:
* gets(str)允许输入的字符串含有空格.
* scanf(“%s”,str)不允许含有空格

7. C99中的编程数组:
* 在C89中谈到,数组变量的长度必须用常量表达式进行定义。但是在c99中,有时候也可以使用非常量表达式


知识详解05:函数的基本概念与应用(术语“函数”来源于数学)

1. C程序是由函数组成的,函数是程序的基本模块,通过对函数模块的调用实现特定的功能.

2. C语言不仅提供极为丰富的库函数(如Turbo C,MS C 都提供了300多个库函数),还允许用户建立自己定义的函数.
3. 用户可把自己的算法编成一个个相对独立的函数模块,通过调用的方法来使用函数
4. 函数的分类
* 从函数定义的角度看,函数可分为:
库函数和用户定义函数(系统调用、API用户编程接口)
* 从函数的返回值看,函数可分为
有返回值函数和无返回值函数
* 从函数是否需要参数看,函数可分为:
无参函数和有参函数
* 注:函数分类没有严格的原则,一切以函数完成的功能为前提

5. 库函数
* 库函数并不是C语言的一部分,它是由人们根据需要编制并提供给用户使用的,每一个C编译系统都提供了一批库函数,不同的编译系统所提供的库函数的数目和函数名以及功能并不完全相同.
* ANSI C标准给大家提供了一些建议使用的、通用的标准库函数,这些库函数在大部多数编译系统中都有所应用* 数学函数:include <math.h>
绝对值/正弦/余弦/开方等
* 字符串函数:include <string.h>
strcat/strcmp/strcpy 等
* 输入、输出函数:include <stdio.h>
printf/scanf/getchar/puts 等
* 动态存储分配函数
malloc/calloc/free/realloc 等
* 注:可以参考 《linux C函数.chm

6. 函数的定义
* [函数类型] 函数名(void)  
* {    
* 说明语句部分;    
* 执行语句部分;
* }

7. 例如:
* void Hello(void)
* {  
* int num = 1;
* printf(“num = %d\n”,num); 
* }
* 说明:
* [函数类型] :函数返回值的类型,默认为int型
* 函数名由用户定义的标识符,代表函数的首地址
* 无参数函数,函数名后的括号中一般为void

8. 有参函数的一般形式:
* 形参可以有2种表示方式
* 注意尽量用第二中方式
* 学生练习T1

9. 函数的声明
* 函数的说明和定义是不同的
函数定义是指对函数功能的确立:指定函数名,函数返回值类型,参数类型、函数体.
函数声明只对已定义的函数进行说明,包括函数类型名函数名、括号、参数类型、参数名.
* C语言不允许嵌套定义
定义一个函数的内部又定义其他函数.
* C语言允许函数的嵌套调用
在一个被调函数中又调用其它函数及递归调用
* 主函数与被调函数在同一个文件中: 
被调函数在主函数之前定义,不需要声明
被调函数返回值类型为整形或字符型不需要声明
库函数不需要声明,但必须用include包含头文件
其它情况在进行函数调用之前必须进行声明操作
* 主函数与被调函数不在同一个文件中:调用前必须进行函数声明


10. 内部函数与外部函数:
* 内部函数:一个源文件中定义的函数只能被本文件中的函数调用,而不能被其它文件中的函数调用
定义形式:static 类型说明符 函数名(形参表)
* 外部函数:外部函数在整个源程序中都有效
定义形式:类型说明符 函数名(形参表)
* 在函数定义中如没有说明extern或static则隐含为extern

11. 总之我们记住一点
* 所有的函数均进行声明,对函数进行声明时直接将定义的搬上去,后面加一个 “;”号或在前面加extern

知识详解06:变量的存储类别

1. 局部变量与全局变量
* 普通局部变量
* 在一个函数内定义,只在函数范围内有效
* 在复合语句中定义,只在复合语句中有效
* 随着函数调用的结束或复合语句的结束而消亡
* 普通全局变量
* 在函数外定义,可被本文件中其它函数所共用
* 作用域:从定义变量的位置开始到文本结束
* 生存周期:在程序运行的整个周期都存在
* 若其它文件中的函数调用此变量,须用extern声明

2. 静态局部变量与静态全局变量
* 静态局部变量 static
* 第一次进入函数调用变量时初始化,以后每次进入函数,此变量保持上一次的值,具有记忆特性
* 作用域:本函数范围内
* 生存周期:整个程序运行的周期
* 静态全局变量 static
* 在函数外定义,作用范围被限制在所定义的文件
* 不同文件静态全局变量可以重名,但作用域不冲突
* 生存周期:整个程序运行的周期
* 例子1:

* 请问输出的结果是啥?

3. 注意事项:
* 形参变量属于被调函数的局部变量,实参变量属于主调函数的局部变量.
* 允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰.
* 局部变量如不作说明,都是auto的,且auto可以省略.
* 局部变量也可以定义为register型,告诉系统对其分配地址时尽量将其分配在寄存器中,以提高访问速度
* 尽量少使用全局变量,因为在程序执行整个过程中都占用资源;会破坏函数的封闭性.
* 同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用.
* 静态局部变量如不赋初值,编译时系统会自动将其赋值(整形变量为0,指针为NULL);动态局部变量如不赋值,则为随机值

知识详解07:预编译命令(预编译处理--->编译---->汇编--->连接)

1. 预处理
* 预处理是C语言的一个重要功能,前面提到的文件包含、常量定义都属于预处理命令
* C语言提供的预处理功能主要有以下三种:
1)文件包含  #include
2)宏定义    #define
3)条件编译  #if #endif
4)防止头文件重复包含

2. 文件包含处理
* “文件包含处理”是指一个源文件可以将另外一个源文件的全部内容包含进来,C语言提供了#include命令用来实现“文件包含”的操作

3. #include< > 与 #include“ ”
* “ ”表示系统先在file1.c所在的目录找file2.c,如果找不到,再按系统指定的目录检索.
* < >表示系统直接按系统指定的目录检索.
* 注意:
1.所以用“ ”保险一些.
2.#include一般用于头文件的包含,头文件中一般为函数、结构体与全局变量的声明等

知识详解08:宏定义

1. 宏定义

* 在进行文本编辑时,“替换”是一个很有用的功能.C语言编译预处理程序也提供类似的功能:在源程序中,允许一个标识符(称为宏名)来表示一个语言符号字符串.在C语言中,“宏”分为无参数的宏和有参数的宏
2. 无参数的宏定义
* #define 宏名 字符串
* 说明:
* 1)宏名一般用大写,以便于与变量区别.
* 2)字符串可以是常数、表达式等.
* 3)宏定义不作语法检查,只有在编译被宏展开后的源程序才会抱错.
* 4)宏定义不是C语言,不在行末加分号.
* 5)宏名有效范围为定义到本源文件结束.
* 6)可以用#undef命令终止宏定义的作用域.
* 7)在宏定义时,可以引用已定义的宏名.
* 8)在头文件中定义的宏,其他源文件只要包含该头文件,同样可以使用此宏定义(对于宏没有声明这一说法)
* 例子:

%10.4f,表示宽度为10,其中的小数点后有4位

3. 带参数的宏定义
* 1)格式:#define  宏名(形参表)  字符串
* 2)调用:宏名(形参表)
* 3)宏展开:进行宏替换
* #define S(a,b) a*b
* Area = S(3,2);
* 说明:
*  用3和2分别代替宏定义中的形式参数a和 b,用3*2代替S(3,2).
* 因此赋值语句展开为:Area = 3*2;
* 对带实参的宏:如S(3,2),按#define命令行中指定的字符串从左到右进行置换.
* b.若串中包含宏中的形参(如a、b),则将程序中相应的实参代替形参.
* c.如果宏定义字符串中的字符不是参数字符(如a*b中的*号),则保留.这样就形成了置换的字符串




4. 带参数的宏与带参函数的区别:
* 1.函数调用时,先求实参表达式值,再代入形参,而宏只是简单的替换,并不求值.
* 2.函数调用在程序运行时分配内存,而宏展开时并不分配内存,也没有返回值的概念.
* 3.函数中的实参和行参都要定义类型,而且要求一致,宏名无类型,其参数也无类型.
* 4.宏替换不占运行时间,只占编译时间,而函数调用占运行时间

知识详解08:条件编译

1. 一般情况下,源程序中所有的行都参加编译.但有时希望对部分源程序行只在满足一定条件时才编译,即对这部分源程序行指定编译条件


2. 条件编译的作用
* 1、防止头文件被重复引用
#ifndef __LCD_H__
#define __LCD_H__
#endif
如果math.h、Hardware.h都引用了LCD.h,且main.c中同时引用了math.h,Hardware.h,那么会出现头文件重复被引用的现象.

* 2、同样的C源代码,条件选项不同可以编译出不同的可执行程序
* 例:
输入一行字母字符,根据需要设置条件编译,将字母全改为大写输出,或全改为小写字母输出





0 0
原创粉丝点击