《C语言程序设计》琐碎知识点总结笔记
《C语言程序设计》琐碎知识点总结笔记
本篇笔记旨在拾掇干净一些琐碎的小/冷知识点,免得给之后的学习留不必要的念想。
一、变量的存储类别
变量的定义形式:
[存储类别] 类型标识符 变量名
现对“存储类别”加以说明。
存储空间分为程序区和数据区,数据区分为静态存储区和动态存储区。
存储类别有:自动型(auto)、寄存器型(register)、静态型(static)、外部型(extern)。
1.局部变量的存储类别
局部变量的存储类别有:自动型、静态型、寄存器型。
(1)自动型局部变量
在动态存储区分配存储空间,调用函数时分配存储空间,调用结束后自动释放存储空间。
默认的存储类别,相当于最常使用的、定义时不加“存储类别”的变量。
auto float m;
auto int a,b,c=10;
(2)静态型局部变量
在静态存储区分配存储空间,调用函数时分配存储空间,调用结束后不会释放存储空间,并保留变量的值。
人话:只记住初始化的值,之后的一切变量值的改变都无视。
static int c=3;
上述代码中变量c的值便永远是3了(在其作用域内)。
(3)寄存器型局部变量
将局部变量的值放在CPU通用寄存器中。
只有自动型局部变量和形式参数可以说明为寄存器型变量。(说明寄存器型满足自动型的特性,如可改变值)
register int a,b;
ps:曾见过有人将循环语句中的 \(i\) 声明为寄存器型来进行常数优化,但我的C语言老师又说寄存器型已经“名存实亡”,由此可见还是不要随便用寄存器型为好。
2.全局变量的存储类别
通过引用声明可以扩展全局变量的作用域(类似于函数的引用说明),引用声明的形式为:
extern 数据类型 变量名
全局变量的存储类别有:静态型、外部型。
(1)静态型全局变量
静态型全局变量只能被它所在文件中的函数使用,不能被其他文件中的函数使用。
可以通过引用声明在本文件内扩展其作用域。
举例如下:
extern int a,b;// a,b的引用说明语句
int fun()
{
if(a>b) return a;
else return b;
}
static int a,b;// a,b的定义语句
上述代码也可直接写成:
static int a,b;
int fun()
{
if(a>b) return a;
else return b;
}
(2)外部型全局变量
若没有给出存储类别(即前面什么也不加),则定义的变量为外部型全局变量。
通过引用声明,可以将全局变量作用域扩展到定义它之前的函数,也可以扩展到程序中的其他文件。
举例如下:
(源文件1)
int a,b;// 定义外部型全局变量a,b
(源文件2)
extern int a,b;// 外部型全局变量引用说明
二、函数的存储类别
函数的定义形式:
[函数存储类别][函数返回值类型]函数名([函数形式参数表])
{
函数体说明部分
函数功能语句序列
[return 表达式;]
}
现对“函数存储类别”加以说明。
根据函数能否被其他源文件的函数调用,函数可以分为内部函数(static)和外部函数(extern)。
1.内部函数
存储类别为static,又称静态函数。
只能被它所在文件中的函数调用。
不同文件中的内部函数可以同名,互不干扰。
static int fun(int a,int b)
{
return (a>b?a:b);
}
2.外部函数
存储类别为extern或省略存储类别。
既能被它所在文件中的函数调用,也能被其他文件中的函数调用。
如果一个文件要调用另一个文件中定义的外部函数,则在调用函数的文件中一定要对被调用的外部函数进行引用声明。
引用声明的形式:
extern 函数名([函数形式参数表])
举例如下:
(源文件1)
#include<stdio.h>
int main()
{
extern fun(int a,int b);
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",fun(a,b));
return 0;
}
(源文件2)
#include<stdio.h>
int fun(int a,int b)
{
return (a>b?a:b);
}
三、编译预处理
在编译之前进行的处理,以“#”开头。
1.宏定义
宏定义的本质是在程序编译前进行替换。
注意替换后的运算优先级,在必要时使用括号。
(1)不带参数的宏定义
定义与取消定义:
#define 宏名 宏体
#undef 宏名
举例如下:
#define PI 3.1415926
(2)带参数的宏定义
定义与取消定义:
#define 宏名(形参列表) 宏体
#undef 宏名
使用:
宏名(实参列表)
举例如下:
#define s(a,b) a>b?a:b
s(3,4)
宏替换为:3>4?3:4
2.文件包含
#include "文件名"
#include<文件名>
3.条件编译
形式1:
#ifdef 标识符
程序段1
[#else
程序段2]
#endif
若标识符已经被定义(使用#define 标识符),则对程序段1进行编译,否则对程序段2进行编译。
形式2:
#ifndef 标识符
程序段1
[#else
程序段2]
#endif
若标识符未被定义,则对程序段1进行编译,否则对程序段2进行编译。
形式3:
#if 常量表达式
程序段1
[#else
程序段2]
#endif
若表达式的值为真,则对程序段1进行编译,否则对程序段2进行编译。
四、主函数与命令行参数
主函数的格式为:
void main([int argc,char *argv[]])
{
...
}
()中的信息称为命令行参数。
argc用于保存用户命令行中输入的命令中参数的个数,命令名本身也是一个参数。
argv[]是一个字符指针数组,用于保存各个参数的首地址(包括命令名本身)。
五、变量的作用域
ps:只提几个关键点。
1.{}括起来的是一个复合语句,其中定义的局部变量只能在该复合语句中使用。
2.主函数中定义的变量也是局部变量,只在主函数中有效。
3.不同函数中定义的局部变量可以同名,它们互不干扰。
4.形式参数也是局部变量,只在它所在的函数中有效。
5.全局变量的作用域从定义点开始直到文件尾,可以被作用域内的所有函数共用。
6.在同一个源文件内,如果全局变量和局部变量同名,则在局部变量的作用域内全局变量不起作用。(强权难压地头蛇)
六、运算符与表达式
1.算术运算符和算术表达式
(1)算术运算符
双目:+加法 -减法 *乘法 /除法 %取模
单目:+取正 -取负 ++自加 --自减
取负不改变变量的值。
自加自减运算符只能用于变量,不能用于常量和表达式。
(2)算术表达式
单个的常量和变量都是算术表达式。
算术表达式的值是数值型。
(3)优先级
高:-(取负)、++、--
中:*、/、%
低:+、-
(4)结合方向
右结合:-(取负)、++、--
左结合:+、-、*、/、%
当++、--、+、-混合运算时,应自左向右尽可能多地组合运算符。
(5)类型转换
自动类型转换:从低精度类型转换成高精度类型。
精度从高到低:double->long->unsigned->int->char
强制类型转换
(类型标识符) (表达式)
2.关系运算符和关系表达式
(1)关系运算符
双目:> >= < <= == !=
(2)关系表达式
关系表达式的值只能是1或0,数据类型为整型,代表关系运算结果的真/假。
(3)优先级
高:>、>=、<、<=
低:==、!=
(4)结合方向
均为左结合
3.逻辑运算符和逻辑表达式
(1)逻辑运算符
双目:&&逻辑与 ||逻辑或
单目:!逻辑非
(2)逻辑表达式
逻辑表达式的值:1代表真,0代表假。
若a,b都为真,则a&&b为真,否则为假;
若a,b都为假,则a||b为假,否则为真;
若a为真,则!a为假;若a为假,则!a为真。
(3)优先级
高:!
中:&&
低:||
(4)结合方向
&& ||为左结合,!为右结合。
并不是所有逻辑运算都被执行,只有在必须执行下一个逻辑运算才能求出表达式的值时,才执行该运算。
4.赋值运算符和赋值表达式
(1)赋值表达式
赋值表达式的值就是被赋值的变量的值,赋值表达式的值的类型就是被赋值的变量的类型。
(2)结合方向
赋值运算符的结合方向都是右结合。
a=b=c=10等价于a=(b=(c=10))
赋值允许连等,但定义变量时不允许,错误写法:
int a=b=c=10;
赋值表达式中的赋值运算符的左侧必须是变量,不能是常量和表达式。
5.逗号运算符和逗号表达式
形式:
表达式1,表达式2[,表达式3,...,表达式n]
求解过程:从左到右依次计算每个表达式的值。
逗号表达式的值就是“表达式n”的值,逗号表达式的值的类型就是“表达式n”的值的类型。
6.条件运算符和条件表达式
形式:
表达式1?表达式2:表达式3
表达式1一般为关系表达式或逻辑表达式,表达式2和3同类型。
求解过程:执行表达式1,若表达式1的值非0,则执行表达式2,表达式2的值就是条件表达式的值;若表达式1的值为0,则执行表达式3,表达式3的值就是条件表达式的值。

浙公网安备 33010602011771号