C语言——9-c语言的组成
"对象" 就是一个东西, "名字" --> 标识符
变量,数组,函数,类型,....
1 作用域
什么是作用域 ?
一个东西(对象) 起作用的范围. "可见性"
一个C语言工程,可以由多个.c文件组成.
整个工程的作用域
在工程的所有文件中,都可以起作用
本文件作用域
只在本文件(.c/.h)起作用
函数作用域
只在某个函数中起作用.
代码块作用域
只在某个代码块中起作用.
根据作用域不同,把变量分为如下几类.
1.1 全局变量 : 定义在函数外部的变量
加了static 修饰的全局变量 和 没有加static修饰的全局变量
int a = 5;//没加static修饰的全局变量 static int b = 6;//加了static修饰的全局变量 void func() { }a 普通的全局变量(没加static修饰的全局变量)
作用域是在整个工程起作用.
1.c 2.c //全局变量 int a = 5; extern int a;extern 用来声明一个对象,告诉编译器,后面的这个对象是在外部定义的.
extern 声明的语法 :
extern 外部对象的类型 外部对象名;b 加了static修饰的全局变量 : " 本文件有效 "
如果你定义了一个全局变量,只想在本文件中可用,其他文件不可用.
这个时候,需要你在定义全局变量时,在最前面加一个"static" "本地的"
static int c = 8;
1.2 局部变量 : 定义在 {}内的变量
局部变量的作用域 : 在 定义他的{ } 内有效
函数作用域
代码块的作用域
根据作用域的不同,把函数分为如下 :
1.3 全局函数 : 普通的,没有加staitc修的函数
全局函数的作用域是在整个工程有效
1.c 2.c int sum(int a,int b) { return a + b; } //extern告诉编译器,后面的那个对象是外部定义的 extern int sum(int,int); //告诉编译器, sum是一个函数 //他可能是外部定义的,也有可能是本文件定义的. int sum(int,int);
1.4 加了static修饰的函数
static函数作用域是在本文件中有效
程序员自定义的类型(如 : 结构体,联合体) 作用域,只在他声明的文件 或 函数内部有效!!!
自定义的类型的作用域, 不可以跨文件.why
类型只在编译阶段有意义,并且编译都是一个一个文件编译的.
1.c 2.c struct test { int a; int b; } //这个类型struct test只能在1.c中用. 想在2.c中用struct test该怎么办? 只能在2.c中重新定义struct test这个类型 struct test { int a; int b; }关于C语言中对象的名字, 能不能"重名"的问题.
一首诗 :
同名不要紧, 只要域不同."域" 作用域 具体是哪个, 往上就近找,"就近原则"
2 生存期
什么是生存期?
生存期是指一个对象从生到死的期间,''活着的期间''
随文件系统持续性
只要你的文件系统存在,那么这个对象就一直存在.
如 : 你保存的硬盘上的 "电影" "代码"
随进程持续性
进程 : 一个正在运行的程序.
随进程持续性,是指这个对象,进程启动的时候,他就存在,并且一直存在到进程死亡,
进程启动时再执行第一条用户指针前,就会给那些"随进程持续性的" 对象,分配空间,
并且这个空间一直到你进程死亡.
如 :
全局变量, 函数(代码) , static修饰的局部变量.
随代码块持续性
是指这个对象,在运行他所属的代码块时,才分配空间给他,
并且当代码块执行完毕时,他的空间就会自动回收.
如 :
普通的局部变量(没有加staitc修饰的局部变量) ,生存期就是随代码块持续性.
加了static修饰的局部变量
加了static修饰的局部变量,该局部变量的生存期就变成 "随进程持续性"
上述的代码中 : 局部变量a的作用域 在整个func内有效 "代码块有效" 局部变量 b 的作用域 在真个fun内有效 "代码块有效" 局部变量 a 的生存期 "随代码块持续性" 局部变量 b(加了static) 的生存期 "随进程持续性" 进程启动时(在运行 fun 前),系统就会为 b 分配空间,不管你后面fun 有没有运行 整个进程运行期间, b的空间一直不回收,直到进程结束.思考 :
1. 初始化语句 和 赋值语句的区别?初始化语句 : 定义一个对象并给对象赋值的语句, 初始化语句. 初始化语句,只会在给对象分配空间的时, 执行一次!!! 赋值语句: 每次运行他,就会执行. int a = 5;//初始化语句 a = 1024;//赋值语句
3 C文件组成
一般来说, .c文件有如下部分组成 :
3.1 头文件包含部分
include : 包含命令
include 作用是把后面的那个文件的内容在此处展开
include <stdio.h>
include <stdlib.h>
include "led.h"
include "lcd.h"
include "bmp.h"
<> " " 有什么区别?
include 后面只指定了文件名, 并没有指定该文件的绝对路径.
include 他到哪里去找这个文件呢?
<> 和 " " 区别是在于 : <> 和 " " 搜索路径不一样
include <stdio.h> 会到编译器或系统指定的标准路径下去找,如 : /usr/include
include "lcd.h" 先到工程当前的路径下去找,找不到,再去标准路径下去找.
3.2 宏定义 和 声明部分
(1) 宏
什么是宏?
宏就是一个可以替换其他东西的东西.''替换''
宏定义的语法 :
#define 宏名 要替换的内容(表达式或语句) //宏定义不能跨行,遇到换行符(\n)就认为宏定义结束了宏有两种 :
不带参数的宏 :
如 :
#define N 10 #define PI 3.14带参数的宏 :
写法有点类似函数
如 :
#define MAX(a,b) ((a > b) ? (a) : (b)) //后面用括号括起来,避免宏的副作用 //宏里面的参数猎列表,不需要类型int m = m + MAX(3,5 + 9); ==> m = m + ((3) > (5 + 9) ? (3) : (5 + 9)) //"宏仅替换" ,不会做其他处理练习 :
1.分析如下程序的结果 :
#define MAX(a,b) ((a > b) ? (a) : (b)) int main() { int i = 6; int j = 5; int m; m = MAX(i++,j);//==> m = ( (i++) > j ? (i++) : (j) ) printf("m = %d\n",m);//7 printf("i = %d\n",i);//8 printf("j = %d\n",j);//5 } //宏的副作用 :宏展开后, 表达式可能会做多次 如何修改 ? 用函数为什么要用宏? 宏的作用
(1) 替换
(2) 宏名 "字面意思" <<<<<
我们用宏能实现的功能可以用函数来实现的.
用函数能实现的功能,有时候也可以用宏来实现.
宏和函数有什么区别?
宏仅替换,在编译的时候展开的; 函数是运行时候展开的,占运行时间.
宏有时候会有副作用.
(2) 声明
什么是声明?
C语言的声明就是把一个符号(名字,标识符) 与一个对象或类型关联起来.
让编译器知道, 这个符号(这个名字) 他是什么东西.
为什么要声明?
在编译C源文件时,是一个文件一个文件去编译的,并且每个文件是
从第一行到最后一行,一行一行的去编译.
当编译器.碰到一个不认识的或没有定义的符号时,
他就会认为是一个 undefine/undeclared 的东西.
编译就结束了(不成功).
如何声明呢?
- <1> 外部变量(数组)的声明
extern 外部变量的类型 外部变量名;
- <2> 外部函数的声明
{extern} 函数返回值类型 函数名(函数参数的类型列表); { }表示可选
- <3> 类型的声明 或定义
自定义的类型 作用域最大就是本文件有效. ctrl + c ,ctrl + v 拷贝过去
- <4> 全局变量 或 函数的定义
int a = 5; void print_a(void) { }
C源程序文件是一个.c文件,这个.c文件由函数组成,C语言的指令(如 : if/switch/while/for/...) 必须写在函数的内部 , 可以由多个函数组成一个.c文件,一个工程可以由多个.c文件组成.
但是一个工程只能并且必须有一个mian函数,这个main函数就是你程序的入口.
同时main函数结束,程序就执行完毕.
4 .h文件组成
一个工程里面可不可以不要.h文件? 当然可以
.h 文件到底有什么作用?
为什么需要用到呢?
.h文件如何写呢?
liubei.c zhangfei.c guanyu.c (zhugeliang) int cm = 7; int sum(int a,int b) extern int cm; { return a + b } //需要用到guanyu.c的sum extern int sum(int,int); void func() { sum(3,4); } ....每次用到liubei.c中变量或类型或guanyu.c中的函数,都需要问一下 guanyu(如何使用,参数是什么等问题),
并且需要他们各自的源文件中, 包含 函数或类型或变量的声明 .
...
能不能把一些函数的说明(使用说明),类型的说明,变量的说明等等,
写成一个"告示/使用说明书"
接口文件?
这个接口文件("使用说明书") 就是头文件.
guanyu.h
/* sum : 求两个整数的和 @a : 第一个加数 @b :第二个加数 返回值 : 返回 a + b 的和 */ int sum(int a,int b);所有需要用到guanyu.c里面的函数,类型,变量的其他文件,就只需要包含guanyu.h
include "guangyu.h"
这个就是头文件的作用!!!
头文件就是一个接口说明文件,包含一些函数的声明(说明),类型的声明(说明),全局变量的声明...
所有的对外接口(可以给别人用的 函数, 类型,变量)
其他文件用,就直接包含就可以了.
.h文件(接口描述文件)包含哪些内容呢?
包含一些声明 : 函数的声明 , 外部变量的声明, 类型的声明
头文件一般不包含具体代码的实现,.h就是一个接口说明文件.
头文件包含的原则 : 用到谁包含谁
.h文件如何来写呢?
guanyu.h struct test { int a; int b; }; zhangfei.c #include "guanyu.h" #include "zhangfei.h" //间接 或直接多次包含了guanyu.h zhangfei.h //也用到了 struct test #include "guanyu.h" //也定义了一些其他的类型,如 : struct abc { char c; };
同一个头文件,如果被其他文件重复多次包含,就会造成 类型的 "重定义 redefinition"的错误
==> 头文件不能被重复包含!!!!
如何防止头文件被重复包含呢?
为了防止头文件内容被重复包含 , 约定 : 头文件的写法(格式) 如下 :
guanyu.h
#ifndef __文件名大写_H__ //如果 "__文件名大写_H__"没有被定义 #define __文件名大写_H__ //定义 "__文件名大写_H__" //...头文件的内容 struct test { int a; int b; }; #endif //结束前面的 #ifndef#ifndef __GUANYU_H__ #define __GUANYU_H__ /* sum : 求两个整数的和 @a : 第一个加数 @b :第二个加数 返回值 : 返回 a + b 的和 */ int sum(int a,int b); struct test { int a; int b; }; #endif
5 关于头文件
xxx.c 具体功能代码的实现 xxx.h 接口文件的说明
(1) 头文件只是一个接口文件
一般只包含声明部分或类型定义部分
不能包含具体代码的实现!!!
(2) "模块化思想"
array.c/array.h lcd.c/lcd.h ... .c 是具体的代码和功能的实现 .h 是声明和接口描述(3) 头文件的写法(格式) 如下:
#ifndef __文件名大写_H__ #define __文件名大写_H__ //...头文件的内容 #endif //结束前面的(4) 头文件包含/引用的原则
谁用谁包含
头文件也可以包含其他头文件
练习 1 :
写一个sum.c ,sum.h,main.c 三个文件,main.c中调用sum.c中求和函数.
注意sum.h的写法.
sum.c
#include "sum.h" /* sum : 求两个整数的和 @a : 第一个加数 @b : 第二个加数 返回值 : 返回 a + b 的和 */ int sum(int a,int b) { return a + b; }sum.h
#ifndef __SUM_H__ #define __SUM_H__ /* sum : 求两个整数的和 @a : 第一个加数 @b : 第二个加数 返回值 : 返回 a + b 的和 */ int sum(int a,int b); #endifmain.c
#include <stdio.h> #include "sum.h" int main() { int s = sum(3,8); printf("sum = %d\n",s); return 0; }



浙公网安备 33010602011771号