完善2.2函数,修改markdown格式。
This commit is contained in:
parent
0262e7506e
commit
4302ce742f
@ -1,55 +1,55 @@
|
||||
2.1 基础语法
|
||||
===
|
||||
# 2.1.1 关键字
|
||||
# 2.1 基础语法
|
||||
|
||||
## 2.1.1 关键字
|
||||
|
||||
以下单词或字符在C语言中有特殊含义,称作关键字:
|
||||
|
||||
* include
|
||||
* define
|
||||
* ifdef
|
||||
* ifndef
|
||||
* endif
|
||||
* extern
|
||||
* typedef
|
||||
* static
|
||||
* const
|
||||
* struct
|
||||
* union
|
||||
* void
|
||||
* signed
|
||||
* unsigned
|
||||
* char
|
||||
* short
|
||||
* int
|
||||
* long
|
||||
* float
|
||||
* double
|
||||
* if
|
||||
* else
|
||||
* for
|
||||
* do
|
||||
* while
|
||||
* break
|
||||
* continue
|
||||
* goto
|
||||
* include
|
||||
* define
|
||||
* ifdef
|
||||
* ifndef
|
||||
* endif
|
||||
* extern
|
||||
* typedef
|
||||
* static
|
||||
* const
|
||||
* struct
|
||||
* union
|
||||
* void
|
||||
* signed
|
||||
* unsigned
|
||||
* char
|
||||
* short
|
||||
* int
|
||||
* long
|
||||
* float
|
||||
* double
|
||||
* if
|
||||
* else
|
||||
* for
|
||||
* do
|
||||
* while
|
||||
* break
|
||||
* continue
|
||||
* goto
|
||||
|
||||
以上关键字的作用将在后续章节讲解。
|
||||
|
||||
# 2.1.2 特殊符号
|
||||
## 2.1.2 特殊符号
|
||||
|
||||
C语言中常会用到以下符号:
|
||||
|
||||
* 赋值运算:=、+=、-=、*=、/=、%=、&=、|=
|
||||
* 算数运算:+、-、*、/、%、++、--
|
||||
* 比较运算符:==、>=、<=、!=、>、<
|
||||
* 逻辑运算:&&、||、!
|
||||
* 位运算:&、|、~、^、>>、<<
|
||||
* 指针运算:*、&
|
||||
* 其他:;、#、{、}、[、]、0x、0b、//、/\*、\*/
|
||||
* 赋值运算:=、+=、-=、*=、/=、%=、&=、|=
|
||||
* 算数运算:+、-、*、/、%、++、--
|
||||
* 比较运算符:==、>=、<=、!=、>、<
|
||||
* 逻辑运算:&&、||、!
|
||||
* 位运算:&、|、~、^、>>、<<
|
||||
* 指针运算:*、&
|
||||
* 其他:;、#、{、}、[、]、0x、0b、//、/\*、\*/
|
||||
|
||||
以上符号中的运算符将在学习数据类型之后进行说明。
|
||||
|
||||
# 2.1.3 注释
|
||||
## 2.1.3 注释
|
||||
|
||||
注释是一些说明性的文字,他并不影响程序的逻辑、执行和运算。仅仅是帮助编程开发人员更好阅读和理解代码。
|
||||
C语言有行注释和块注释,下面是几个行注释:
|
||||
@ -69,72 +69,73 @@ C语言有行注释和块注释,下面是几个行注释:
|
||||
```
|
||||
|
||||
C语言的编码和注释有很多种风格,你会发现每种风格有各自的优缺点。你可以尝试多种风格,但成熟的软件系统会采用统一的风格,这样的要求经常由《编码规范》来约定。
|
||||
# 2.1.4 字面常量
|
||||
|
||||
## 2.1.4 字面常量
|
||||
|
||||
常量可以理解为固定不变的量,是与变量相对的概念。常量一经申明变不允许再发生改变。以下均是常量:
|
||||
|
||||
* 16、0xFA90、0b1010
|
||||
* 25.1
|
||||
* "Have Fun!"、'x'、"15996699996"、"2\*3.1415926535898\*r"
|
||||
* 16、0xFA90、0b1010
|
||||
* 25.1
|
||||
* "Have Fun!"、'x'、"15996699996"、"2\*3.1415926535898\*r"
|
||||
|
||||
常量的不变性是指如下赋值语句都是错误的:
|
||||
|
||||
* "var"="Have Fun!"
|
||||
* "var"=1024
|
||||
* 16=0x16
|
||||
* "var"="Have Fun!"
|
||||
* "var"=1024
|
||||
* 16=0x16
|
||||
|
||||
而下列写法是可以编译通过的:
|
||||
|
||||
* "var"=="Have Fun!"
|
||||
* "var"!=1024
|
||||
* 16<0x16
|
||||
* "var"=="Have Fun!"
|
||||
* "var"!=1024
|
||||
* 16<0x16
|
||||
|
||||
因为以上并非赋值语句,而是比较语句。通过英文双引号表达的是字符串变量,他们可以是一串字符。使用英文单引号表达的是字符常量,他只可以包含一个字符。而类似于16、25.1这种的是数字量。数字量分为整数和浮点数,他们有多种方式去表达方式。
|
||||
|
||||
# 2.1.5 整数的表达方式
|
||||
## 2.1.5 整数的表达方式
|
||||
|
||||
除了常用的十进制方式以外,在C语言甚至其他语言中,还经常用到二进制和十六进制数。
|
||||
|
||||
* 0b 开头的表达二进制数,仅使用0、1表达
|
||||
* 0x 开头的表达十六进制数,除0~9外,使用A~F来表达10~15
|
||||
* 0b 开头的表达二进制数,仅使用0、1表达
|
||||
* 0x 开头的表达十六进制数,除0~9外,使用A~F来表达10~15
|
||||
|
||||
二进制、十进制、十六进制间通过 8421BCD 码进行转换。
|
||||
|
||||
* 二进制:0b 1010
|
||||
* 十进制:1\*2^3+0\*2^2+1\*2^1+0\*2^0
|
||||
* 也就是:1\*8+0\*4+1\*2+0\*1
|
||||
* 二进制:0b 1010
|
||||
* 十进制:1\*2^3+0\*2^2+1\*2^1+0\*2^0
|
||||
* 也就是:1\*8+0\*4+1\*2+0\*1
|
||||
|
||||
且每 4 位二进制数可表示一位十六进制数:
|
||||
|
||||
* 二进制:0b 1010 0101
|
||||
* 十六进制:0xA5
|
||||
* 二进制:0b 1010 0101
|
||||
* 十六进制:0xA5
|
||||
|
||||
# 2.1.6 变量
|
||||
## 2.1.6 变量
|
||||
|
||||
在C语言中,可以使用字符来表示数字量或字符串等。就好像数学里用x表示一些数那样。这样的量,可以被反复修改,被称作变量。变量只可以使用英文字符或下划线“_”开头,可包含0~9的数字,不可包含其他字符。以下是一些变量:
|
||||
|
||||
* int aint
|
||||
* unsigned long _along
|
||||
* double flt0, flt1, flt2
|
||||
* void* pointer
|
||||
* int aint
|
||||
* unsigned long _along
|
||||
* double flt0, flt1, flt2
|
||||
* void* pointer
|
||||
|
||||
变量需要被定义才能够使用,在定义变量的时候,在变量前面的用于描述变量的C关键字表示了变量的类型和作用范围。
|
||||
例如 static 表示变量是静态的,而 short 表示了有符号的32位整数。
|
||||
所谓有符号、无符号,即变量所表示的数是否包含负数。
|
||||
|
||||
* unsigned char: 0~255
|
||||
* signed char:-128~127
|
||||
* char = signed char
|
||||
* unsigned char: 0~255
|
||||
* signed char:-128~127
|
||||
* char = signed char
|
||||
|
||||
char是8位数,可表示2^8=256个数,无符号的char从0开始,取256个整数,最大是255。当表示有符号数时,则用128个数表示-1~-128,另一半表示0~128。
|
||||
其他类型的位数为:
|
||||
|
||||
* short 16位
|
||||
* int 32位
|
||||
* long 与总线位宽有关,不低于32位
|
||||
* long long 64位
|
||||
* float 32位浮点数,非常不精确
|
||||
* double 64位浮点数,精确
|
||||
* short 16位
|
||||
* int 32位
|
||||
* long 与总线位宽有关,不低于32位
|
||||
* long long 64位
|
||||
* float 32位浮点数,非常不精确
|
||||
* double 64位浮点数,精确
|
||||
|
||||
另一种变量是如下定义的:
|
||||
|
||||
@ -186,6 +187,7 @@ ashort++;
|
||||
char a = 25;
|
||||
long b = a; // b=25.
|
||||
```
|
||||
|
||||
但如果反过来,将位数多的变量赋值给位数少的,将会截取低位的部分数据进行赋值。
|
||||
|
||||
```cpp
|
||||
@ -202,7 +204,8 @@ char b = (char)a; // 强制转换成char型.
|
||||
|
||||
在进行上述类型转换时,并没有改变a的类型,只是赋值了一份a的值,然后进行扩展或裁剪,再将结果赋值给b。而a本身的值和类型并未发生任何变化。
|
||||
|
||||
# 2.1.7 符号常量
|
||||
## 2.1.7 符号常量
|
||||
|
||||
有时候我们希望用一个符号来代替某个字面常量,这看起来很像一个不允许改变值的变量,我们用 const 关键字来修饰它,使之成为符号常量。例如:
|
||||
|
||||
```cpp
|
||||
@ -217,7 +220,7 @@ const int cnint = 256;
|
||||
cnint++;
|
||||
```
|
||||
|
||||
# 2.1.8 运算符
|
||||
## 2.1.8 运算符
|
||||
|
||||
c语言运算符主要包括赋值运算、算数运算、逻辑运算、位运算、比较运算等。运算符主要涉及到优先级,结合性y以及前加加和后加加的问题。例如:
|
||||
|
||||
@ -236,9 +239,10 @@ val &= mask; // val=4.
|
||||
|
||||
TODO: 有一个非常常用的特殊运算符,即sizeof运算符。
|
||||
|
||||
# 2.1.9 宏
|
||||
## 2.1.9 宏
|
||||
|
||||
通过使用宏,可以指导c编译器做一些特别的工作。例如使用define关键字来做一些替代的工作:
|
||||
|
||||
```cpp
|
||||
#define BASE_ADDR (0x25)
|
||||
#define REG1_OFFSET (0x01)
|
||||
@ -247,6 +251,7 @@ TODO: 有一个非常常用的特殊运算符,即sizeof运算符。
|
||||
printf("Register 1 addr=0x%x.\n", BASE_ADDR+REG1_OFFSET); // addr=0x25+0x01
|
||||
printf("Register 2 addr=0x%x.\n", BASE_ADDR+REG2_OFFSET); // addr=0x25+0x02
|
||||
```
|
||||
|
||||
这称作宏替换。宏替换可以提高程序的可移植性,例如上述例子中,只要修改 #define BASE_ADDR (0x25) 一处便可以修改所有寄存器的地址。
|
||||
注意,定义宏的时候一般不加分号,这是因为在进行宏展开时,会将宏名替换为后边的全部,如果有多余符号存在,则会产生如下问题:
|
||||
|
||||
@ -270,7 +275,8 @@ int b = REG1_ADDR_1*2; // b=0x25+0x01*2=39
|
||||
|
||||
另外,我们很少使用小写英文字母作为宏的名称。
|
||||
|
||||
# 2.1.10 typedef
|
||||
## 2.1.10 typedef
|
||||
|
||||
typedef也是一个能够有效提高程序可维护性的关键字。它允许你声明一个自定义的类型。例如:
|
||||
|
||||
```cpp
|
||||
@ -287,6 +293,7 @@ U16 a = 0x55AA; // 等于 unsigned short a.
|
||||
MY_STRUCT x;
|
||||
MY_STRUCT y;
|
||||
```
|
||||
|
||||
这样的代码会产生奇异,由于宏的字面替换,最终声明了两个一样的结构体类型,并分别用它们去定义x和y。编译器很难区分x和y的类型,它们看起来相同,却又不同。这样的问题我们用typedef来解决:
|
||||
|
||||
```cpp
|
||||
@ -299,7 +306,7 @@ MY_STRUCT y;
|
||||
由于typedef不会产生字面替换,仅仅是声明了新的类型,因此,此处与定义两个普通变量没有差别,并且不存在上述的问题。
|
||||
关于结构体这一复合类型,将在后续章节详细说明。
|
||||
|
||||
# 2.1.11 指针
|
||||
## 2.1.11 指针
|
||||
|
||||
前文提及的变量,无论是哪种类型的,在程序运行起来之后,都会占用一定的内存空间。所占用具体空间的大小,与其类型有关。而每个变量所在的位置,便是内存地址。
|
||||
我们可以通过变量名来获取变量,此外,也可以通过变量地址来获取变量。这是通过指针操作来实现的:
|
||||
@ -327,7 +334,7 @@ pa++;
|
||||
我们说指针变量,意味着指针本身也是一个变量,是变量就有存储空间和存储地址。存储空间便是类型的长度,指针代表了内存地址,因此其长度总是与内存总线宽度一致。如32位机则指针变量长度为4,64位机为8。
|
||||
指针也有存储地址,意味着可以通过另一个指针来索引指针变量,另一个指针便成为了二级指针。
|
||||
|
||||
# 2.1.12 语句
|
||||
## 2.1.12 语句
|
||||
|
||||
只有词法没有句法就无法构成完整的语言。因此要学习c语言的语句。
|
||||
与自然语言的一个区别在于,c语言通常以英文分号作为一句话的结束。
|
||||
@ -352,7 +359,7 @@ c语言有赋值语句,判断语句,条件语句,循环语句,分支语
|
||||
|
||||
语句块非常有用,经常出现在条件语句,循环语句或者分支语句的后面。之后讲到函数时,你会注意到,函数体本身就是一个语句块。
|
||||
|
||||
# 2.1.13 c/c++文件
|
||||
## 2.1.13 c/c++文件
|
||||
|
||||
c/c++语言程序被写入到文件中,这些文件有特殊的扩展名,c文件扩展名是.c和.h;c++文件扩展名为.cpp和.h。
|
||||
其中.h文件称作头文件,其余都是源码文件。源文件是我们编写程序实体的主要文件,变量和函数的定义都在源文件中。
|
||||
|
@ -1,6 +1,7 @@
|
||||
2.2 函数
|
||||
===
|
||||
|
||||
# 2.2 函数
|
||||
|
||||
## 2.2.1 函数基础
|
||||
|
||||
所谓函数,是指按照某种规律,将输入转变成输出的系统。
|
||||
我们可以用c语言描述这样的系统。
|
||||
|
||||
@ -27,13 +28,13 @@ fun0(a, 3) // 计算(5+3)*(5-3).
|
||||
```
|
||||
|
||||
在定义函数的时候写的参数名称称作形式参数,简称形参。而这里在调用时传入的a和3称作实际参数,简称实参。
|
||||
函数的参数通过return语句返回给调用者:
|
||||
函数的返回值通过 **return** 语句返回给调用者:
|
||||
|
||||
```cpp
|
||||
int c = fun0(5, 3) // c=16.
|
||||
```
|
||||
|
||||
函数可以没有输入,此时函数的参数列表应该为void,或者不写。
|
||||
函数可以没有输入,此时函数的参数列表应该为 **void**,或者不写。
|
||||
|
||||
```cpp
|
||||
int fun1(void)
|
||||
@ -42,7 +43,7 @@ int fun1(void)
|
||||
}
|
||||
```
|
||||
|
||||
函数也可能没有返回值,此时返回类型为void:
|
||||
函数也可能没有返回值,此时返回类型为 **void**:
|
||||
|
||||
```cpp
|
||||
void fun2(int a)
|
||||
@ -54,7 +55,7 @@ int b;
|
||||
b = fun2(0x10); // 这种写法是错的,因为fun2没有返回值,所以无法为变量b赋值.
|
||||
```
|
||||
|
||||
对于大部分c/c++程序而言,有一个特殊的函数,它是整个程序的总入口——main函数。main函数的第一条语句便是整个程序的第一条指令。
|
||||
对于大部分 c/c++ 程序而言,有一个特殊的函数,它是整个程序的总入口——**main**函数。**main**函数的第一条语句便是整个程序的第一条指令。
|
||||
|
||||
```cpp
|
||||
#include <stdio.h> // 包含printf等标准输入输出函数
|
||||
@ -75,12 +76,67 @@ int main(void)
|
||||
}
|
||||
```
|
||||
|
||||
以上,是我们迄今为止编写的最完整的c语言程序。它包含了该程序的总入口函数mian,定义并初始化了两个局部变量a和b,通过printf语句进行了一些输出,return 0代表程序正确执行。另外,我们定义了了一个函数fun0,并且为了让程序能被编译通过,我们通过include包含了一些c标准库对外声明的函数等,在这里就是printf函数。
|
||||
以上你所见的,是所有main函数形式中的一种。main函数不但可以返回零,还可以返回其他值给调用者。也可以接收来自调用者传入的一些参数,这时候main函数的形式如下:
|
||||
以上,是我们迄今为止编写的最完整的 c 语言程序。它包含了该程序的总入口函数 mian,定义并初始化了两个局部变量 a 和 b,通过 **printf** 语句进行了一些输出,return 0 代表程序正确执行。另外,我们定义了了一个函数 fun0,并且为了让程序能被编译通过,我们通过 **include** 包含了一些c标准库对外声明的函数等,用于声明 **printf** 函数。
|
||||
以上你所见的,是所有 **main** 函数形式中的一种。**main** 函数不但可以返回零,还可以返回其他值给调用者。也可以接收来自调用者传入的一些参数,这时候 **main** 函数的形式如下:
|
||||
|
||||
```cpp
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## 2.2.2 函数调用
|
||||
|
||||
函数之间可以相互调用,例如在 fun3 中调用 fun4 的代码如下:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @file example0.c
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
static void fun4(void); // 由于 fun4 是在被使用后定义的,因此需要在使用前声明.
|
||||
|
||||
void fun3(void)
|
||||
{
|
||||
printf("This fun3.\n");
|
||||
fun4(); // 调用 fun4.
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief static 关键字限定了 fun4 只能在本文件中使用,不能在其他文件中使用.
|
||||
*/
|
||||
static void fun4(void)
|
||||
{
|
||||
printf("This fun4.\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
fun3(); // fun3 是在 main 函数前定义的,因此不需要再次声明.
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
有一种非常独特的函数调用方法,被称作第归调用:
|
||||
|
||||
```cpp
|
||||
/**
|
||||
* @file example1.c
|
||||
*/
|
||||
|
||||
void fun5(void)
|
||||
{
|
||||
fun5();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
fun5();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
在上面程序中,**main** 函数中的 **return** 语句永远不会被执行。因为 fun5 这个函数不断的调用自己,致使程序一直被执行下去。
|
||||
实际上,每次调用函数都会压栈,fun5 无穷无穷尽的调用自己,只会导致堆栈溢出,或者被看门狗检测到死循环。通常,我们不会这样编写程序,而是在 fun5 中增加条件判断,在某些情况下不再调用自己,从而使程序可以返回。第归调用在某些情况下是非常有用的。
|
@ -0,0 +1,3 @@
|
||||
# 2.3 编译原理
|
||||
|
||||
## 2.3.1 编译与连接
|
@ -1 +1,2 @@
|
||||
TODO: typedef 结构体在多个文件中的使用。
|
||||
TODO: typedef 结构体在多个文件中的使用。
|
||||
static的限制作用
|
@ -0,0 +1,3 @@
|
||||
函数指针
|
||||
指针数组
|
||||
数组名
|
Loading…
x
Reference in New Issue
Block a user