这一章呢,来讲一些之前没讲到过的一些点
1.作用域
什么是作用域?
一个东西(对象)起作用的范围。
变量的作用域
函数的作用域
类型的作用域
一个c语言工程,可以包含多个.c/.h
作用域还可以分为
整个工程的作用域
在工程的所用文件中,都可以起作用
本文件的作用域
只在本文件中起作用
函数作用域
仅在{}内起作用
a,变量的作用域
根据变量作用域的不同,分为如下几类的变量:
(1)全局变量:
作用域:整个工程作用域
定义在函数外面的变量。
1.c
int a=5; // 全局变量
2.c
在2.c中也可以使用1.c的那个a
extern int a.
(2) 由static 修饰的全局变量(静态全局变量)
定义在函数外面的变量,只不过在前面加了一个 static
static 修饰全局变量,使得被修饰的全局变量的作用域发生改变
变成仅在本文件中生效
1.c
static int b = 1024;
2.c
在2.c中想要使用1.c中的那个b,
即使你进行外部声明,那么“呵呵” 做不到了!!!
(3)局部变量
作用域: 定义在{}内,仅在定义它的那个{}可见
func()
{
int a = 5;
if(1)
{
int b =6; //局部变量
printf("a== %d\n",a); // a==5
}
printf("b==%d\n“,b) ; //ERROR
}
b,函数的作用域
(1)整个工程作用域
1.c
int sum(int a,int b)
{
return a+b;
}
2.c
想要使用1.c中sum,
需要外部声明,
extern int sum(int a,int b);
(2)本文件中作用域
1.c
static int sum(int a,int b)
{
return a+b;
}
static 修饰函数,表示被修饰的函数的作用域发生改变,变成仅在本文件中生效
2.c
在2.c中想要使用1.c中的sum ,即使使用外部声明 ”呵呵“,也做不到
c,类型的作用域
(1)本文件作用域
1.c
typedef int int32_t;
在1.c中都可以使用 int32_t
2.c
如果在2.c中要使用1.c中的int32_t,则必须在2.c中也要声明定义
typedef int int32_t;
(2)代码块作用域
func1()
{
typedef unsigned int uint32_t;
uint32_t仅在定义它的那个{}内可见
思考题
3.c
void func(void);
int a = 5;
int main()
{
int a = 6;
if(1)
{
int a = 7;
if(1)
{
int a = 8;
printf("a == %d\n", a); // 8
}
printf("a == %d\n", a); // 7
}
printf("a == %d\n", a); // 6
func(); // 5
}
同名不要紧
只要域不同。 //域:作用域
具体是哪个,
往上就近找。 // ”就近原则“
2生存期
是指一个对象从生到死的期间。
随文件系统持续性
只要一个文件系统存在,那么这个对象就一直存在
随程序持续性 / 随进程持续性
进程: 一个正在运行的程序
是指这个对象,在进程启动的时候,它就存在,并且一直持续到进程消亡。
进程启动时,就会给那些”随进程持续性“的对象,分配内存空间,并且这个空间
一直持续到进程消亡。
随进程持续性的对象;
全局变量
函数
static 修饰的全局变量(静态全局变量)
随代码块持续性
是指这个对象,在运行它所属的代码块时,才分配空间给它,
并且当这个代码块执行完毕时,它的空间就会自动收回
局部变量
练习:
分析如下代码的输出结果
void f (void)
{
int a = 7;
a++;
printf("%d\n",a);
}
int main()
{
f(); //8
f(); //8
}
void f(void)
{
static int a = 7;
a++;
printf("%d\n",a);
}
int main()
{
f(); // 8
f(); // 9
}
//这句话面试中常常会问到
static 在c语言中的作用:
只有两个作用
(1)static 修饰的全局变量和函数,作用是:
使得被修饰的全局变量和函数的作用域变为仅在本文件中有效
(2)static 修饰局部变量,作用是:
使得被修饰的局部变量的生存期,变成了随进程持续性
3C语言程序组成
一个C语言程序是由多个.c和.h组成的
.c :C源代码文件
.h :头文件,头文件是干什么用的呢?
1. .c文件的组成
一般来说,.c文件由以下部分组成
(1)头文件包含部分
#include : 包含命令
作用是把后面的那个文件的内容在此处展开
#include <stdio.h>
#include "xxxx.h"
<>与”“有什么区别?
搜索路径是不一样的
#include 后面只是指定了文件名,
并没有指定搜索的路径,那么我们的编译器到哪里去找呢?
<> :表示到标准的搜索路径下去找 INC_PATH
"" :表示先到工程指定的路径下面去找,然后再到标准路径下面去找
如果你没有指定路径的话,默认在当前目录下面找
(2)宏定义 或 声明部分
宏定义:定义一个宏
宏:宏是一个替换别人的东西。
宏有两种:
a,不带参数的宏
语法;
#define 宏名 要替换的内容
例子:
#define pi 3.14
.....
宏的作用:
1)替换
2)增加了代码的可读性
b, 带参数的宏
语法:
#define 宏名(参数列表) 要替换的内容
参数列表:在宏中的参数,不需要类型的
使用:
宏名(实参列表)
如:
写一个宏,求两个整数之间较大的那一个
#define MAX(a,b) (a)>(b)?(a):(b)
main()
{
int m = MAX(3,4);
int m = (3)>(4)?(3):(4);
}
练习:
(1)写一个宏,求数组a的元素个数
#define ARRAY_NUM(a) (sizeof(a)/sizeof(a[0]))
(2)写一个宏,取一个整数x中的第n bit。
#define GET_BIT(x,n) ((x>>n)&1)
--------------------------------------------------
声明部分:
声明:
C语言的声明是指把一个符号(名字)与一个对象或类型关联起来
告诉编译器,这个符号是个什么东东。
为什么要声明?
在编译c源文件的时候,是一个文件一个文件编译的,是从第一行到最后一行
一行一行进行编译的,遇到一个不认识或没有定义的对象名时,它就会认为这个对象
是一个undefine 的东西。
外部变量的声明
语法;
extern 外部变量的类型 外部变量名;
1.c int a = 5;
2.c 需要用到1.c里面a ---> extern int a;
外部函数的声明:
语法:
extern 外部函数的返回值类型 函数名(形参列表)
类型声明
自定义的类型的作用域,最多就是本文件中有效
1.c
struct student
{
int num;
int age;
int score;
};
2.c 需要用到1.c中的 struct student 该怎么办
只能 ctrl + c 然后 ctrl + v
(3)全局变量和函数定义
全局变量:
定义在{}外面的变量!!!
生存期是随进程持续性,
作用域是在整个工程中都有效。
int a = 6; // 全局变量
int main()
{
int b = 6; // 局部变量
}
C源程序是由函数组成的,C语言的指令必须写在函数内部,
一个.c文件,可以由多个函数组成,一个工程可以由多个.c和
.h组成,一个工程有且仅有一个main函数,这个main函数就是程序的入口
main函数结束,程序就执行完毕了。
2. .h文件组成
一个工程里面可不可以不要.h呢? 可以
.h 有什么作用呢?
zhangsan.c wangwu.c
lisi.c
int sum(int a,int b)
{
return a+b;
}
没个用到zhangsan.c中函数或变量或宏定义,都可能需要问zhangsan.c 该怎么用,并且
我们需要在他们各自的源文件,包括zhangshan的函数或类型
能不能把我们的一些函数说明,类型说明,变量说明,写成一个接口文件
需要用到的朋友们,只需要包含我提供给他们的这个接口文件就可以了。
zhangsan.h
struct test
{
char a;
int b;
};
int sum(int a,int b);
lisi.c
#include "zhangsan.h"
wangwu.c
#include "zhangsan.h"
==>so, .h 接口描述文件
包含一些声明:
函数声明
类型声明
外部变量声明
宏定义声明
===
但是,在头文件中,一般不包含具体代码的实现,它就是一个接口文件
注意:
头文件不能被同一个文件重复包含
xxx.h
struct nb250
{
int a;
char b;
};
xxx.c
#include "xxx.h"
#include "xxx.h"
编译器在编译的时候,会将头文件在原地展开
===》
struct nb250
{
int a;
char b;
};
struct nb250
{
int a;
char b;
};
在上述程序,不就是重复定义struct nb250 的类型了嘛!!!
为了防止头文件被重复包含时报错
头文件的编写规范如下:
xxx.h
#ifndef __XXX_H__
#define __XXX_H__
//...头文件的具体内容
函数声明,类型声明....
#endif
例子:
xxx.h
#ifndef __XXX_H__
#define __XXX_H__
struct nb250
{
int a;
char b;
};
#endif
xxx.c
#include "xxx.h"
#include "xxx.h"
===>
#ifndef __XXX_H__
#define __XXX_H__
struct nb250
{
int a;
char b;
};
#endif
#ifndef __XXX_H__
#define __XXX_H__
struct nb250
{
int a;
char b;
};
#endif
关于头文件:
(1)头文件是一个接口函数
一般只包含一些声明部分
不含代码的具体实现
(2) ”模块化思想“
xxx.c / xxx.h
(3)头文件的写法
为了防止头文件被重复包含时报错,头文件的结构一般如下
#ifndef __XXX_H__
#define __XXX_H__
//......
#endif
(4)头文件的引用原则
谁用谁包含
#include "xxx.h"
练习题
1,若 char str[]="Hello";char *p = str; 那么sizeof(p)=?
(4或8)
2,数组int a[10],编写表达式求数组a的长度(数组中元素的个数)
(sizeof(a))
3,以下for循环的执行次数是for(x=0,y=0;(y=123)&&(x<4);x++)
(4)
4,有以下程序,执行后的输出结果是?(程序出错)
main()
{
char s[]="abcde";
s+=2;
printf("%d\n",s[0]);bvbb
}