第一个程序

  1. #include <stdio.h>
  2. int main(int argc, char **argv)
  3. {
  4. printf("hello, world\n");
  5. return 0;
  6. }
  1. # gcc hello.c
  2. # ./a.out

包含头文件的两种方式

  1. #include <头文件.h> : gcc 编译器将在 /usr/include 目录下查找头文件
  2. #include "头文件.h" : gcc 编译器将现在当前目录下查找,找不到再到 /usr/include 目录下查找头文件

gcc 编译器

  1. gcc 选项 源文件

选项:

  • -o : 指定输出文件名, 后面跟一个要输出的文件名
  • -E : 预处理选项
  • -c : 只编译不链接

gcc 编辑步骤:

  1. 预处理:gcc 将执行预处理指令,如包含头文件,将头文件内容拷贝到c文件中

    1. gcc -E file.c -o file.i
  2. 编译:只编译不链接

    1. gcc -c -o file.o file.i
  3. 链接:将代码中用到的别的内容函数包含进来

    1. gcc -o file file.o

直接编译可执行文件:

  1. gcc -o file file.c

数据类型

类型 关键字 字节数 数字范围 占位符 常量符号
整型 char 1 -128 ~ 127 %c 输出字符
%hhd 输出数字
unsigned char 1 0 ~ 255
short 2 -32768 ~ 32767 %hd
unsigned short 2 0 ~ 65535 %hu
int 4 -2^31 ~ 2 ^ 31 - 1 %d gcc默认为int类型
unsigned int 4 0 ~ 2^32-1 %u U u
long(32位系统下) 4 -2^31 ~ 2 ^ 31 - 1 %ld L l
long(64位系统下) 8 -2^63 ~ 2 ^ 63 - 1
unsigned long
(32位系统下)
4 0 ~ 2^32-1 %lu UL ul
unsigned long
(64位系统下)
8 0 ~ 2^64-1
long long 8 -2^63 ~ 2 ^ 63 - 1 %lld (c99) LL ll
unsigned long long 8 0 ~ 2^64-1 %llu (c99) ULL ull
浮点型 float 4 %f 保留多余的0
%g 不保留
F f
double 8 %lf 保留多余的0
%lg 不保留
gcc 默认为double类型

进制

十六进制常量: 0X0x 开头的数, 如 0xFF
格式化输出符号:

  • %x a~f字母为小写, %X A~F字母为大写, %#x 0x开头并且字母为小写, %#X

八进制常量: 0 开头的数,如 076

十六进制与二进制转换表

十六进制 二进制 十进制
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
A 1010 10
B 1011 11
C 1100 12
D 1101 13
E 1110 14
F 1111 15

运算符

算术运算符

+ - * / %
取余

如果除数为浮点数,最后得到 inf 无穷大;如:5/0.0

赋值运算符

= += -= *= /= %=
基础赋值
a = 10
b = c = 10
a += b
等价于
a = a+b
a -= b
等价于
a = a - b
a = b
等价于
a = a
b
a /= b
等价于
a = a / b
a %= b
等价于
a = a % b

自增运算符

前++,如 ++a 后++,如 a++ 前—,如 —a 后—,如 a—
先对变量进行加一,
后再用变量值进行表达式计算
先用变量值进行表达式计算,
再对变量进行加一
先对变量进行减1,
后再用变量值进行表达式计算
先用变量值进行表达式计算,
再对变量进行减1

不能对常量进行自增或自减操作

关系运算符

== != > < >= <=
等于 不等于 大于 小于 大于等于 小于等于

逻辑运算符

&& || !

逻辑运算属于短路运算
例如:
表达式1 && 表达式2
如果 表达式1 为false, 则不再执行表达式2
表达式1 || 表达式2
如果 表达式1 为true,则不再执行表达式2

位运算符

