从文件中插入内容到剪切板
你希望把当前文件的文本插入到剪切板中。当你的文笔传输到剪切板上,你可以粘贴它到另外一个应用中。
剪切板拷贝 | 描述 |
---|---|
:%y+ | 拷贝整个文件到剪切板 |
:y+ | 拷贝文件中当前行到剪切板上 |
:N,My+ | 拷贝文件中指定范围的行到剪切板中 |
为了拷贝可视化选择的行到剪切板上,首先可视化选择行,:y+
会显示为:’<,’>y+
。
在拷贝之后,你可以使用传统的<CTRL+V>
操作粘贴这些内容到任意其它应用中。
任何一种编程语言,都会存在作用域的概念,比如C++中的private、public等,在C语言里面,主要根据变量所在的位置来区分,主要有:
所谓的局部变量就是只能被局部访问,一般定义在函数内部或者某个块,在函数的外部是不可知且不能被访问的,比如下面的变量:
1 | /*beginner/scope/scope1.c*/ |
其中的变量a、b、c,也就是1、3、5就是main函数的局部变量。
全局变量按照字母意思就是在整个程序的声明周期都存在,也就是说所有的函数都可以访问到,一般而言,都会位于函数的外面,通常在程序的顶部,比如:
1 | /*beginner/scope/scope2.c*/ |
那么问题就来了,万一全部变量和局部变量定义了相同给的名字,该访问使用哪一个呢,这个记住即可,如果两个名字相同,会使用局部变量,临近原则吧。
1 | /*beginner/scope/scope3.c*/ |
编译也有所不同,方法为:
1 | #beginner/scope/Makefile |
这会产生可执行程序,当程序被执行时,它会产生下列结果:
1 | $ ./scope1 |
如果有两个变量,我希望打印出最大的一个,怎么做呢,很简单一个if-else
语句搞定,嗯嗯,👍。
1 | /*beginner/ternary/ternary1.c*/ |
今天来一个比较搞定的三元操作符,前面有提过什么事三元操作符,参考逻辑运算符原文,简单的理解就是:a?b:c
,如果a为真就返回b,如果a为假就返回c。
所以rst = a?b:c
用if-else
来表达就是:
1 | if (a) |
所以上面的程序可以简化为:
1 | /*beginner/ternary/ternary2.c*/ |
看到没有,奇迹就是用1行能写出4行的效果。
或许你会说用python可以1行写出c语言10行甚至100的功能,这个我承认,所以下一阶段开讲Python
或许你问了,有了if-else还要这个干吗,简单,我们可以做很多事情,比如在宏定义里面定义下面的一行
1 | #define MAX(a,b) a>b?a:b |
后面直接调用MAX就可以得出最大值了,不过这个写法目前看来OK,如果严谨说起来是有bug的,且听下回分解。
另外一个比较有用的例子如下,我觉得很适合英语国家,在我们中国不存在这个问题,就是如果你有一个叔叔和几个叔叔的表达方式不一样:
看到没有,有好几个叔叔就要加个s,这个怎么来办呢,有了三元操作符,解决方案如下:
1 | /*beginner/ternary/ternary3.c*/ |
编译也有所不同,方法为:
1 | #beginner/ternary/Makefile |
这会产生可执行程序,当程序被执行时,它会产生下列结果:
1 | $ ./ternary1 |
存储类型定义了C程序中变量与函数的可见范围和生命周期。
这些关键字位于修饰的函数或变量之前,主要说说下面几个:
auto 存储类是所有局部变量默认的存储类型,不管写不写,默认是auto类型的,比如:
1 | /*beginner/auto/auto1.c*/ |
上面的实例定义了两个带有相同存储类的变量。
auto 只能用在函数内,即 auto 只能修饰局部变量。
register 存储类型用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 &
运算符(因为它没有内存位置)。
1 | /*beginner/register/register2.c*/ |
寄存器主要用于快速访问及变化的变量,比如计数器。不过,定义 register
并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,只是可能,这取决于硬件和实现的限制,所以这个一般不太实用,除非对系统很精通,确认是寄存器使用,才能起到加速的效果。
static有多重用法,这里先说一下作为存储类型的用法,这个功能对于需要在某个函数进行初始化且需要保持状态作用大大滴。
static 存储类型指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内,还未说到多个文件,后续再介绍
全局声明的一个 static 变量或方法可以被任何函数或方法调用,只要这些方法出现在跟 static 变量或方法同一个文件中。
以下实例演示了 static 修饰全局变量和局部变量的应用:
1 | /*beginner/static/static3.c*/ |
可以看到运行的结果为:
1 | $ ./storage3 |
前面说了
auto、
register和
static存储类型,这次说一下extern
存储类型。
在使用这个关键字之前,需要了解多个函数的编译,什么意思呢,就是文件a.c
会使用b.c
或者c.c
的变量、函数等,在编译的时候也需要提供所有的文件。
下面来看看extern
的概念吧。
extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。
extern
修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:
第一个文件:
1 | /*beginner/extern/extern1.c*/ |
第二个文件:
1 | /*beginner/extern/test.c*/ |
可以看到两个文件互为extern
,extern1.c
调用test.c
的test
函数,test.c
调用extern1.c
的number
变量。
编译也有所不同,方法为:
1 | $ gcc -o extern1 extern1.c test.c |
这会产生可执行程序,当程序被执行时,它会产生下列结果:
1 | $ ./extern1 |
删除类似于拷贝,然而你必须使用d替代y。
键 | 描述 |
---|---|
x | 删除当前字符 |
dw | 删除当前词 |
dj | 删除当前行和下一行 |
这里应该可以出来以及技巧,x是删除,p是粘贴,
所以xp不是系统而是交互两个字符
。
关于回车与换行
很久以前,老式的电传打字机使用两个字符来另起新行。一个字符把滑动架移回首位 (称为回车,ASCII码为0D),另一个字符把纸上移一行 (称为换行, ASCII码为0A)。当计算机问世以后,存储曾经非常昂贵。有些人就认定没必要用两个字符来表示行尾。UNIX 开发者决定他们可以用一个字符来表示行尾,linux沿袭Unix,也是如此。而Apple 开发者规定了用r,MS-DOS以及Windows 则继续使用rn表示,所以换行就有了3种方法。
三种行尾格式如下:
这意味着,如果你试图把一个文件从一种系统移到另一种系统,那么你就有换行符方面的麻烦。
因为MS-DOS及Windows是回车+换行来表示换行,因此在Linux下用Vim查看在Windows下写的代码,行尾后“^M”符号。
在Vim中解决这个问题,很简单,在Vim中利用替换功能就可以将“^M”都删掉,键入如下替换命令行:
1 | :%s/^M//g |
注意:
上述命令行中的“^M”符,不是“^”再加上“M”,而是由“Ctrl+v”、“Ctrl+M”键生成的,或者Ctrl+v,再按回车。
或者使用这个命令:
1 | :% s/r//g |
在命令模式中,使用如下指令删除包含指定字符串的行:
1 | :g/string/d |
在命令模式中,使用如下指令删除不包含指定字符串的行:
1 | :v/xxx/d |
1 | $ sed -i "s/$(echo -ne '\u200b')//g" file |
或者在vim中如此操作:
1 | %s/\%u200b// |
使用vim内建的功能,如下即可快速在排序后删除重复的行:
1 | :sort u |
:g/^$/d
:g/^\s*$/d
:g/^\s*#/d
:g/^\s*;/d
:/hello/d
:2,/hello/d
:/hello/,$d
:g/hello/d
:g/.hello/d
:g/^hello/d
:g/hello$/d
:%s/\;.\+//g
%s/\#.*//g
:%s/ABCD.*$//g :
删除所有行的指定字符ABCD到每行末尾的字符
| 键 | 描述 |
| p(小写的p) | 立即粘贴到当前光标位置之后 |
| P(大写的P) | 立即粘贴到当前光标位置之前 |
如果你执行几次的删除操作,如果你希望粘贴这些删除的字符,使用下面的方法。
首先,查看寄存器,使用下面的命令:
1 | :reg |
最近的删除内容会显示在0-9寄存器中,记下你想粘贴的删除词语的寄存器位置。
如果你想粘贴寄存器标号为N的词组,执行下面命令:
1 | “Np |
前面可以看到,使用unoin
共用体可以节省数据的存储空间。
同样,在结构体或者共用体中,使用位域也可以达到这个效果。
先看看什么时候可以使用位域,这个特点大多数人都不会用到,用到的大部分人都基本跟底层打交道,比如驱动开发、单片机开发等。
先看一个最简单的例子,比如我们的红绿灯系统,先定义一个结构体:
1 | typedef struct |
此时如果看一下TrafficLight结构体的大小,应该是12个字节
但是我们知道对于这几种灯而言,只有2中状态,开和关,也就是1和0,也就是1个bit其实就能表达,所以针对这种情况,有了位域的概念,先看一下位域的声明:
1 | typedef struct |
所以交通灯的结构体使用位域的概念就如下所示:
1 | typedef struct |
三色红绿灯加起来一共需要3个bit,所以一个无符号整型就可以容纳这些值了,此时看一下这个结构体的长度,应该为4。
总结一下:
当结构体或共用体中有无符号整型或有符号整型成员时,C语言允许用户指定这些成员所占用的存储位数,即位域。通过将数据存储在它们所需的最小数目的存储位内,位域能够有效地提供存储空间的利用率,但是,要注意,位域成员必须被声明为有符号整型或无符号整型。
代码如下:
1 | /*beginner/struct/struct6.c*/ |
直接输入make
就可以了。
1 | #beginner/struct/Makefile |
1 | $ ./struct6 |
既然位域指定了长度位,所以就涉及到万一赋值超过了会发生什么情况,可以通过给红绿灯赋一个大值看看。
比如复制一个2,那么会得到如下警告:
1 | warning: implicit truncation from 'int' to bit-field changes value from 2 to 0 [-Wbitfield-constant-conversion] |
编译有警告,不过还是生成了可执行文件,运行下看看结果吧。
共用体的访问与结构体类似,也是有2中类型,我们只看看成员访问运算符.
。
所以按照通用的赋值方式,来看一下是否按照我们预定的方式运行。
1 | /*beginner/union/union2.c*/ |
从结果上来看,what are you弄啥嘞,感觉什么跟什么呀,只有最后的字符串是正确的,这也就间接证明了共用体使用相同的存储空间,其他类型的赋值会破坏原先的赋值,正常情况下只有最后一次的赋值才会保证正确结果。
所以我们需要在每次赋值后直接查看结果,是可以保证结果正确的:
1 | /*beginner/union/union3.c*/ |
再次运行,可以看到结果就按照预想的进行了。
1 | #beginner/union/Makefile |
1 | $ ./union2 |
C语言的短小精悍从它的关键字就能够看出来,作为C语言的关键字、保留字,是不能作为变量名、常量名或者其他标识符的,他们有自己独特的含义。
前面的章节中其实多多少少已经进行了讲解,先总结如下:
关键字 | 说明 |
---|---|
auto | 声明自动变量 |
break | 跳出当前循环 |
case | 开关语句分支 |
char | 声明字符型变量或函数返回值类型 |
const | 声明只读变量 |
continue | 结束当前循环,开始下一轮循环 |
default | 开关语句中的”其它”分支 |
do | 循环语句的循环体 |
double | 声明双精度浮点型变量或函数返回值类型 |
else | 条件语句否定分支(与 if 连用) |
enum | 声明枚举类型 |
extern | 声明变量或函数是在其它文件或本文件的其他位置定义 |
float | 声明浮点型变量或函数返回值类型 |
for | 循环语句 |
goto | 无条件跳转语句 |
if | 条件语句 |
int | 声明整型变量或函数 |
long | 声明长整型变量或函数返回值类型 |
register | 声明寄存器变量 |
return | 子程序返回语句(可以带参数,也可不带参数) |
short | 声明短整型变量或函数 |
signed | 声明有符号类型变量或函数 |
sizeof | 计算数据类型或变量长度(即所占字节数) |
static | 声明静态变量 |
struct | 声明结构体类型 |
switch | 用于开关语句 |
typedef | 用以给数据类型取别名 |
unsigned | 声明无符号类型变量或函数 |
union | 声明共用体类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
volatile | 说明变量在程序执行中可被隐含地改变 |
while | 循环语句的循环条件 |
_Bool |
_Complex |
_Imaginary |
inline |
restrict |
---|---|---|---|---|
_Alignas |
_Alignof |
_Atomic |
_Generic |
_Noreturn |
---|---|---|---|---|
_Static_assert |
_Thread_local |
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。
什么意思呢,就是在同一块内存存储可以定义多个数据类型,但是在使用的时候,只有一个变量有效。
这里就有一个问题,变量有大有小呀,对的,所以这个时候共用体的空间为内部变量最大占用空间的值。
如此这般,共用体就可以通过共享存储空间,来避免当前没有被使用的变量所造成的存储空间的浪费。
共用体的成员可以使用任何数据类型,但是一个共用体所占用的存储空间的字节总数,必须保证至少足以能够容纳其占用空间字节数最大的成员。并且共用体每次只允许访问一个成员,也就是一种数据类型,确保按照正确的数据类型来访问共用体中的数据,就是你的责任了。
先看看union的格式:
1 | union [tag] |
其中:
举个例子:
1 | union test |
通过这个例子可以看到,这个结构体的大小是多少呢?可以通过程序来确认一下。
OK,这次我们来聊聊结构体。
任务来了,我想让你给学生建立一个数据库,该怎么来做。
这个学生包含的信息如下:
按照目前学过的知识我们的代码如下,比如先来一个李雷同学的吧:
1 | #include <stdio.h> |