准备

由于PE方式只适用于安装windows,因此选择常规的制作启动U盘进行Ubuntu的安装。

操作系统镜像下载(桌面版,文件名后缀 .iso.torrent)

3G及以上空间的U盘/移动硬盘(用于装载系统镜像)

制作启动U盘

  • 方式一:Windows 10 可以直接打开 iso镜像文件,不用下载任何工具

    1. 格式化U盘

    2. 双击ISO文件,然后选中打开的所有文件,复制到U盘即可

  • 方式二:下载U盘制作制作工具Rufus(无需安装,可直接运行)

    1. 选择设备(需要被制作的U盘/移动硬盘)

      • 只有一个外接设备时会自动读取
      • 如果外接设备是移动硬盘,需要显示高级设备选项 — 显示USB外置硬盘
    2. 选择操作系统镜像

    3. 分区类型理论上(联想Y480不知道为什么无法通过UEFI成功安装))

      硬盘格式与引导方式、MBR与GPT、UEFI 与LEGACY bios的区别

      • 若选择GPT,之后系统安装时选择UEFI启动推荐

      • 若选择MBR,之后系统安装时选择BIOS启动,否则报错(该模式一定能够安装成功)

    4. 其余选项默认,点击开始,即可制作启动U盘

    Rufus制作启动U盘

安装系统

  1. 重启电脑,进入选择启动方式界面(联想笔记本:F12;惠普笔记本:F9)

  2. 选择启动U盘

    • 想要安装系统的硬盘的格式为GPT,选择UEFI启动,即 EFI USB Device
    • 启动U盘分区类型为MBR,选择BIOS启动,即 选择对应的U盘
  3. 欢迎 — 中文 — 安装Ubuntu

  4. 键盘布局 — 汉语

  5. 无线 — 不连接

  6. 更新和其他软件 — 最小安装

  7. 安装类型 — 其他选项 — 进行分区

    硬盘大小110GB

    • UEFI启动:

      空间大小 主分区/逻辑分区 类型
      200MB 逻辑分区 EFI
      8GB
      (和内存大小一致)
      主分区 swap(交换)
      40GB 逻辑分区 ext4
      挂载至 “/”
      剩余空间 逻辑分区 ext4
      挂载至 “/home”
    • BIOS启动:

      空间大小 主分区/逻辑分区 类型
      200MB 主分区 biosxx
      8GB (和内存大小一致) 主分区 swap(交换)
      40GB 逻辑分区 ext4 挂载至 “/”
      剩余空间 逻辑分区 ext4 挂载至 “/home”
  8. 完成安装,根据提示重启即可(可拔去启动U盘/硬盘,避免再次从启动U盘启动)


将 从主服务器下载 更换为 国内镜像站点下载

​ 显示应用程序 - 软件和更新 - Ubuntu软件 - 下载自 - 其他站点 - 选择最佳服务器(自动选择最合适的站点)


更新系统文件

打开终端的快捷键:Ctrl+Alt+T

1
2
sudo apt update
sudo apt upgrade

获得各种开发工具

sudo apt-get install build-essential

  • 安装文字编辑器vim浏览器chromium

    1
    2
    sudo apt-get install vim
    sudo apt-get install chromium-browser
  • 树视图

    sudo apt-get install tree

下载

官网下载Cmder(压缩包,解压即用),有两种不同的版本可供选择:

  • Mini
  • Full(附带msysgit工具)

可根据设备是否已安装git自行选择


配置

添加系统环境变量

我的电脑 - 右键属性 - 高级系统设置 - 环境变量 - 系统变量,在Path中添加Cmder路径


添加Cmder到右键菜单

  1. Win + R - 输入Cmder,确认,打开Cmder
  2. 点击右下方的Create new console
  3. Startup command or {Task} name - {bash::bash}
  4. Run as administrator
  5. Start,即打开一个管理员权限的控制台
  6. 输入 Cmder.exe /REGISTER ALL,回车。即可在每个文件夹中鼠标右键右键 - Cmder here,打开Cmder

默认开启设置

Cmder窗口右下角右键Show system menu - General - Choose your startup task or even a shell with arguments: - {bash::bash} - Save settings


关闭Tab不提示

Cmder窗口右下角右键Show system menu - General - Confirm - Close confirmations下的复选框全不勾选 - Save settings


参考:Cmder 使用 笔记

简介

Dev-C++是一个SourceForge的项目,早已停止更新

缺点:

  • 分辨率低,脱节于屏幕素质的提升,使用感官不佳
  • 中文注释容易乱码,且没有明显的选项用来修改编码格式

下载安装

安装过程中的语言没有中文选项,选择English即可,按照默认选项安装。首次运行时,选择使用软件时的语言为简体中文即可,按照默认选项完成初运行配置。


