前言
跟随视频【千锋教育嵌入式物联网教程800集,C语言编程入门到精通(物联网开发工程师必备视频)】 https://www.bilibili.com/video/BV1FA411v7YW/?share_source=copy_web&vd_source=9b3841f7a383f6d5e7143a03873a616b
学习笔记
提示:以下是本篇文章正文内容,下面案例可供参考
一、本章内容
- 内存的分区
- 普通的全局变量
- 静态全局变量
- 普通的局部变量
- 静态的局部变量
- 外部函数
- 内部函数
- c语言编译过程
- include
- define
- 选择性编译
二、各内容详细介绍
1.内存的分区
- 32位的操作系统中,虚拟内存(4G)被分为两个部分,3G的用户空间和1G的内核空间,其中用户空间是当进程所私有的,内核空间是一个系统中所有的进程所公有的。
进程:当一个程序在运行的时候称为进程 - 在运行程序时,操作系统会将虚拟内存进行分区
1)堆区
在动态申请内存时,在堆区开辟内存
2)栈区
主要存放局部变量
在一个函数里,所定义的一个变量,不加任何修饰都是局部变量
3)静态全局区
1:未初始化(即未赋值)的静态全局区
静态变量或全局变量,没有初始化,存在此区。
2:初始化的静态全局区
全局变量,静态变量,赋过初值的,存放在此区。
4)代码区
存在咋们的程序代码。
5)文字常量区
存放常量。
2.普通全局变量
概念:在函数外部定义的变量
int num=100; //num是一个全局变量
int main(){
return 0;
}
作用范围
全局变量的作用范围,是程序的所有地方
在同一个.c文件中,声明时前面不需要加上extern,如果要在其他.c文件中使用的话则需要加上extern 例如:extern int num;
注意:声明前不能赋值
生命周期
程序运行的整个过程 ,一直存在,直到程序结束
注意
不赋值时,默认为0
全局变量可在程序的任一位置进行操作。
3.静态全局变量
概念
静态全局变量:在定义全局变量前用static修饰
static int num=100; //num是一个静态全局变量
int main(){
return 0;
}
作用范围
static限定了静态全局变量的作用范围
只能在它定义的.c文件中使用。
生命周期
在程序的整个运行过程中,一直存在。
注意
不赋值时,默认为0。
4.普通局部变量
概念
在函数内部定义的,或者符合语句中定义的变量
int main()
{
int num; //局部变量(在函数内部定义)
{
int a; //局部变量【在复合语句(在这个大括号内)中定义,相当于在函数内部申请了一块独立的空间】
}
return 0;
}
作用范围
在函数中定义的变量,在函数中有效。
在复合语句中定义的变量,在复合语句中有效。
生命周期
在函数调用之前,局部变量不占用空间,调用函数的时候,才为局部变量开辟空间,函数结束后,局部变量才释放(函数执行完后,局部变量恢复初始状态)。
复合语句同理。
5.静态的局部变量
概念
定义局部变量的时候,前面加static修饰
作用范围
在它定义的函数或复合语句中有效
生命周期
第一次调用函数的时候,开辟空间赋值,函数结束后,不释放,以后再调用函数的时候,就不再为其开辟空间,也不赋初值给他,用的是以前的那个变量
注意
1:普通局部变量不进行初始化,则默认值为随机值。
静态局部变量不进行初始化,则默认值为0.
2:普通全局变量和静态全局变量如果不赋初值,他们的值都是为0.
6.外部函数
自己定义的普通函数,都是外部函数。
即函数可以再程序的任何一个文件中调用
在分文件编程中,只需要将函数的实现过程写在指定的.c文件中,然后将其声明写在指定的.h文件中,其他文件只要包含了头文件,就可以使用外部函数。
7.内部函数
在定义函数的时候,返回值前面加个static修饰,这样的函数成为内部函数。
static限定了函数作用 范围,在定义的.c中有效
扩展
在同一作用范围内,不允许变量重名。
作用范围不同的可以重名。
局部范围内,重名的全局变量不起作用。(向上就近原则)
例:
int num=100; //全局变量,放在静态全局区
int main()
{
printf("%d\n",num); //输出为100,用的是全局变量
int num=9; //局部变量,存放在栈区
printf("%d\n",num); //输出为9,用的是局部变量
return 0;
}
以上称为向上就近原则
8.c语言的编译过程
过程:预处理 > 编译 > 汇编 > 链接
9.include
#include<> //用尖括号包含头文件,在系统指定的路径下找头文件
#include"" //用双引号包含的头文件,先再当前目录下找头文件,找不到再到系统指定的路径下找
所以我们自己写 的头文件一定要用""双引号,不然可能报错。
注意
1:include经常用来包含头文件,也可以包含.c文件,但大家不要包含.c
因为include包含的头文件会在预编译被展开,如果一个.c文件被包含多次,展开多次,会导致函数重复定义,所以不要包含.c文件。
2:预处理只是对include等预处理操作进行处理,并不会进行语法检查,这个阶段有语法错误也不会报错,第二个阶段即编译阶段才会进行语法检查。
10.define
定义宏用define定义
宏是在预编译的时候进行替换的。
1)不带参宏
例:#define PI 3.14
在预编译的时候如果代码中出现了PI就用3.14代替
宏的好处:只要修改宏定义,其他地方在预编译的时候就会重新替换。
注意:宏定义后边没有分号。
宏定义的作用范围:
从定义的地方到本文件末尾。
如果想在中间终止宏的定义范围,用:#undef PI //终止PI的作用
2)带参宏
#define S(a,b) a*b
注意带参宏的形参a和b没有类型名。
S(2,4)将在预处理的时候替换成实参 代替字符串的形参,其他字符保留,变成2*4
例
#define S(a,b) a*b
int main()
{
printf("%d/n",S(2,4));
printf("%d/n",S(2+8,4));
return 0;
}
执行结果:
8
34
S(2+8,4)等于 34的原因是代入a*b后,2+8*4=2+32=34
若想得(2+8)*4=40的话,则宏定义为#define S(a,b) ((a)*(b))
3)带参宏和带参函数的区别
带参宏被调用了多少次就会展开多少次,执行代码的时候没有函数调用的时候,不需要压栈弹栈,所以带参宏是浪费了空间,节省了时间。
带参函数,代码只有一份,存在代码段调用的时候去代码段取指令,调用的时候要压栈弹栈,有个调用过程,所以说带参函数是浪费了时间,节省了空间。
带参函数的形参具有类型的,带参宏的形参没有类型名
如果功能实现的代码相对简单,并且不需要开辟太多的空间,可以选着使用带参宏,但大多数情况都会使用函数。
11.选择性编译
1)
#ifdef AAA
代码段一
#else
代码段二
#endif
//在当前.c中若ifdef上边定义过AAA,就编译代码段一,否则编译代码段二
注意:和if else 语句的区别,if else 语句都会被编译,而这个是通过条件选着性执行代码而选着性编译,只有一块代码被编译。
例:
#define AAA
int main()
{
#ifdef AAA
printf("hello\n");
#else
printf("no\n");
#endif
return 0;
}
执行结果为:hello
2)
#ifndef AAA
代码段一
#else
代码段二
#endif
过程:如果没有AAA,编译代码段一,否则编译代码二
和第一种互补
这种方法经常用在防止头文件重复包含,#ifndef常用于多文件编程中.h的第一行,#endif写在最后一行
3)
#if 表达式
程序一
#else
程序二
#endif
如果表达式为真,编译程序一,否则编译程序二,这种形式一般用于注释多行代码
例如:
#if 0 //0为假,在这里表示不编译,改为1时则编译
…
#endif
选择性编译都是在预编译阶段干的事情。
总结
继续努力