C语言学习笔记(二)
C的编程机制
C具有良好的可移植性,可以在很多操作系统下使用:UNIX、Linux、MS-DOS、macOS、Windows…在不同的操作系统(OS)或者环境下写C的编译器和过程一般会有所不同
在C语言编写过程中,编写的内容——源代码一般以文本文件存储在以.c为文件拓展名的文件中。比如:example.c,在文件名中,英文句号(.)前面部分称为基本名
,英文句号(.)后面部分是文件拓展名
。
C的基本策略是:用程序把源代码文件
——>可执行文件
(包括可直接运行的机器语言代码),典型的步骤是使用编译
——>链接
——>运行
,其中编译器
把源代码
转换成中间代码
,链接器
把中间代码
和其他代码
合并,生成可执行文件
,这种策略使得C可以方便地对程序进行模块化
设计。
C模块化的好处是:对于一个项目,可以先独立编译单独的模块,然后再用链接器合并已编译的模块。如果需要更改某个模块,不必因此重新编译其他模块。此外链接器还将你编写的程序和预编译的库代码进行合并。
UNIX C
C最开始为UNIX系统开发而生,这里只放个UNIX下使用C的过程图
GNU&&LLVM
关于GNU和LLVM可以看目录[参考资料]中的维基百科
GNU编译器集合也就是GCC,其中包含C的编译器GCC C,在UNIX、Linux、Windows等操作系统下都有可以使用的GCC编译器,当然也包括C的编译器GCC C,它跟随C标准的改动,对最新的新标准支持较好。一般地,使用gcc命令调用GCC C编译器,而很多使用gcc的系统又使用cc作为gcc的别名。
LLVM项目可以说是cc的另一个替代品,它使用Clang编译器处理C(使用clang调用),Clang对最新的C标准的支持也很好。
对于gcc和clang:
1 | 显示所使用的编译器及其版本 |
C中的令牌(Tokens)
令牌是程序的最小元素,令牌的类型有:关键字
、标识符
、常量
、字符串
和运算符
等。
关键字(Keywords)
关键字是C中预定义的保留字,每个保留字均有特定功能或与特定功能相关联,它们对编译器有特殊意义。
C中共有32个关键字:
/ | / | / | / |
---|---|---|---|
auto | double | int | struct |
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
continue | for | signed | void |
do | if | static | while |
default | goto | sizeof | volatile |
const | float | short | unsigned |
标识符(Identifiers)
C中标识符用于明明变量、函数、数组等,可以由由用户定义这些标识符,由字母、数字和下划线组成,关键字(Keywords)不能作为标识符
。
标识符命名规则:
- 必须以字母或者下划线开头
- 只能使用字母、数字和下划线,不允许使用其他特殊字符、标点符号
- 不能包含空格
- 不应该是C的关键字
- 最长为31个字符
常量(Constant)
常量是固定值,就像是常规的变量,在定义后不能进行修改并且在程序执行期间不会改变。这些固定的值,又叫做字面量。
常量可以是任何基本数据类型,比如:
- 整数常量
- 浮点常量
- 字符常量
- 字符串字面值
- 枚举常量
下面是常量的数据类型的一些实例:
整数常量
整数常量可以是八进制、十进制、十六进制的常量
前缀指定基数
:0x/0X表示十六进制,0 表示八进制,十进制一般不带前缀
后缀是U和L的组合
:U是无符号整数(unsigned),L是长整数(long)U和L可以是大写/小写的任意顺序,比如:
1 | 85 /* 十进制 */ |
浮点变量
浮点常量由整数部分、小数点、小数部分和指数部分组成,使用小数或者指数形式来表示。
小数形式表示
:必须包含小数点、指数或者同时包含两者;
指数形式表示
:必须包含整数部分、小数部分,或同时包含两者,带符号的指数使用e或者E引入,比如:
1 | 3.14159 /* 合法的 */ |
字符常量
字符常量使用单引号
括起来,比如:’s’可存储在char类型
的简单变量中,字符常量可以是一个普通字符(如’s’)、一个转义序列(如’\n’)或者一个通用字符(如’\u02C0’),在C中,一些特定的字符前面有反斜杠’\‘时,它们就会变成有特殊含义的转义序列(escape sequence)。转义序列
用于代表难以表
示或无法输入的字符,像\t代表Tab键,\b代表Backspace键,比如:
转义序列 | 含义 |
---|---|
\ | \ 字符 |
\‘ | ‘ 字符 |
\“ | “ 字符 |
? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
字符串常量
字符串字面值或常量是括在双引号 “” 中的。一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。
可以使用空格做分隔符,把一个很长的字符串常量进行分行,比如下面这三种实际显示就是相同的:
1 | # 第一种 |
定义常量
在 C 中,有两种简单的定义常量的方式(#define和const关键字),定义常量时经常以大写英文字母形式。
下面是两个实例:
1.使用 #define 预处理
1 |
|
2.使用 const 关键字
1 |
|
上面两个例子的结果一样,输出结果都是:
1 | value of area : 50 |
字符串(String)
字符串是以null空字符
“\0”结尾的一维字符数组,此空字符表示字符串已结束,字符串始终用双引号(“ ”)引起来,C 编译器
会在初始化数组
时,自动把 ‘\0’ 放在字符串的末尾。在C语言中声明字符串,比如:
1 | char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; |
也可以写成下面的:(数组初始化规则)
1 | char greeting[] = "Hello"; |
上面的字符串在内存中可以如下图所示:
C中操作字符串的函数:
序号 | 函数 & 目的 |
---|---|
1 | strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 |
2 | strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 |
3 | strlen(s1); 返回字符串 s1 的长度。 |
4 | strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 |
5 | strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 |
6 | strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。 |
比如:
1 |
|
输出结果:
1 | strcpy( str3, str1) : Hello |
运算符
运算符是一种告诉编译器执行特定数学或逻辑操作的符号。C中提供了以下类型的运算符:
- 算数运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 杂项运算符
算数运算符
给定变量A和B,C中常用的算数运算符罗列如下:
运算符 | 描述 | 结果 |
---|---|---|
+ | 两操作数相加 | A+B |
- | 前一个操作数减去后一个操作数 | A-B |
* | 两操作数相乘 | A*B |
/ | 两操作数相除(分子/分母) | A/B |
% | 取模,整除后的余数 | A/B后的余数 |
++ | 自增运算符,整数值+1 | A+1或者B+1 |
– | 自减运算符,整数值-1 | A-1或者B-1 |
特别地,对于a++和++a,相同点都是给a的值+1,不同点是a++是先赋值再+1,而++a则是先+1再赋值
下面是个实例:
1 |
|
输出结果为:
1 | 先赋值后运算: |
关系运算符
给定变量A和B,C中常见的关系运算符有:
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真 | (A == B) |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真 | (A != B) |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真 | (A > B) |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真 | (A < B) |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真 | (A >= B) |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真 | (A <= B) |
比如:
1 |
|
输出结果为:
1 | Line 1 - a 不等于 b |
逻辑运算符
给定变量A和B,C中常见的逻辑运算符有:
运算符 | 描述 | 实例 |
---|---|---|
&& | 称为逻辑与运算符 ,如果两个操作数都非零,则条件为真 |
(A && B) |
|| | 称为逻辑或运算符 ,如果两个操作数中有任意一个非零,则条件为真 |
(A || B) |
! | 称为逻辑非运算符 ,用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假 |
!(A && B) |
位运算符
所谓位运算,就是对一个比特(Bit)位进行操作。比特(Bit)是一个电子元器件,8个比特构成一个字节(Byte),它已经是粒度最小的可操作单元了。
C中提供了6种位运算符:
运算符 | 说明 |
---|---|
& | 按位与运算 |
| | 按位或运算 |
^ | 按位异或运算 |
~ | 取反运算 |
<< | 左移运算 |
>> | 右移运算 |
C的位运算比较复杂,深究请看参考资料
赋值运算符
给定变量A、B和C,C语言中支持的赋值运算符:
运算符 | 描述 | 例子 |
---|---|---|
= | 简单的赋值运算符,把右边操作数的值赋给左边操作数 | C = A + B 将把 A + B 的值赋给 C |
+= | 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 | C += A 相当于 C = C + A |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 | C -= A 相当于 C = C - A |
*= | 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 | C *= A 相当于 C = C * A |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 | C /= A 相当于 C = C / A |
%= | 求模且赋值运算符,求两个操作数的模赋值给左边操作数 | C %= A 相当于 C = C % A |
<<= | 左移且赋值运算符 | C <<= 2 等同于 C = C << 2 |
>>= | 右移且赋值运算符 | C >>= 2 等同于 C = C >> 2 |
&= | 按位与且赋值运算符 | C &= 2 等同于 C = C & 2 |
^= | 按位异或且赋值运算符 | C ^= 2 等同于 C = C ^ 2 |
|= | 按位或且赋值运算符 | C |= 2 等同于 C = C | 2 |
杂项运算符
运算符 | 描述 | 例子 |
---|---|---|
sizeof() | 返回变量的大小 | sizeof(a) 将返回 4,其中 a 是整数 |
& | 返回变量的地址 | &a; 将给出变量a在内存中的实际地址 |
* | 指向一个变量 | *a; 将指向一个变量(指针) |
?: | 条件表达式 | 如果条件为真 ? 则值为 X : 否则值为 Y |
运算符优先级
类别 | 运算符 | 结合性 |
---|---|---|
后缀 | () [] -> . ++ - - | 从左到右 |
一元 | + - ! ~ ++ - - (type)* & sizeof | 从右到左 |
乘除 | * / % | 从左到右 |
加减 | + - | 从左到右 |
移位 | << >> | 从左到右 |
关系 | < <= > >= | 从左到右 |
相等 | == != | 从左到右 |
位与 AND | & | 从左到右 |
位异或 XOR | ^ | 从左到右 |
位或 OR | | | 从左到右 |
逻辑与 AND | && | 从左到右 |
逻辑或 OR | || | 从左到右 |
条件 | ?: | 从右到左 |
赋值 | = += -= *= /= %=>>= <<= &= ^= |= | 从右到左 |
逗号 | , | 从左到右 |
参考资料
- 维基百科—GNU
- 维基百科—LLVM
- Token in C
- GCC中的aligned和packed属性
- W3CSchool C教程
- C Primer Plus 第六版
- C语言位运算