& | ^ ~ << >>
按位与 按位或 异或 取反 左移 右移
1 & 1 = 1
1 & 0 = 0
0 & 0 = 0
1 | 1 = 1
1 | 0 = 0
0 | 0 = 0
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 0 = 1
~ 1 = 0
~ 0 = 1
左移后低位补0 右移后高位补0
  • 左移运算和乘法:a << n 等价于 a * 2^n
  • 右移运算和除法:a >> n 等价于 a / 2^n

开发中常用位操作公式

清零公式:

  1. 将某个变量的某1个位清0,其他位不变: A &= ~(1 << 位编号)
  2. 将某个变量的某2个连续的位清0,其他位不变: A &= ~(3 << 位编号)
  3. 将某个变量的某3个连续的位清0,其他位不变: A &= ~(7 << 位编号)
  4. 将某个变量的某4个连续的位清0,其他位不变: A &= ~(0xF << 位编号)

置1公式:

  1. 将某个变量的某1个位置1,其他位保存不变: A |= (1 << 位编号)
  2. 将某个变量的某2个连续位置1,其他位保存不变: A |= (3 << 位编号)
  3. 将某个变量的某3个连续位置1,其他位保存不变: A |= (7 << 位编号)
  4. 将某个变量的某4个连续位置1,其他位保存不变: A |= (0xF << 位编号)

    指针运算符

    | & | * | | —- | —- | | 取地址运算符 | 解引用运算符 | | 获取一个变量在内存对应的首地址
    占位符 %p 用来显示地址信息 | 根据变量的首地址获取内存中的数据 |

三目运算符

  1. 表达式结果 = 表达式1 ? 表达式2 : 表达式3
  2. if 表达式1 == true then 表达式结果 = 表达式2
  3. else if 表达式1 == false then 表达式结果 = 表达式3

运算符优先级

开发中使用 ( ) 来确定表单式优先级

数据类型转换

  • 隐式转换:如果表达式中不同数字的数据不同,gcc编译器先将数据类型转换成相同的类型;
    • 把占内存小的转换成占内存大的类型
    • 如果既有整型又有浮点型,将整型转换成浮点型
    • 如果既有无符号类型又有有服务数据类型,将有符号转换成无符号类型
  • 强制转换(开发中推荐,代码可读性好)
    • (类型)变量 : 类型为需要将变量强制转换的类型;如: int a=10; cahr b=(char)a;
    • 强制转换类型需注意数据丢失

结构语句

分支结构