主题

  • 菜单栏 - 工具 - 编辑器选项 - 语法 - 预设 - Obsidian 黑曜石主题

工具 - 编辑器选项

语法 - 预设 - Obsidian 黑曜石主题

  • 菜单栏 - 工具 - 编辑器选项 - 基本

    • 显示编辑器提示
    • 显示函数提示
    • 高亮显示当前行 - 色彩 - Black


字体

菜单栏 - 工具 - 编辑器选项 - 显示 - 字体 - YaHei Consolas Hybrid

字体下载: Consolas和微软雅黑混合字体


快捷键

功能 快捷键
自动整理代码 Ctrl + Shift + A
代码补全 Ctrl + Space(和输入法切换快捷键冲突)

自定义快捷键:工具 - 快捷键选项 - 菜单项的底部 - Show Code Completion(代码补全),自定义即可。


添加 C99/C++11 标准

工具 - 编译选项 - 编译时加入以下命令 - -std=c99-std=C++11


调试

初次启用调试,出现“项目没有调试信息,您想打开项目调试选项并重新生成吗”

菜单栏 - 工具 - 编译选项 - 代码生成/优化 - 连接器 - 产生调试信息 - Yes

调试窗口查看变量的实时数据

开启调试后,有三种方式可以查看变量的实时数据:

  1. 左侧调试窗口 - 空白处鼠标右键 - 添加查看
  2. 下侧调试窗口 - 添加查看
  3. 代码界面鼠标在变量名处停留,会显示当前变量值


参考:

准备:下载卡硬工具箱免安装版至U盘或其他移动设备。

以下任何一步有问题,退货。

  1. 检查笔记本外壳是否有指纹、裂痕、凹陷,机身是否有变形,屏幕是否完好
  2. USB接口是否有抽插痕迹。
  3. 不插电源,直接按电源键,若能开机,则很可能是二手
  4. 插上电源,按电源键开机。
  5. 进入系统,系统初始化设置(若没有设置界面,直接进入了系统,二手)。
  6. 不连接网络,防止后续检查有问题无法退货。
  7. 进入桌面后,插上有卡硬工具箱的U盘。
    1. 显示器工具 - 检测屏幕坏点。
    2. 硬盘工具 - Diskinfo - 检查通电次数和通电时间(一般通电时间在24小时内,通电次数在150次以内,可以判定为新机硬盘)。

上述步骤没有出现问题后,依然不要联网,在无理由退货时间内使用电脑,以便后续使用不满意时方便退货。


参考:笔记本电脑验机指南

彻底拆机清灰(涂硅脂、更换)

清理键盘

  1. 键盘在D面有两个螺丝固定,先卸下D面挡板,再卸下键盘螺丝、固定光驱的螺丝。
  2. 按照彻底拆机的拆卸键盘方法即可取下键盘。

  • 电脑屏幕色彩管理软件:LUT Manager
  • MAC自带的颜色配置文件:MAC.icc
  • 适用系统:Windows

  1. 下载LUT Manager和MAC.icc
  2. 将MAC.icc放到系统盘/windows/system32/spool/drivers/color/目录下
  3. 打开控制面板(查看方式-图标)- 颜色管理 - 高级 - 更改系统默认值 - 高级 - 使用Windows显示器校准 - 关闭
  4. 设备 - 显示器 - 使用我对此设备的设置 - 添加 - 选择MAC.icc - 设置为默认配置文件 - 关闭

参考:矫正色差的软件

优势

  1. 可以更改硬盘格式与引导方式
  2. 可以备份原有系统的数据
  3. 自带修复软件,可以在不重装系统的情况下修复系统

准备

  • PE安装工具下载:微PE工具箱
  • 操作系统镜像下载:MSDN
  • 8G及以上空间的U盘/移动硬盘(主要用于存放系统镜像)

安装系统

  1. 安装PE到U盘/移动硬盘

  2. 将操作系统镜像放到U盘(或移动硬盘对应的存储分区)

  3. 重启电脑,进入选择启动方式界面(联想笔记本:F12;惠普笔记本:F9)

  4. 选择PE所在设备,即启动PE

  5. 启动桌面上的Windows安装器

    • 第一行:选择操作系统镜像文件

    • 第二行(选择引导驱动器):想要安装系统的硬盘的格式(硬盘格式与引导方式、MBR与GPT、UEFI 与LEGACY bios的区别)不同,需要选择的盘区也不同。

      1. GPT格式:选择ESP分区(大约90~250MB大小的一个隐藏盘区)
      2. MBR格式:选择要装系统的盘区即可

      两种方式都需要右边的三个指示灯不为红色(黄绿两色OK)

    • 第三行(安装磁盘的位置):选择系统将要安装的盘区即可。

    • 第四行:选择安装的Win 10版本。

    完成以上四行内容即可点击开始安装。

    ​ 注:用 DiskGenius 格式化硬盘/转换硬盘格式 时记得勾选对齐分区到此扇区数的整数倍(即4K对齐,无需修改具体参数)。

  6. 重启系统,进行Win 10的初始化设置,建议进入桌面后再联网,操作过程会快一些。


