目录
C语言简介
一、起源
C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSI C”,最新的C语言标准为2018年6月发布的“C18”。
二、特点
C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发。
C语言能以简易的方式编译、处理低级存储器。
C语言是仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言。
尽管C语言提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个标准规格写出的C语言程序可在包括类似嵌入式处理器以及超级计算机等作业平台的许多计算机平台上进行编译。
主要编译器有:Visual C++、Clang、GCC、Turbo C等。
三、C语言程序结构
C程序主要包括以下部分:
预处理器指令、函数、变量、语句&表达式、注释
样例:
#include <stdio.h>
int main()
{
/* 我的第一个 C 程序 */
printf("Hello, World! \n");
return 0;
}
讲解:
1.程序的第一行 #include <stdio.h> 是预处理器指令,告诉C编译器在实际编译之前要包含stdio.h头文件
2.下一行 int main() 是主函数,程序从这里开始执行
3.下一行 { 是代表一个程序的开始
4.下一行 /*...*/ 将会被编译器忽略,这里放置程序的注释内容。它们被称为程序的注释,注释写法有如下几种:
单行注释:// 注释内容 或 /* 注释内容 */
多行注释:/*
注释内容1
注释内容2
...
*/
5.下一行 printf(...) 是 C 中另一个可用的函数,会在屏幕上显示消息 "Hello, World!",其中“;”是语句结束符,每个语句必须以分号结束,它表明一个逻辑实体的结束
6.下一行 return 0; 终止 main() 函数,并返回值 0。
7.下一行 } 是代表一个程序的结束
编译&执行C程序(以linux系统为例,装好GCC编译器):
1. 打开文本编辑器将上述实例代码添加并存文件为hello.c
2. 进入保存文件的目录,打开命令窗口,依次输入以下命令:
$ gcc hello.c //编译代码
$ ./a.out //执行程序
Hello, World! //屏幕上输出显示结果
C语言基础
一、数据类型
1. 基本类型
1.1 整数类型
类型 | 存储大小 | 值范围 |
---|---|---|
char(字符) | 1字节(8个bit位) | -27 ~ 27-1 或 0 ~ 28-1 |
signed char(有符号字符) | 1字节(8个bit位) | -27 ~ 27-1 |
unsigned char(无符号字符) | 1字节(8个bit位) | 0 ~ 28-1 |
short(短整型) | 2字节(16个bit位) | -215 ~ 215-1 或 0 ~ 216-1 |
signed short(有符号短整型) | 2字节(16个bit位) | -215 ~ 215-1 |
unsigned short(无符号短整型) | 2字节(16个bit位) | 0 ~ 216-1 |
int(整型) | 4字节(32个bit位) | -231 ~ 231-1 或 0 ~ 232-1 |
signed int(有符号整型) | 4字节(32个bit位) | -231 ~ 231-1 |
unsigned int(无符号整型) | 4字节(32个bit位) | 0 ~ 232-1 |
long(32位长整型) | 4字节(32个bit位) | -231 ~ 231-1 或 0 ~ 232-1 |
signed long(有符号32位长整型) | 4字节(32个bit位) | -231 ~ 231-1 |
unsigned long(无符号32位长整型) | 4字节(32个bit位) | 0 ~ 232-1 |
long long(64位长整型) | 8字节(64个bit位) | -263 ~ 263-1 或 0 ~ 264-1 |
signed long long(有符号64位长整型) | 8字节(64个bit位) | -263 ~ 263-1 |
unsigned long long(无符号64位长整型) | 8字节(64个bit位) | 0 ~ 264-1 |
1.2 浮点类型
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
float(单精度浮点型) | 4字节(32个bit位) | 1.2E-38 ~ 3.4E+38 | 6位有效数字 |
double(双精度浮点型) | 8字节(64个bit位) | 2.3E-308 ~ 1.7E+308 | 15位有效数字 |
long double(多精度浮点型) | 16字节(128个bit位) | 3.4E-4932 ~ 1.1E+4932 | 19位有效数字 |
对于以上两种类型,我们可以通过sizeof()运算符来得到其存储字节大小,以下样例演示了获取char、int及double类型的大小:
#include <stdio.h>
int main()
{
// %d代表以十进制形式输出带符号整数,详细说明参考printf()函数详解,\n代表换行
printf("char存储字节大小: %d\n", sizeof(char));
printf("int存储字节大小: %d\n", sizeof(int));
printf("double存储字节大小: %d\n", sizeof(double));
return 0;
}
执行结果:
char存储字节大小: 1
int存储字节大小: 4
double存储字节大小: 8
1.3 枚举类型
被用来定义在程序中只能赋予其一定的离散整数的变量。
1.4 void类型
void类型指定没有可用的值,通常用于以下三种情况:
-
函数返回为空:C 语言中有很多函数都不返回值,不返回值的函数的返回类型为空,即为void类型。写法如 void exit (int status);
-
函数参数为空:C 语言中有很多函数不接受任何参数,不带参数的函数可以接受一个 void。写法如 int get(void);
-
指针指向void:类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
1.5 派生类型
指针类型、数组类型、结构类型、共用体类型和函数类型(后续章节会有详细介绍)。
二、变量
1. 概念
变量是程序可操作的存储区的名称。C 语言中每个变量都有特定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
变量的名称可以由字母、数字和下划线字符组成,它必须以字母或下划线开头,其中大写字母和小写字母是不同的。
变量的值是可变的。
2. 定义
// 语法
type var_list;
type var_name = value;
以下为几种常用的声明方式:
int i, j, k; // 声明了三个int数据类型的变量i, j, k
int i = 1, j = 2, k = 3; // 声明了三个int数据类型的变量i, j, k, 并分别给予一个初始值1, 2, 3
char c; // 声明了一个char数据类型的变量c
char c = 'a' // 声明了一个char数据类型的变量c, 并给予一个初始值'a'
3. 分类
3.1 局部变量
定义在函数内部的变量。
作用域:变量所在的代码块内均可调用。
生命周期:进入作用域生命周期开始,出作用域结束。
3.2 全局变量
定义在函数外部的变量,
作用域:整个工程均可调用,其他文件调用需要添加声明:extern 变量声明
生命周期:整个程序的生命周期
样例:
#include <stdio.h>
#include <stdio.h>
int global = 1;
void test(void) {
printf("global = %d\n", global);
}
int main()
{
int local1 = 2; // 局部变量1
{
local1 = 3; // 局部变量1可在其所在的代码块内调用,因此这里可以更改变量值为3
int local2 = 4; // 局部变量2
}
//local2 = 5; // 局部变量2出了代码块后不可调用,因此这里不可调用
global = 2; // 全局变量整个工程均可调用,因此这里可以更改变量值为2
test(); // 调用test方法,打印全局变量的值为2
int global = 3; // 局部变量与全局变量重名时,局部变量在该代码块内优先被使用
printf("local1 = %d\n", local1); // 输出local1值为3
printf("global = %d\n", global); // 输出为局部变量global值为3
test(); // 局部变量出代码块后结束,因此这里输出全局变量global值为2
return 0;
}
执行结果:
global = 2
local1 = 3
global = 3
global = 2
4. 使用
可以对变量进行赋值和运算。
样例:
#include <stdio.h>
int main()
{
int a = 4;
int b = 2;
int sum = a + b;
int c = a / b;
printf("sum = %d\n", sum);
printf("c = %d\n", c);
return 0;
}
执行结果:
sum = 6
c = 2
三、常量
1. 概念
常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。
常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。
常量就像是常规的变量,只不过常量的值在定义后不能进行修改。
2. 分类、定义及使用
2.1 整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
样例:
85; // 十进制
021; // 八进制
0xF1; // 十六进制
85U; // 无符号整数
85l; // 长整数
85uL; //无符号长整数
2.2 浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成,可以使用小数形式或者指数形式来表示浮点常量。
样例:
3.1415; // 合法
3f; // 不合法,缺少小数部分
31415E-5; // 合法
31415E; // 不合法,缺少指数部分
2.3 字符常量
字符常量是括在单引号中。
样例:
'a';
'Z';
2.4 字符串常量
字符常量是括在双引号中。
样例:
"hello, world"
2.5 枚举常量
样例:
#include <stdio.h>
// 语法,括号中的 MALE, FEMALE, SECRET 是枚举常量0, 1, 2
enum Sex
{
MALE,
FEMALE,
SECRET
};
int main()
{
printf("MALE: %d\n", MALE);
printf("FEMALE: %d\n", FEMALE);
printf("SECRET: %d\n", SECRET);
return 0;
}
执行结果:
MALE: 0
FEMALE: 1
SECRET: 2
2.6 #define定义的标识符常量
样例:
#include <stdio.h>
// 语法
#define LENGTH 10
#define WIDTH 5
#define NEWCHAR 'a'
int main()
{
int area;
area = LENGTH * WIDTH;
printf("value of area: %d\n", area);
printf("NEWCHAR: %c\n", NEWCHAR);
return 0;
}
执行结果:
value of area: 50
NEWCHAR: a
2.7 const修饰的常变量
// 语法
const type var_name = value;
样例:
#include <stdio.h>
int main()
{
const int a = 4; // 修饰的变量为常变量,不可再被修改
//a = 5; // 不可修改常变量的值
//int arr[a] = {0}; // 不可作为常量传入,因为只是语法层面限制该变量值不可变,但本质上仍然是一个变量
printf("a = %d\n", a);
return 0;
}
执行结果:
a = 4
四、运算符
1. 算术运算符
运算符 | 描述 |
---|---|
+ | 将两个操作数相加 |
- | 从第一个操作数中减去第二个操作数 |
* | 将两个操作数相乘 |
/ | 前一个操作数除以后一个操作数(整数相除对商取整,浮点数相除则保留对应浮点数小数位数) |
% | 取模运算符,取前一个操作数整除后一个操作数的余数 |
++ | 自增运算符,整数值增加1 |
– | 自减运算符,整数值减少1 |
样例:
#include <stdio.h>
int main()
{
int a = 32;
int b = 10;
int c = 0;
c = a + b; // 32 + 10 = 42
printf("相加后c = %d\n", c);
c = a - b; // 32 - 10 = 22
printf("相减后c = %d\n", c);
c = a * b; // 32 * 10 = 320
printf("相乘后c = %d\n", c);
c = a / b; // 32 / 10 = 3 (取整)
printf("相除后c = %d\n", c);
c = a % b; // 32 % 10 = 2 (取余)
printf("取模后c = %d\n", c);
a++; // a增加1为33
printf("自增后a = %d\n", a);
b--; // b减少1为9
printf("自减后b = %d\n", b);
return 0;
}
执行结果:
相加后c = 42
相减后c = 22
相乘后c = 320
相除后c = 3
取模后c = 2
自增后a = 33
自减后b = 9
2. 关系运算符
运算符 | 描述 |
---|---|
== | 比较两个操作数的值,如果相等则条件为真,反之为假 |
!= | 比较两个操作数的值,如果不相等则条件为真,反之为假 |
> | 比较两个操作数的值,如果左操作数大于右操作数,则条件为真,反之为假 |
< | 比较两个操作数的值,如果左操作数小于右操作数,则条件为真,反之为假 |
>= | 比较两个操作数的值,如果左操作数不小于右操作数,则条件为真,反之为假 |
<= | 比较两个操作数的值,如果左操作数不大于右操作数,则条件为真,反之为假 |
样例:
#include <stdio.h>
int main()
{
int a = 32;
int b = 10;
int c = 10;
printf("a == b: %d\n", a == b);
printf("a != b: %d\n", a != b);
printf("a > b: %d\n", a > b);
printf("a < b: %d\n", a < b);
printf("a >= b: %d\n", a >= b);
printf("c >= b: %d\n", c >= b);
printf("a <= b: %d\n", a <= b);
return 0;
}
执行结果:
// 在C语言中,0表示为假,非0为真
a == b: 0
a != b: 1
a > b: 1
a < b: 0
a >= b: 1
c >= b: 1
a <= b: 0
3. 逻辑运算符
运算符 | 描述 |
---|---|
&& | 逻辑与运算符,如果两个操作数都非0,则条件为真,反之为假 |
|| | 逻辑或运算符,如果两个操作数至少有一个非0,则条件为真,反之为假 |
! | 逻辑非运算符,逆转操作数的逻辑状态,如果操作数为真,则使其为假,反之则为真 |
样例:
#include <stdio.h>
int main()
{
int a = 32;
int b = 10;
int c = 0, d = 0;
printf("a && b: %d\n", a && b);
printf("a && c: %d\n", a && c);
printf("a || c: %d\n", a || c);
printf("c || d: %d\n", c || d);
printf("!(a && b): %d\n", !(a && b));
printf("!(a && c): %d\n", !(a && c));
return 0;
}
执行结果:
a && b: 1 // a,b均非0,所以为真
a && c: 0 // c为0,所以为假
a || c: 1 // a为非0,所以为真
c || d: 0 // c,d均为0,所以为假
!(a && b): 0 // a && b 为真,所以取非为假
!(a && c): 1 // a && c 为假,所以取非为真
4. 位运算符
按照二进制位来进行运算。
运算符 | 描述 |
---|---|
& | 按位与运算符,按两个操作数转化成的二进制位来进行”与“运算,规则为:0&0=0; 0&1=0; 1&0=0; 1&1=1 |
| | 按位或运算符,按两个操作数转化成的二进制位来进行”或“运算,规则为:0|0=0; 0|1=1; 1|0=1; 1|1=1 |
^ | 异或运算符,按两个操作数转化成的二进制位来进行”异或“运算,规则为:0^0=0; 0^1=1; 1^0=1; 1^1=0 |
~ | 取反运算符,按操作数转化成的二进制位来进行”取反“运算,规则为:~1 = -2; ~0 = -1 |
<< | 左移运算符,将操作数的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 |
>> | 右移运算符,将操作数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 |
样例:
#include <stdio.h>
int main()
{
int a = 37; // 37 = 0010 0101
int b = 14; // 14 = 0000 1110
printf("a & b: %d\n", a & b); // 0000 0100 = 4
printf("a | b: %d\n", a | b); // 0010 1111 = 47
printf("a ^ b: %d\n", a ^ b); // 0010 1011 = 43
printf("~a: %d\n", ~a); // 1101 1010 = -38
printf("a << 2: %d\n", a << 2); // 1001 0100 = 148
printf("a >> 2: %d\n", a >> 2); // 0000 1001 = 9
return 0;
}
执行结果:
a & b: 4
a | b: 47
a ^ b: 43
~a: -38
a << 2: 148
a >> 2: 9
5. 赋值运算符
运算符 | 描述 |
---|---|
= | 赋值运算符,将右边操作数的值赋给左边操作数 |
+= | 加且赋值运算符,将左边操作数加上右边操作数的结果赋值给左边操作数 |
-= | 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 |
*= | 乘且赋值运算符,把左边操作数乘以右边操作数的结果赋值给左边操作数 |
/= | 除且赋值运算符,把左边操作数除以右边操作数的结果**(取整)**赋值给左边操作数 |
%= | 求模且赋值运算符,把左边操作数除以右边操作数的结果**(取余)**赋值给左边操作数 |
<<= | 左移且赋值运算符,把左边操作数左移右边操作数位后的结果赋值给左边操作数 |
>>= | 右移且赋值运算符,把左边操作数右移右边操作数位后的结果赋值给左边操作数 |
&= | 按位与且赋值运算符,把左边操作数与右边操作数按位与运算的结果赋值给左边操作数 |
|= | 按位或且赋值运算符,把左边操作数与右边操作数按位或运算的结果赋值给左边操作数 |
^= | 按位异或且赋值运算符,把左边操作数与右边操作数按位异或运算的结果赋值给左边操作数 |
样例:
#include <stdio.h>
int main()
{
int a = 4;
printf("a = 36: %d\n", a = 36); // a被赋值为36
printf("a += 4: %d\n", a += 4); // a = a + 4 = 40, a被赋值为40
printf("a -= 4: %d\n", a -= 4); // a = a - 4 = 36, a被赋值为36
printf("a *= 2: %d\n", a *= 2); // a = a * 2 = 72, a被赋值为72
printf("a /= 2: %d\n", a /= 2); // a = a / 2 = 36, a被赋值为36
printf("a %= 3: %d\n", a %= 10); // a = a % 10 = 6, a被赋值为6
printf("a <<= 2: %d\n", a <<= 2); // a = a << 2 = 24, a被赋值为24
printf("a >>= 2: %d\n", a >>= 2); // a = a >> 2 = 6, a被赋值为6
printf("a &= 2: %d\n", a &= 2); // a = a & 2 = 2, a被赋值为2
printf("a ^= 2: %d\n", a ^= 2); // a = a ^ 2 = 0, a被赋值为0
printf("a |= 2: %d\n", a |= 2); // a = a | 2 = 2, a被赋值为2
return 0;
}
执行结果:
a = 36: 36
a += 4: 40
a -= 4: 36
a *= 2: 72
a /= 2: 36
a %= 3: 6
a <<= 2: 24
a >>= 2: 6
a &= 2: 2
a ^= 2: 0
a |= 2: 2
6. 其他运算符
运算符 | 描述 |
---|---|
& | 取变量的地址 |
* | 1. 指针指向一个变量,*p代表一个指针指向变量p 2. 解引用运算符,*p表示取出变量p的值 (详见指针章节讲解) |
(数据类型) | 强制类型转换 |
sizeof | 获取操作数的数据类型存储长度(单位为字节) |
exp1 ? exp2 : exp3 | 条件运算符,如果满足条件exp1,则执行exp2,否则执行exp3 |
(exp1, exp2, exp3,…) | 逗号表达式,按顺序执行exp1,exp2,exp3,返回最后一个执行的结果 |
[] | 数组下标引用**(详见数组章节讲解)** |
.或-> | 结构体调用结构成员**(详见结构体章节讲解)** |
样例:
#include <stdio.h>
int main()
{
int a = 1;
printf("(int)12.03: %d\n", (int)12.03); // 强制转换数据为int类型,输出为12
printf("sizeof(8): %d\n", sizeof(8)); // 计算int数据类型的存储大小为4个字节
printf("(7 > 6 ? 4 : 5): %d\n", (7 > 6 ? 4 : 5)); // 7 > 6成立,则返回4;
printf("(a = 4, a += 4, a = 6, a +=3)4: %d\n", (a = 4, a += 4, a = 6, a +=3));
//按顺序执行,a先被赋值4,然后再被赋值8,接着被赋值6,最后被赋值9,则输出最后执行后的9
return 0;
}
执行结果:
(int)12.03: 12
sizeof(8): 4
(7 > 6 ? 4 : 5): 4
(a = 4, a += 4, a = 6, a +=3)4: 9