基础知识
- C语言的编译和链接
编译型语言:程序在执行之前需要有一个专门的编译过程,编译成机器语言。e.g. C, C++
特点:依赖编译器
C语言代码需要经过编译和链接生成可执行程序才能运行
过程:源文件>>预编译(生成“.i”文件)>>编译(生成二进制的目标程序.obj)>>汇编>>链接>>可执行程序
注意:
- 编译程序能生成≠程序无错误;逻辑错误需要通过调试发现
解释性语言:HTML,XML,Python,Ruby
- 集成开发环境IDE
编写代码
VS上要运行代码需要:
1)编译+链接
2)运行
做法:按键ctrl+F5(笔记本电脑注意fn)
注意区别:F5是调试!
-
C中的源文件(.c)和头文件(.h)
-
在C的源程序中,main函数的位置可以任意
但是:如果代码放main()函数后面的话,要在main()之前声明。先声明再调用
- define属于预处理命令,作用是文本替换
- C 语言程序的三种基本控制结构是顺序结构、分支(又称选择)结构和循环结构;
- 运算符:在算术、赋值和关系运算符中,按照优先级从高到低的顺序排列为:算术运算符、关系运算符、赋值运算符。(算官富)
- 双目运算符(需要两个操作数)和单目运算符(1个操作数)(+,-表正负)
- C语言的历史:1983年美国国家标准化协会制定ANSI C
- C语言的特点:
- 是结构化语言
- 语句借鉴紧凑,使用方便灵活
- c语言程序易于移植
- 有强大处理能力
- 目标代码质量高,运行效率高
- 复合语句;语法上视为一条语句
变量类型
1.单精度浮点型float
最多有7位十进制有效数字,四舍五入 e.g. 3.1415926535 --> 3.141593
输出格式控制符
- 整型输出时,输出8进制:“%o”, …; 输出十六进制: “%x”, …
- 添加宽度限定词: e.g.整型输出:%md, 指定输出宽度m(包括符号位);实际位数<m, 左端补空格;>m, 根据实际位数输出;
又e.g. 实型数据输出%m.nf, 保留n
位小数,输出宽度是m(包括符号和小数点),补空格规则同上. e.g. %6.1f 表示按照右对齐的方式输出实数,总共输出6位,其中小数保留1位,不足六位就用空格补上
若要左对齐,则用%-6.1f
For循环
for (表达式1; 表达式2; 表达式3)
循环体语句
for语句的表达式2若为空,则表示真值,常常会导致循环无限制条件而死循环。
- 循环体语句只执行一条;若有多条,需要变为复合语句。
- 表达式3执行,是在执行完循环体语句之后。表达式2,3重复执行,表达式1只在循环前执行1次
- 循环(控制)变量:for循环中,通过改变或判断某个变量的值来控制循环的执行。
- 若for()语句后有个分号,编译器会认定是for()语句后紧跟这一条空语句(作为循环体语句)(见以下代码)
//...
int fahr;
double celsius;
for (fahr = 121 ; fahr <= 125; fahr++) ; //执行空语句5次,结束时fahr = 126;
celsius = 5.0 * (fahr - 32) / 9.0; /* 语句① */ //执行1次
printf("%4d%6.1f\n", fahr, celsius); /* 语句② */ //执行1次
函数
一般来说函数要写在main函数之前,但当自定义函数数量过多,会影响main函数被阅读;
解决方法:在main函数前率先进行自定义函数的声明。
函数声明:可以写在开头,便于之后所有的函数都能调用之。
局部变量和全局变量
局部变量:
1)在函数内定义(including形参)
2)定义在复合语句内部
全局变量:在函数以外定义的变量,不从属于任何一个函数
全局变量有初始值,默认为0;从程序执行开始,到程序结束,存储单元始终保持。
作用范围:从定义处到源文件结束。
**若局部变量与全局变量同名,(共同作用范围内)局部变量优先。
##变量生命周期和静态局部变量
生命周期:从其产生(每次函数调用时,为其分配存储空间),到其消亡(每次该函数调用结束,变量的存储单元被回收)
自动变量:以前所称的局部变量
全局变量比局部变量自由度大,因此应该谨慎使用,因为每一个函数调用都有可能改变其的值。
静态局部变量
static 类型名 变量名
作用范围:局部变量
生命周期:同全局变量;
在静态局部变量作用范围内,静态变量的初值为0,且其会记住前一次调用时留下来的值。
数据类型和储存
原/反/补码
正数的三码相同
负数:反码:符号位不变,原码取反;补码:
使用补码的好处:避免了反码中出现的两个零?:-0和+0,只有一个零
根据补码的原理,16位2进制,能表示的正数最大值只有32767,否则会溢出。再加1,会向下循环到-32768
整数
整数的表示
- 十进制
首位不可为0. - 八进制
+/-, 0-7, 首位是0. - 十六进制
+/-, 0-9,a-f/A-F(大小写等价)前缀0x/0X.
e.g.以下三种形式等价
十 八 十六
123 0173 0x7b
16 020 0x10
整数类型
- 字母后缀
e.g. 123L (long)
123U (unsigned)
123LU (unsigned long)
-8U -->非法数据 - 整数的值
整型数据的输入输出
系统输出时,不管以何进制,输出都是没有前导的0(八进制)和0x(十六进制)的。
不同的格式控制说明符只是改变数据输出的形式,实际数据还是同一个。
输入scanf()
%e: 以科学计数法的格式接收数值
字符型数据
- 字符具有数值特征 (在ASCII码范围内)
e.g. ‘A’ 65 0100 0001 等价
char c = 65; ---->合法语句
‘A’ + 1 = 66; - ASCII字符集
0-9:
A-Z: 65-90
a-z: 97-122
C语言一个独有特点:字符‘1’可以进行运算(以ASCII码值的形式)
转义字符
- 是字符常量,代表一个字符
- 所有字符都可以用转义字符表示
- \t: 横向跳格(tab键作用)
- \b: 显示输出时,刷新左边一个字符
- \: 反斜杠字符""
- ': 单引号
- \r: 回车(输出位置重新移到行首)
e.g. printf(“Hello world\rHello Hangzhou”);
输出“Hello Hangzhou” - \ddd: 八进制数ddd代表的字符
\401–>非法(超出ASCII码范围)
\007 = \7; \101 = ‘A’
\后不写0也可以,因为\后无十进制用法 - \xhh: 十六进制数hh(最多两位)所代表的字符,e.g. \x41 = ‘A’
\401: 系统识别为’ '和‘1’
实型
- 数据精度和取值范围是两个不同概念
e.g. float x = 1234567.89 (在取值范围内,但不能精确表达)
e.g.2
实型常量
- 科学技术法
e.g.
类型转换
- 强制类型转换: (类型名)表达式, 优先级最高
e.g. (double)3; (int)3.8
表达式
x++:
++x:
两者单独使用是等效的;但作为表达式的一部分就有差异了。
e.g. i= -1;
算数运算符的优先级和结合性
数组
- ANSI C标准下,定义数组需要明确数组名,元素类型和数组的大小
- 数组名表示该数组所分配连续内存空间中第一个单元的地址(首地址)。由于数组空间一经分配后,在运行过程中不会改变。因此,数组名是一个地址常量,不允许修改。
- 引用:只能引用单个元素,不允许引用整个数组;引用数组元素时,方括号内表达式可以是变量;
- 静态数组:可初始化。e.g.
static int b[5] = {1,2, 3, 4, 5};
如果静态储存数组没有初始化,系统自动给所有的数组元素赋0.
只对部分元素赋值,其余元素初值为0;
变量
变量赋初值:使用的‘=’不是赋值号
e.g. static int a = 1;
{static int a; a=1;}.
两者不一样
一维字符数组 vs 字符串
-
存放字符型数据。e.g. char t[6] = {‘H’, ‘a’, ‘p’, ‘p’, ‘y’ };
-
部分初始化:其余未赋值元素为0
等价于:char[6] = {‘H’, ‘a’, ‘p’, ‘p’, ‘y’, 0}; -
0代表’\0’, ASCII码为0
等价于:char[6] = {‘H’, ‘a’, ‘p’, ‘p’, ‘y’, ‘\0’}; -
字符串常量:双引号括起来的字符序列,有一个结束标志’\0’;
e.g. 字符串"Happy"由6个字符组成: ‘H’, ‘a’, ‘p’, ‘p’, ‘y’ 和’\0’;
前五个为字符串的有效字符,'\0’是字符串结束符。 -
字符串的有效长度就是有效字符的个数; e.g. "Happy"的有效长度是5;
-
C语言将字符串作为一个特殊的一维字符数组来处理:
-
字符串的存储-数组初始化
- 字符串可以存放在一维字符数组中:
static char s[6] = {‘H’, ‘a’, ‘p’, ‘p’, ‘y’, ‘\0’}; //数组s中存放了字符串"Happy"; - 字符数组的初始化也可以使用字符串常量:
等价于:static char s[6] = {“Happy”};
or static char s[6] = “Happy”; - 字符串存入字符数组时,由于存在结束符’\0’, 数组长度 至少是字符串的有效长度+1
- 若数组长度 > 字符串有效长度+1, 则数组中除了存入的字符串,还有其他内容,即字符串只占用了数组的一部分。e.g.
auto char str[80] = “Happy”;
只对数组的前6个元素(str[0] - str[5])赋初值,其他元素的值不确定。然而,不会影响对字符串”Happy“的处理。因为字符串遇’0’结束,数组中第一个’\0’前面的所有字符和第一个’\0’一起构成了字符串"Happy"; 第一个’\0’之后的其他数组元素与该字符串无关。
- 字符串可以存放在一维字符数组中:
-
字符串的操作
- 字符串存入一维字符数组后,对字符串的操作即为对该字符数组的操作。但需要注意:
- 普通数组:元素个数确定,用下标控制循环;
- 字符串:没有明显给出有效字符个数,只规定’\0’之前都为有效字符,通过==比较元素值是否等于’\0’==控制循环。
- 字符串存入一维字符数组后,对字符串的操作即为对该字符数组的操作。但需要注意:
-
字符串的存储:赋值和输入
-
e.g. static char s[80];
s[0] = ‘a’;
s[1] = ‘\0’; (赋值)
等价于: static char s[80] = “a”;(输入)
区别:
“a”: 字符串常量,包括’a’ 和’\0’两个字符,用一维数组存放;
‘a’: 字符常量,只有一个字符,可以赋给字符变量
注意:输入(字符串)时,由于后面的结束符’\0’无法输入,因此,输入时需要事先指定输入结束符,代表输入结束,并将输入结束符转换为字符串结束符’\0’;
再次强调:把字符串存入数组后,对字符串的操作就是对字符数组的操作!
指针
定义
- 直接访问
- 间接访问:通过另一个变量访问变量的内容。把变量的地址放到另一变量中,使用时先找到后者,再从中取出前者的地址。e.g. p指向a, *p==a
- 指针变量:存放地址的变量。若指针变量p存放了变量x的(首)地址,则p指向x;
- 定义: …
- 指针必须先赋值再引用,可将其赋值为NULL或数组首地址。
悬挂指针
e.g. int *p; *p=5; 会在随机空间内改变储存变量的内容,有害
数组和指针的关系
任何由数组下标来实现的操作都能用指针来完成
以下两式恒等价:
*(a+i) (指针法表示元素)
a[i] (下标法表示元素)
甚至有时没有定义数组,使用[]也可以,编译器会理解成指针
- 数组的循环也可以使用指针
- e.g.
- for (p=a; p<=&a[i]; p++) (表达式2还可以写成p<=a+99)
- 当仅引用(或称呼)地址,而没有访问数组边界外的内容时,是不算越界的
- e.g. a[0] == *(&a[-1]+1) 没有访问a[-1],之势引用了其地址
- 定义函数时,使用int a[]作为形参,也相当于int *a作为形参
结构
定义和初始化
- 定义结构体类型定义时不可以初始化;此时赋值是没有空间储存之的。
- 定义方式:
-
-
- 混合定义:定义结构类型的同时定义结构变量
-
- 无类型名定义:定义结构时省略结构名,变量必须立刻在结构后定义完
- 初始化:按照结构定义时分量的描述顺序给出各个分量的值
结构变量的使用
- 具有相同类型的结构变量可以直接赋值