安装驱动

去官网下载驱动安装,或者下载驱动精灵免安装版安装驱动(另外,Windows自身的更新会下载部分相关驱动)。

注:部分旧机型官网没有Windows10版本的相关驱动,不建议官网下载相关旧驱动,很可能不兼容。直接使用驱动精灵无脑安装基本驱动即可。

注注:博主的老机子联想Y480的触摸板在驱动精灵中没有相关的驱动,无法使用Win10的触摸板手势,但是鲁大师的驱动检测功能下可以看到触摸板驱动,选择升级即可使用手势。博主猜测此方法同样适用于部分老机型,不得不赞美一下娱乐大师。

注注注:Elitebook 830 G5的固态硬盘是三星的PM961,需要加上Turbo驱动提速,官网没有直接的链接,可以通过驱动精灵安装。


参考

Win 10全面官方装系统教程

变量名

传统的C语言用法中,变量名使用小写字母,符号常量名使用大写字母


类型限定符

类型限定符signed与unsigned可以用于限定char类型或任何整型

  • signed (默认)
    如char类型 signed char,取值范围为-128~127
  • unsigned无符号类型
    总是正值或0,例如char类型,unsigned char 取值范围为0~255

定义常量

  • 方法一: const 数据类型 变量名 = 常量;推荐

    例: const int AMOUNT=100;

    const也能配合数组参数使用,表明函数不能修改数组元素的值。

  • 方法二:#define 标识符 常量
    末尾没有分号
    例:#define LOWER 0


定义布尔类型

  • 需要在开头写如下代码:#include <stdbool.h>

布尔类型为bool (Java中为Boolean)


数组

例:int number[100]; (Java中为 int[] number = new int[100];)