if else

  1. if (表达式1) {
  2. // 表达式1 为true,执行此处代码
  3. else {
  4. // 否则执行此处代码
  5. }
  6. if (表达式1) {
  7. // 表达式1为 true,执行此处代码
  8. }else if (表达式2) {
  9. // 表达式2为true,执行此处代码
  10. }else {
  11. // 表达式1和表达式2都为false,则执行此处代码

switch case

  1. switch(控制表达式) {
  2. case 常量表达式1:
  3. 语句1;
  4. break;
  5. case 常量表达式2:
  6. 语句2;
  7. break;
  8. ...
  9. case 常量表达式N:
  10. 语句n;
  11. break;
  12. default:
  13. 语句M;
  14. }
  • 控制表达式被当成整数处理(int),可以是字符,但不能是浮点类型或字符串
  • 处理表达式必须是常量,不能有重复的case出现
  • case后不加break将继续执行下一个case语句
  • default 可以在任意case前面,但case语句中必须加break
  • 如果要在case中定义变量,最好在case语句中加 {} ,该变量只在本case中个有效 ```c switch(exp) { case val1:{
    1. char a = 'a';
    2. printf("%c", a);// 变量a只在当前case中有效
    3. break;
    } case val2:
    1. // 此处变量a不能使用
    … }
  1. <a name="ogCre"></a>
  2. ## 循环结构
  3. <a name="5CUkg"></a>
  4. ### for循环
  5. ```c
  6. for(表达式1; 表达式2; 表达式3) {
  7. 循环语句;
  8. }

执行步骤:

  • 步骤1. 首先执行表达式1,只执行一次
  • 步骤2. 然后执行表达式2
    • 表达式2 == true ,执行循环语句
    • 表达式2 == false 结束循环
  • 步骤3. 如果表单2 == true 执行完循环语句后,执行 表达式3,再跳到 步骤2 继续执行

表达式1,表达式2,表达式3都可省略

while循环

  1. while (表达式) {
  2. 循环语句
  3. }

表达式为true 则执行循环语句,否则结束循环

do while循环

  1. do {
  2. 循环语句;
  3. }while (表达式);

步骤:

  • 步骤1,先执行循环语句
  • 步骤2,执行表达式,表达式为true跳转到步骤1,否则循环结束

break和continue

  • break 结束当前循环
  • continue 跳过本次循环,继续下一次循环

goto

可让CPU跳转到任何一条语句去执行

  1. 标签定义
  2. 标签名:
  3. 语句
  4. 跳转到定义标签
  5. goto 标签名

Linux中goto经典使用: 分配3块内存,只要有一块内存分配失败,程序结束,并释放之前分配成功的内存

  1. int main(void)
  2. {
  3. int ret = 0; //0:表示分配内存成功;1:表示分配内存失败
  4. printf("分配第一块内存.\n");
  5. if(1 == ret)
  6. goto free1;
  7. printf("分配第二块内存.\n");
  8. if(1 == ret)
  9. goto free2;
  10. printf("分配第三块内存.\n");
  11. ret = 1; //表示第三块分配失败了
  12. printf("第三块分配失败了.\n");
  13. if(1 == ret)
  14. goto free3;
  15. printf("三块内存都分配成功.\n");
  16. return 0; //给操作系统返回成功
  17. free3:
  18. printf("释放第二块内存.\n");
  19. free2:
  20. printf("释放第一块内存.\n");
  21. free1:
  22. return ret; //给操作系统返回失败
  23. }

数组

  1. 元素数据类型 数组名[数组长度];
  2. 元素数据类型 数组名[长度(又称数组元素个数)] ={初始化列表(如果有多个,用逗号,分开)};
  3. 例:
  4. int arr[10]; // 元素值为定义
  5. int arr1[5] = {1, 2, 3, 4, 5};
  6. int arr2[] = {1,2,3}; // 长度自动推断
  7. // 对于未赋值的数组元素默认值为0
  8. int arr3[5] = {1, 2, 3}; // 1, 2, 3, 0, 0
  9. int arr4[5] = {1}; // 1, 0, 0, 0, 0
  10. int arr5[5] = {}; // 0, 0, 0, 0, 0
  11. 访问元素:使用[]运算符 数组下标
  12. 数组元素 = 数组名[下标]
  13. 修改元素值
  14. 数组名[下标] = 元素值
  • 数组分配的内存是连续的
  • 数组名就是整个数组的首地址,等于数组第0个元素的首地址
  • 数组长度又称为数组元素个数,而不是数组分配内存的大小
  • 数组下标就是数组中元素的编号(索引号,index),下标从0开始
  • 数组中元素访问通过 [] 运算符 和 数组下标
  • 需注意数组下标越界问题, 对于数组越界范围不会报错,越界修改数组时程序报错(segment falult 已放弃(核心已转储))

求数组长度

  1. 元素个数 = sizeof(数组名) / sizeof(数组的第0个元素)

变长数组

  1. int len;
  2. scanf("%d", &len);
  3. // 长度使用变量
  4. int arr[len];

多为数组(二维数组)

  1. 数组元素类型 二维数组名[二维数组长度][一维数组长度] = {初始化表};
  2. 例:
  3. int arr[5][10];
  4. int arr[3][4] = {{1,2,3,4}, {11,12,13,14}, {21,22,23,24}};
  5. int arr[3][4] = {1,2,3,4,11,12,13,14,21,22,23,24}
  6. int arr[][2] = {1,2,3,4,5,6} // arr[3][2]
  • 一位数组的长度可以省略,但二维数组长度不能省略
  • 数组名arr为二维数组的搜地址 = &arr[0][0]
  • sizeof(arr) 获取整个二维数组占用的内存大小
  • sizeof(arr[0]) 获取二维数组中第0个一位数组元素占用的内存大小
  • sizeof(a[0][0]) 获取二维数组中第0个一维数组中第0个元素占用的内存大小
  • sizeof(a)/sizeof(a[0]) 获取二维数组的长度
  • sizeof(a[0])/sizeof(a[0][0]) 获取一维数组的长度

函数

  1. // 声明函数
  2. extern 返回值数据类型 函数名(形参表);
  3. // 函数定义
  4. 返回值数据类型 函数名(形参表){
  5. 一堆函数体语句;
  6. // 返回值
  7. return 返回值
  8. }
  9. // 调用函数
  10. 函数名(实参)
  11. 返回值 = 函数名(实参)
  • 函数声明:
    • 如果函数定义在函数调用之前,可以省略函数声明,否则必须声明
    • 声明函数时加 extern 提高代码可读性,建议添加
    • 如果函数没有声明,gcc会给函数默认加一个 extern int 函数名(); 声明,不建议
    • 函数名的命名遵循标识符的命名规则
  • 函数定义
    • 返回值数据类型:函数允许结束后返回值的数据类型,如函数无返回值,返回数据类型用 void 关键字
    • 形参表:
      • 就是一堆的变量定义,多个形参使用多个逗号, 分开,作用域在函数体内
      • 如无形参使用 void 关键字
      • 形参个数建议不要超过4个,否则影响函数使用效率
    • 返回值:
      • 函数使用 return 关键字返回值
      • 函数返回值类型 void ,则可以使用 return 后面不跟值结束函数
      • 如果函数定义了数字返回值,函数不 return ,则gcc默认会返回随机数
      • exit() 函数,让程序强制结束,是要函数需包含头文件 stdlib.h
    • 函数调用
      • 实参的内存空间和形参的内存空间是独立的,地址是不一样的,但是里面存储的数据是一样的!

作用域和可见性

C语言变量可见性:

  • 局部变量:定义在函数内部的变量
  • 全局变量:定义在函数外部的变量,变量名建议加前缀 g_

static :静态变量

  • static 修饰的全局变量只能用于本文件,其余文件不能访问
  • static 修饰的函数(定义函数时)只能用于本文件,其余文件不可访问调用 ```c static int a = 10;

static int fun(int x, int y) { //…
}

  1. 局部变量:
  2. - 静态局部变量:从定义地方开始分配内存直到程序结束,程序运行期间只分配一次内存空间,下次不会再分配空间,直接使用上次分配的内存空间
  3. - 非静态局部变量:从定义地方开发分配内存直到代码块执行结束后操作系统回收内存空间,下次使用重新申请
  4. 全局非静态变量:
  5. - 作用域
  6. - 在同一文件下,从定义的地方开始依次向下所有的函数都可以访问,之前函数无法访问
  7. - 在不同文件中,范围是从声明( `extern` 声明)的地方开始依次向下所有函数都可以访问,之前函数无法访问
  8. - 生命周期:
  9. - 从启动,运行程序时操作系统就会分配内存,直到程序结束,操作系统回收内存
  10. - 使用时需加互斥锁访问保护
  11. ```c
  12. demo1.c文件
  13. -----------------
  14. extern int g_num;
  15. extern int add();
  16. int main(void)
  17. {
  18. printf("%d\n", g_num);
  19. add();
  20. printf("%d\n", g_num);
  21. }
  22. =================
  23. demo2.c 文件
  24. -----------------
  25. int g_num = 10;
  26. int add()
  27. {
  28. g_num++;
  29. }

全局静态变量:

  • 作用域:只能在定义文件中访问,并且是从定义的地方开始依次向下所有的其他函数都可以访问,之前的函数不可访问
  • 生命周期:同全局非静态变量