(C99开始,可以用变量定义数组大小

二维数组的初始化

  • 列数必须给出,行数可以交给编译器来数

  • 每行一个{},用逗号分隔,

  • 最后的,可以存在,有古老的传统

  • 如果内容省略,表示补0

  • 也可以用定位(C99 ONLY)

    1
    2
    3
    int a[10] = {
    [0] = 2, [2] = 3, 6,
    };
    • [n]在初始化数据中给出定位
    • 没有定位的数据接在前面的位置后面
    • 其他位置的值补零
    • 也可以不给出数组大小,让编译器算
    • 特别适合初始数据稀疏的数组

数组的大小

sizeof(a)/sizeof(a[0])

  • sizeof给出整个数组所占据的内容的大小,单位是字节
  • sizeof(a[0])给出数组中单个元素的大小,相除就得到了数组的单元个数
  • 数组作为函数的参数时 实际是指针(数组的地址),需要用另一个参数来传入数组的大小
    • 不能在[]中给出数组的大小
    • 不能再利用sizeof来计算数组的元素个数

数组的赋值

  • 数组变量本身不能被赋值
  • 要把一个数组的所有元素交给另一个数组,必须采用遍历

字符类型

char是一种整数,也是一种特殊的类型——字符。

  • ''也是一个字符
  • printfscanf里用%c来输入输出字符

逃逸字符

字符 意义
\b 回退一格
\t 到下一个表格位
\n 换行
\r 回车
\" 双引号
\' 单引号
\\ 反斜杠本身

函数

C语言的编译器自上而下,按顺序分析代码

函数的先后顺序很重要。

C语言的函数可以将声明和定义分离,从而顺利通过编译,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void sum(int begin, int end); //声明,函数原型, 如果没有参数,括号内最好填写 void,更严谨

int main() { //main的参数部分也可以写 void
sum(1,10);
sum(20,30);

return 0;
}

// 定义
void sum(int begin, int end) {
int i = 0;
int sum = 0;
for (i = begin; i <= end; i++) {
sum += i;
}
printf("%d到%d的和是%d\n", begin, end, sum);
}

参数传递

  • 调用函数时,给的值可以与参数类型不完全匹配,编译器自行转换类型,(Java则对类型转换要求严格)。
  • C语言调用函数传参数时,是值传递

指针与数组

  • 一元运算符&
    获取变量的地址,它的操作数必须是变量,没有其他的运算包括其中。
    p = &c; 称p为“指向”c的指针。

  • 一元运算符*
    间接寻址运算符。*作用于指针时,将访问指针所指向的变量。

指针

指针就是保存地址的变量

1
2
3
4
int i;
int *p = &i;

int *p,q; //p为指针,值为所指向的地址;*p是int类型变量,为所指向地址上的值;

指针应用场景

  1. 交换两个变量的值

    1
    2
    3
    4
    5
    void swap(int *pa, int *pb) {
    int t = *pa;
    *pa = *pb;
    *pb = t;
    }
  2. 函数需要返回多个值,某些值就只能通过指针返回

指针与const

  1. 指针是const(在const前):表示一旦得到了某个变量的地址,不能再指向其他变量;不影响改变该地址上的值*

    1
    2
    3
    int *const q = &i; //q是const
    *q = 26; //所指变量的值可以更改
    q++; //ERROR!
  2. 所指的类型是const(在const后):表示不能通过指针去修改那个变量(*不会使那个变量成为const**)

    1
    2
    3
    4
    const int *p = &i; // int const *p 作用相同
    *p = 26; //ERROR! (*p)是const
    i = 26; //OK
    p = &j; //OK

数组参数

以下四种函数原型等价

  • int sum(int *arr, int n);
  • int sum(int *, int);
  • int sum(int arr[], int n);
  • int sum(int [], int)

数组变量是特殊的指针

  • 数组无需用&取地址

  • 数组的单元表达的是变量,需要用&取地址

  • []运算符可以对数组做,也可以对指针做
    p[0] <==> a[0]

  • *运算符可以对指针做,也可以对数组做

  • 数组变量是const的指针,所以不能被赋值
    int a[] <==> int *const a=...


字符串

C语言的字符串以字符数组的形态存在

  • 不能用运算符对字符串做运算
  • 通过数组的方式可以遍历字符串

以整数0结尾的一串字符为字符串。(0或\0是一样的,但是和'0'不同)

  • 0标志字符串的结束,但不是字符串的一部分
  • 字符串以数组的形式存在,以数组或指针(主)的形式访问
  • string.h里有很多处理字符串的函数

字符串变量

1
2
3
char *str = "Hello";
char word[] = "Hello";
char line[10] = "Hello";

字符串常量

例如:Hello,字符串会被编译器变成一个字符数组放在某处,这个数组的长度是5+1,结尾还有表示结束的0

例:char* s = "Hello, world!";

  • s是一个指针,初始化为指向一个字符串常量
    • 由于这个常量存储的地方,实际上s为const char* s。(历史原因,编译器接受不带const的写法)
    • 试图对s所指的字符串做写入会导致严重的后果
  • 如果需要修改字符串,应该用数组:char s[] = "Hello, world!";

选择指针还是数组形式处理字符串?

  • 数组(字符串就存放在当前位置,如果要构造一个字符串

    • 作为本地变量,空间被自动回收
  • 指针(不知道字符串的存储位置,如果要处理一个字符串

    • 处理参数
    • 动态分配空间

读入、输出数据

  • 需要在开头写如下代码:#include <stdio.h>

读取数据,需要在变量名前加上&,从而赋值给变量。

EOF(End Of File)

可以通过printf("%d", EOF);读取EOF的数值,一般的设备上值是-1。
EOF操作:

  • windows:Ctrl + Z
  • unix:Ctrl + D

浮点数的输入输出

  • 输入:scanf("%lf", ...);
  • 输出:
    • printf("%f", ...); //float和double类型,printf函数都使用%f进行说明
    • printf("%ld", ...); //对应long整型的参数

字符的输入/输出

  • 输入 getchar()
    从文本流中读入下一个输入字符,并将其作为结果值返回。
  • 输出 putchar(c)
    打印一个字符

字符串的输入输出

1
2
3
char string[8];
scanf("%s", string);
printf("%s", string);

scanf读入一个单词(到空格、tab或回车为止)


  • program <infile
    从输入文件infile中读取字符。
  • otherprogram | program
    将程序otherprogram的标准输出 通过管道,重定向到程序program的标准输入上。

位运算符

  • &
  • |
  • ^ 异或
  • ~ 求反码
  • >> 右移(高位出现的空位,原来高位是什么,就用什么补该空位;>>>无符号右移,高位的空位用0补

静态变量

  • 外部

    • static声明限定外部变量与函数,可以将对象的作用域限定为被编译源文件的剩余部分
    • 通过static限定外部对象,可以达到隐藏外部对象的目的。
    • 如果把函数名声明为static类型,则该函数名除了对该函数的声明所在的文件可见外,其他文件都无法访问。
  • 内部

    • static类型的内部变量,不管其所在函数是否被调用,都会存在。(一直占据存储空间;自动变量:随着函数的 调用/退出 而 存在/消失)

寄存器变量

register声明告诉编译器,它所声明的变量在程序中使用频率较高


结构(和java中的类 概念类似,只有变量没有函数)

关键字struct引入结构声明。

结构类型

关键字struct后面的名字是可选的,称为结构标记

  • 有结构标记

    1
    2
    3
    4
    5
    6
    struct point {
    int x;
    int y;
    };
    struct point pt = {320, 200}; //定义了一个struct point类型的变量pt
    struct point pt2 = {.y = 200, .x = 320};
  • 无结构标记

    1
    2
    3
    4
    struct {
    int x;
    int y;
    } p1, p2; //p1、p2都是无标记结构,里面有x和y

结构与函数

结构的合法操作只有几种:作为一个整体赋值和赋值(包括向函数传递参数以及从函数返回值),通过&运算符取地址,访问其成员。

例:函数makepoint,带有两个整型参数,并返回一个point类型的结构

1
2
3
4
5
6
struct point makepoint(int x, int y) {
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}

结构指针的使用频度非常高,为了使用方便,C语言提供了另一种简写方式。假设p是一个指向结构的指针,可以用p->结构成员的形式(等价于(*p).结构成员),引用相应的结构成员。

1
2
3
4
5
6
struct point* getStruct(struct point *p) {
scanf("%d", &p->x);
scanf("%d", &p->y);
printf("%d %d", p->x, p->y);
return p;
}

C语言提供一个编译时的一元运算符sizeof,可以用来计算任一对象的长度

sizeof 对象sizeof(类型名)会返回一个整型值,等于指定对象或类型占用的存储空间字节数

类型定义 (typedef)

typedef用来建立新的 数据类型名
例如,声明typedef int Length;
将Length定义为与int具有同等意义的名字,Length与类型int完全相同。

1
2
3
4
5
6
7
8
typedef struct ADate {//ADate同样可以略去
int month;
int day;
int year;
} Date; //简化了复杂的名字

//也可正常构造结构后,按如下方式定义
typedef struct ADate Date;

动态分配内存

C99之前无法用变量作为数组定义的大小,当时如何解决该问题?

malloc函数:在需要时,向操作系统申请存储空间,需要#include <stdlib.h>

void* malloc(size_t size);

  • 向malloc申请的空间的大小以字节为单位
  • 返回的结果是void*,需要类型转换为需要的类型。例如int *a = (int*)malloc(n*sizeof(int))

因为程序中的某些地方可能不通过 malloc调用 申请空间,所以,malloc管理的空间不一定是连续的

释放空间

free(a);
malloc得到的空间一定要有free的习惯,只能free申请来的空间的首地址


常用函数

gets()

gets()函数包含于stdio.h头文件,会一直读取用户输入,直至换行为止;而scanf一直读至空格键


字符串函数

  • strlen 字符串长度
    size_t strlen (const char *s); 返回s的字符串长度,不包括结尾的0

  • strcmp 比较字符串
    int strcmp (const char *s1, char *s2); 比较字符串的大小,返回两者 第一个不同的字符的差值

  • strncmp 比较字符串

    int strncmp (const char *s1, const char *s2, size_t n) 比较字符串,n为比较的字符数量,若前n个字符相同,返回0

  • strcpy 复制
    char* strcpy(char *restrict dst, const char *restrict src); 把src的字符串复制给dst
    restrict表明src和dst不重叠,返回dst

    复制字符串的操作示例:

    1
    2
    char *dst = (char*)malloc(strlen(src)+1);
    strcpy(dst, src);

    memcpy复制

    void* memcpy (void*dest, const void *src, size_t n);

    strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。

  • 字符串中找字符
    char* strchr(const char *s, int c);
    char* strrchr(const char *s, int c);
    返回NULL表示没有找到

  • 字符串中找字符串
    char* strstr(const char *s1, const char *s2);
    char* strcasestr(const char *s1, const char *s2); //忽略大小写 查找字符串

  • 连接字符串

    char *strcat(char *dest, const char *restrict src);将参数 src 字符串复制到参数 dest 所指的字符串尾部


标准库函数 qsort排序

C语言有qsort();C++有sort();Java有Arrays

qsort()声明在stdlib.h文件中。
void qsort(void *base,size_t nelem,size_t width,int (*cmp)(const void *,const void *));

  • base:
    要排序的数组

  • nmemb:
    数组中的元素数目

  • size:
    每个数组元素占用内存空间,可使用sizeof获得

  • cmp:
    比较两个数组元素的比较函数,返回值是int类型。比较函数的第一个参数值a与参数b,此函数需要自定义

    • 返回值 > 0, a 将被排在b后面;
    • 返回值 < 0, a 将被排在b前面;
    • 示例:
      1
      2
      3
      int cmp(const void *a, const void *b) { //升序
      return *(int *)a - *(long int *)b; //不能用'>'、'<'比较符(返回无负值)
      }

转换字符大小写

函数声明在<ctype.h>文件中:

int tolower(int c)

int toupper(int c)


枚举(enumeration)

  • 枚举是用关键字enum来声明的一种自定义的数据类型:

    enum 枚举类型名字 {标识符0, 标识符1, ..., 标识符n};

  • 枚举类型名字第一个字母最好大写,花括号中的标识符是常量符号,只使用大写字母,类型是int,值依次从0到n

    enum Color {RED, YELLOW, GREEN};

  • 主要应用:当需要一些可以排列起来的常量值时,定义枚举就是为了给这些常量值名字

  • 枚举类型可以跟上enum作为类型:void f(enum Color c);Color t = RED;

  • 声明枚举量的时候可以指定值:enum Color {RED = 1, YELLOW, GREEN = 5, BLUE};

    YELLOW的值为2,BLUE的值为6。

  • 不同的枚举常量可以取相同的整数值,但最好采用唯一值,有助于预防难以发现的逻辑错误。


链表

构造结点

1
2
3
4
typedef struct _node {
int value;
struct _node *next;
} Node;

构造链表

1
2
3
typedef struct {
Node *head;
} List;

全局变量

全局变量初始化

  • 没有做初始化的全局变量会得到0值
    • 指针会得到NULL值
  • 只能用编译时刻已知的值来初始化全局变量
  • 它们的初始化发生在main函数之前

如果函数内部存在与全局变量同名的变量,则全局变量被隐藏

静态本地变量

  • 在本地变量定义时加上static修饰符就成为静态本地变量,没有做初始化的静态变量会得到0值
  • 当函数离开的时候,静态本地变量会继续存在并保持其值
  • 静态本地变量的初始化只在第一次进入该函数时,以后进入函数会保持上次离开的值

静态本地变量是特殊的全局变量


编译预处理指令

  • #开头的是编译预处理指令

#define 用来定义宏

  • #define <名字> <值>(结尾没有分号,不是C语句)
  • 名字必须是一个单词,值可以是各种东西
  • 在C语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值(文本替换)

  • 如果一个宏的值中有其他宏的名字,也会被替换
  • 如果宏的值超过一行,最后一行之前的行末需要加\
  • 宏的值后边的注释不会被当作宏的值的一部分
1
2
3
4
#define PI 3.14159
#define PI2 2*PI
#define PRT printf("%f ", PI); \
printf("%f\n", PI2)

预定义的宏

  • _LINE_ :当前行号
  • _FILE_ :文件路径
  • _DATE_ :日期
  • _TIME_ :时间

带参数的宏的原则

  • 一切都要括号

    • 整个值要括号
    • 参数出现的每个地方都要括号

    例:#define RADTODEG(x) ((x)*57.29578)

  • 可以带多个参数

    #define MIN(a,b) ((a)>(b)?(b):(a))

  • 也可以组合(嵌套)使用其他宏

#include 头文件

将included的文件的全部内容原封不动d地插入到所在位置,因此也不一定要在.c文件最前面#include

  • #include "xxx.h" (要求编译器首先在当前目录寻找该文件,如果没有,到编译器指定目录去找)
  • #include <xxx.h> (让编译器只在指定目录寻找)

把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型。#include和宏一样在是编译预处理指令。

#include不是用来引入库的stdio.h中只有printf等函数的原型,用来保证调用时给出的参数值是正确的类型。printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中。现在的C语言编译器默认会引入所有的标准库。

头文件

  • 在使用和定义这个函数的地方都应该#include这个头文件
  • 一般的做法是任何.c文件都有对应同名的.h文件,把所有对外公开的函数原型和全局变量的声明放进去

不对外公开的函数

  • 在函数前加上static使其成为只能在所在编译单元中被使用的函数
  • 在全局变量前面加上static使其成为只能在所在编译单元中被使用的全局变量

变量的声明

  • int i;是变量的定义(产生代码)
  • extern int i;是变量的声明(不产生代码)

标准头文件结构

  • 运用条件编译和宏,保证这个头文件在一个编译单元中只会被#include一次

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #ifndef __LIST_HEAD__
    #define __LIST_HEAD__

    #include "node.h"

    typedef struct _list {
    Node* head;
    Node* tail;
    } List;

    #endif

文件

格式化输出

%[flags][width][.prec][hlL]type

  • Flag
Flag 含义
- 左对齐
+ 强制显示符号(正数会显示+)
(space) 正数留空
0 0填充
  • width
width或prec 含义
number 最小字符数(总长,包括小数点后的位数)
* 下一个参数是字符数,例:printf("%*d, 6, 123");
.number 小数点后的位数
.* 下一个参数是小数点后的位数
  • hlL
hlL(类型修饰) 含义
hh 单个字节
h short
l long
ll long long
L long double
  • type
type 用于
i 或 d int
u unsigned int
o 八进制
x 十六进制
X 字母大写的十六进制
f 或 F float
e 或 E 指数
g float
G float
a 或 A 十六进制浮点数
c char
s 字符串
p 指针
n 读入/写出的个数

格式化输入

%[flag]type

  • flag
flag 含义
* 跳过
数字 最大字符数
hh char
h short
l long, double
ll long long
L long double
  • type
type 含义
d int
i 整数,也可以是16进制、8进制
u unsigned int
o 8进制
x 16进制
a, e, f, g float
c char
s 字符串
[…] 允许的字符(例:*[^,]是到,之前的所有字符都跳过)
p 指针

位运算

按位运算

  • &

    相同位上都为1,结果为1;否则结果为0

  • |

    相同位上至少一个是1,结果为1;否则结果为0

  • ~ 取反

    把1位变0,0位变1

  • ^ 异或

    如果两个位相等,结果为0;两个位不相等,结果为1。

    对一个变量用同一个值异或两次,变量不变(可用于加密)

移位运算

  • << 左移

    • i << j:i中所有的位向左移动j个位置,右边填入0

    • x <<= n 等价于x *= $2^n$

  • >> 右移

    • i >> j:i中所有的位向右移j个位置。unsigned类型,左边填入0;signed类型,左边填入原来的最高位
    • x >>= n 等价于x /= $2^n$

终端界面的说明

示例:
hunter@Hunter-PC:~$

  • 最左侧的hunter目前用户的账号
  • @之后的Hunter-PC主机名
  • ~目前所在目录~当前用户的home目录
  • $提示字符;root的提示符为#,普通用户为$

登录后取得的程序被称为Shell(壳,因为该程序负责最外面跟使用者沟通,被戏称称为壳程序)

在Linux下平常最好使用一般账号来登录,需要动用到系统功能修订时,再使用root。


操作说明及技巧

Linux严格区分大小写

在指令列模式里面下达指令时,会有两种主要的情况:

  • 直接显示结果,然后回到命令提示字符,等待下一个指令的输入
  • 进入到该指令的环境,直到结束该指令,回到命令提示字符的环境

  • 指令太长的时候,可以使用\使指令连续到下一行。\后需要立刻接
    回车,才能跳脱!

快捷键

快捷键 功能
Ctrl + Alt + T 打开终端
Shift + Ctrl + C/V 复制/粘贴

操作命令

Linux命令中,参数一般是无序的,特殊情况除外

更新系统文件

1
2
sudo apt update
sudo apt upgrade

安装软件

sudo apt-get install xxx(程序名)

卸载软件

  • sudo apt-get remove -purge xxx(程序名)

  • sudo apt autoremove

磁盘管理命令

  • 注销Linux:exit(Linux本身还在运行,只是退出了登录的操作)

  • 语系

    • 显示目前支持的语系:
      locale
    • 修改语系:
      1
      2
      LANG=en_US.utf8
      export LC_ALL=en_US.utf8
      LANG只与输出讯息有关,若需要更改其他不同的信息,要同步更新LC_ALL
  • 显示日期:date

  • 计算器:bc(win10的ubuntu子系统需要先安装)
    事实上,该操作是进入到bc这个软件的工作环境
      bc预设仅输出整数,如果要输出小数,需要先执行scale=numbernumber是小数点位数

    • 要离开bc,需要输入quit命令
  • 转换成root用户:sudo su

  • 展示文件和目录:ls

    • -a/--all 展示当前目录下所有文件和目录(隐藏文件和特殊目录以.开头)
    • -l 查询详细列表(可以组合使用-al)
    • -h 友好展示信息
      ls -l可以简写为ll
  • 切换目录:cd

    • ~ 当前用户的家目录
    • / 根目录
    • - 切换到上次访问的目录
  • 显示当前所在目录:pwd(print working directory)

  • 创建目录:mkdir

    • -p(parents) 父目录不存在的情况下,生成父目录
    • -v 显示命令执行过程中的详细信息
  • 删除目录:rmdir

文件操作命令

  • 快捷查看当前文件的内容:cat适合查看少量信息的文件

  • 分页显示内容,标注每行的行号:less -mN(适用于大量数据的查看)

    • 回车 向下n行(需要定义,默认为1行)
    • 空格 向下一页
    • b 向上一页
    • q 退出
  • 快速查看文件后n行的内容:tail -n(查看日志)

  • 复制文件/目录:cp 需要复制的文件 复制的位置

    • -r/--recursive 递归处理,将指定目录下的子目录一并处理
      示例:
      1
      2
      3
      需求:把/root/Demo.java 文件复制到 /root/t1 目录中,改名为d.java
      位置:root
      命令:cp Demo.java t1/d.java
  • 文件移动:mv 需要移动的文件 移动的位置

    • -f 强制性覆盖
    • 文件/目录重命名操作mv 原文件 新文件名(原文件名即转换为新文件名)
  • 文件/目录删除:

    • rm 文件名
      • -f/--force 强制删除
      • -r/--recursive 递归处理,将指定目录下的子目录一并处理
    • rm -rf 目录
  • 查找文件或目录:find [目录名] -name '需要查找的字符串'(不填写目录名则在当前目录下查找)

  • 压缩、解压命令:tar

    • -c 建立一个压缩文件(create)
    • -x 解开一个压缩文件(extract)
    • -z 用gzip压缩
    • -v 压缩过程中显示文件(verbose)
    • -f 使用档名,在f之后要接档名(file)

Linux的压缩包格式:*.tar(打包不压缩)、*.tar.gz(打包并压缩文件大小)
- 压缩:tar -zcvf 压缩包名.tar.gz 需要压缩的内容
例如tar -zcvf test.tar.gz *(将当前目录下内容进行打包压缩)
- 解压:tar -zxvf 需要解压的压缩包名.tar.gz [-C 指定解压路径]

系统命令

  • 查看系统进程:ps -ef(Process Status) 同时会显示执行ps命令的进程信息
    示例:ps -ef | grep -i vim 查看vim相关的进程

  • 强制结束进程:kill -9 pid号

  • 显示网络设备:ifconfig

  • 测试和目标的连通性:ping ip地址

权限命令

  • 权限更改:chmod 权限设置 需要更改权限的文件
    • -R/--recursive 递归处理
      示例:
    • 更改所属用户(user)的权限:chmod u=rwx test.txt
    • 更改所属组(group)的权限:chmod g=rx test.txt
    • 更改其他用户(other)的权限:chmod o=rx test.txt
    • 更改所有用户(all)的权限:chmod a=r test.txt

几个重要的热键

  • Tab
    Tab具有命令补全文件补齐的功能,可以避免打错指令或文件名

    • Tab接在一串指令的第一个字的后面,则为命令补全
    • Tab接在一串指令的第二个字以后时,则为文件补齐
  • Ctrl + c:中断目前程序

  • Ctrl + d: 键盘输入结束;也可以用来取代exit的输入

  • Ctrl + PgUp/PgDown:翻页(指令执行后,显示的内容很长时可以用到)


查看帮助

指令名后加上--help

即可查看指令的用法,如cal --help

man page

  manmanual(操作说明)的简写,通过man 指令名进入的画面,就是man page

man page下能使用的相关操作:

按键 进行工作
空格键 向下翻一页
PgDn 向下翻一页
PgUp 向上翻一页
Home 去到第一页
End 去到最后一页
/string 搜寻string这个字符串,如果要搜寻vbird 的话,就输入/vbird
?string 搜寻string 这个字符串
n, N 利用/?来查找字符串时,可以用n来继续下一个查找,利用N来进
反向查找
q 结束这次的man page

示例(man date):
man date操作之后的页面

在页面第一行,可以看到DATE(1)DATE是指令名称,(1)代表一般用户可使用的指令

代号 代表内容
1 用户在shell 环境中可以操作的指令或可执行文件
2 系统核心可呼叫的函数与工具等
3 一些常用的函数(function)与函式库(library),大部分为C 的函式库(libc)
4 装置文件的说明,通常在/dev 下的文件
5 配置文件或者是某些文件的格式
6 游戏(games)
7 惯例与协议等,例如Linux 文件系统、网络协议、ASCII code 等等的说明
8 系统管理员可用的管理指令
9 跟kernel 有关的文件

man page大致分成以下几个部分:

代号 内容说明
NAME 简短的指令、数据名称说明
SYNOPSIS 简短的指令下达语法(syntax)简介
DESCRIPTION 较为完整的说明,这部分最好仔细看看!
OPTIONS 针对SYNOPSIS 部分中,有列举的所有可用的选项说明
COMMANDS 当这个程序(软件)在执行的时候,可以在此程序(软件)中下达的指令
FILES 这个程序或数据所使用或参考或连结到的某些文件
SEE ALSO 可以参考的,跟这个指令或数据有相关的其他说明!
EXAMPLE 一些可以参考的范例

在查询某个数据时可以这样来查阅:

  1. 先察看NAME的项目,看一下这个资料的意思
  2. 再详看一下DESCRIPTION,这个部分会提到很多相关的资料与使用时机,从这个地方可以学到很多小细节
  3. 如果这个指令已经很熟悉了,主要就是查询关于OPTIONS的部分,可以知道每个选项的意义,可以下达比较细部的指令内容
  4. 最后再看一下,跟这个资料有关的还有哪些东西可以使用。举例来说,date的SEE ALSO就告知我们还可以利用info coreutils date来进一步查阅数据;
  5. 某些说明内容还会列举有关的文件(FILES部分)来提供我们参考!这些都是很有帮助的!
0%