0%

.(点)命令的强大功能

.(点)命令是简单的,也是强大的。.命令重复最后一次文件内容影响命令。下面的例子说明.命令的使用。

  1. 在文件中搜索字符串,使用:/one
  2. 使用one替换two,使用:cwone
  3. 搜索下一个one出现地方,使用:n
  4. 使用two替换one,使用:.(点号)

在上面的例子中,第4步,你不需要再次输入cwtwo。替代的,简单的输入.(点号),它会执行最后一次改变命令,也就是cwtwo。

C语言 文本文件读写

基础储备知识基本结束了,今天看一下稍微高级点的文件读写,这个主要针对的对象是什么呢,比如要统计学生的分数,一般我们都会有一个文件保存所有的学生的成绩,这个时候如果我们能读取这些信息就会少了很多人工输入的工作,在前面,我们的解决方案可能是输入每一个学生的成绩,这次我们通过读取文件来搞定。

而文件的读写又分为两种:

  • 文本文件:可以读懂的文件
  • 二进制文件:读不懂的天数

这个跟程序员的世界很像,因为程序员的世界也是有0和1这两个元素组成的。

先说说直观上比较容易理解的文本文件。

对于文件的读写,一般的步骤为:

  1. 打开文件
  2. 读写文件
  3. 关闭文件

对应的函数为:

  1. fopen
  2. fread/fwrite
  3. fclose

下面详细说说这几个函数.

写文件的部分内容到另一个文件

为了写文件的一部分到新的文件中,你可以使用下面方法中的任意一种。

方法1:在可视化模式下选择特殊的几行。进入可视化模式(使用v或者V),导航到希望的行上,然后执行:

1
:w newfilename

方法2:写入文件的部分到另外一个文件,你可以指定范围,如下所示。它写入当前文件的5到10行到一个新的文件。

1
:5,10w newfilename

C语言 输入和输出续

其实在刚开始学习输入输出的时候,第一个接触的可能不一定是printf和scanf函数,还有两个关于字符和字符串的输入输出函数。

下面分别来是说说:

getchar && putchar

从字面意思来看,很容易理解,就是对字符的获取和输出,所以这两个函数你只能输入一个字符,这是限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
/*beginner/io/io3.c*/
#include <stdio.h>

int main()
{
int c;
printf("Enter a character:");
c = getchar();
printf("You enter :");
putchar(c);
printf("\n");
return 0;
}

gets && puts

这两个函数就比较灵活,处理的为字符串,会等待输入的一行文本,只有在输入回车键或者遇到终止符时,才认为是结束了。

1
2
3
4
5
6
7
8
9
10
11
12
13
/*beginner/io/io4.c*/
#include <stdio.h>

int main()
{
char s[1024];
printf("Enter a string:");
gets(s);
printf("You enter :");
puts(s);
printf("\n");
return 0;
}

NB:不过,对于C语言而言,这两个函数能实现的,printf和scanf都可以实现,并且bug少一些,建议尽量少使用今天提到的4个函数,了解即可。

C语言 输入和输出

这次说说C语言的输入和输出,其实从开始的第一个程序,我们已经和C语言的输出打上交道了,对的,就是printf函数,而输入也有提及,就是scanf函数。

而对于用户或者开发者而言,最直观的程序运行的概念就是你输入给程序信息,程序打印输出给你看到。

BINGO,程序正常运行。

这次来详细说说,在C语言或者Linux的世界里面,所有的设备都是文件,所以访问设备就是访问文件,你可以注意到在Linux系统的根目录有一个dev目录,就是设备的交户口所在地。

如何才能交互呢,在C语言里面,在你运行程序的时候,下面的三个默认文件已经同步打开了,这就方便了我们访问键盘,进行输入或者通过屏幕输出信息:

文件 文件指针 对应设备
标准输入 stdin 键盘
标准输出 stdout 屏幕
标准错误 stderr 屏幕

这里在重温一下C语言中用的最广的printf函数和scanf函数:

  • printf:发送格式化输出到stdout屏幕
  • scanf:从标准输入stdin读取并格式化转交给程序

参考示例:

1
2
3
4
5
6
7
8
/*beginner/io/io1.c*/
#include <stdio.h>

int main()
{
printf("Hello, let's programming using C!!\n");
return 0;
}

示例1展示了最简单的输出一句。

1
2
3
4
5
6
7
8
9
10
11
12
/*beginner/io/io2.c*/
#include <stdio.h>

int main()
{
int all_tips = 100;
printf("Enter how many tips you've learned:");
int learned_tips;
scanf("%d", &learned_tips);
printf("Percents %.2f%%\n", 100.0 * learned_tips / all_tips);
return 0;
}

示例2展示了输入参数,可以看到我们已经学习了47个tips,如果100个可以完成对C语言的入门,那么恭喜,你已经完成一半了。

C语言 函数指针

指针不仅仅能指向变量,也可以指向函数,这个功能主要用在方便快捷地调用函数上。

比如我们结合前面说的三元描述符、子函数来看看如何用函数指针。

看一个例子如下,我们已经写好了如何选择最大值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*beginner/pointer/pointer2.c*/
#include <stdio.h>

int max(a, b)
{
return (a > b ? a : b);
}

int main()
{
int a = 6;
int b = 8;
printf("max is %d\n", max(a, b));

return 0;
}

上面的是比较2个值的大小,如果想比较3个值呢,用上面的代码,新增如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*beginner/pointer/pointer3.c*/
#include <stdio.h>

int max(a, b)
{
return (a > b ? a : b);
}

int main()
{
int a = 6;
int b = 8;
int c = 12;

int t = max(a,b);

printf("max is %d\n", max(t, c));

return 0;
}

在增加一个,4个值呢,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*beginner/pointer/pointer4.c*/
#include <stdio.h>

int max(a, b)
{
return (a > b ? a : b);
}

int main()
{
int a = 6;
int b = 8;
int c = 12;
int d = 9;

int t = max(a,b);
int tt = max(t,c);

printf("max is %d\n", max(tt, d));

return 0;
}

所以看出来,每次新增一个值,就需要多调用一次max函数。

如果使用函数指针就可以很好地解决这个问题,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*beginner/pointer/pointer5.c*/
#include <stdio.h>

int max(a, b)
{
return (a > b ? a : b);
}

int main()
{
int (*p)(int, int) = & max;

int a = 6;
int b = 8;
int c = 12;
int d = 9;

int t = p(a,b);
int tt = p(t,c);

printf("max is %d\n", p(tt, d));

return 0;
}

从这里例子也可以看出来,函数的指针的格式如下所示:

  • 返回值与原函数一致;
  • 定义的参数与原函数类型一致;

不过好像也没有简便很多,不过如果碰到函数名巨长无比的,还是有些作用的,另外我们可以使用内嵌的方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*beginner/pointer/pointer6.c*/
#include <stdio.h>

int max(a, b)
{
return (a > b ? a : b);
}

int main()
{
int (*p)(int, int) = &max;

int a = 6;
int b = 8;
int c = 12;
int d = 9;

printf("max is %d\n", p(p(p(a, b), c), d));

return 0;
}

C语言 指针初探

指针是C语言中最有趣的概念,简洁、优美、快速、复杂、难懂,所以有一本书叫做《C和指针》,足见其强悍之处。

C语言指针一般设计到动态内存分配等高级概念,要想成为一个高手,必须要掌握指针,本节,只需要了解一个概念即可。

即指针是一个变量,指针的值是另外一个变量的地址,可以认为指针指向你这个值的时候,代表的是你家的地址。

看一个例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*beginner/pointer/pointer1.c*/
#include <stdio.h>

int main()
{
char a = 'a';
int b = 1;
float c = 3.1415926;
double d = 3.1415926;

printf("The address of a is %p\n", &a);
printf("The address of b is %p\n", &b);
printf("The address of c is %p\n", &c);
printf("The address of d is %p\n", &d);

return 0;
}
1
2
3
4
5
$ ./pointer1
The address of a is 0x7ffee3e5272b
The address of b is 0x7ffee3e52724
The address of c is 0x7ffee3e52720
The address of d is 0x7ffee3e52718

可以看到变量没有规则,但是出来的地址是有顺序的,按照一定的规则增加或减少。

指针规则

看一下如何使用指针,规则如下:

1
type * var-name;
  • type:是指针的基本类型
  • var-name:为指针的名字

其中type可以是我们前面说过的所有变量类型,比如intdoublefloatchar等。

比如:

1
2
3
4
int *pi;
float *pf;
double *pd;
char *pc;

如果你遇到一个简单输入错误,错误放置相邻的字符,你可以使用xp。例如你输入the替代teh,导航到e,按下xp,它会自动修正输入。

xp

实际上,xp不是真的修正输入,含义如下:

  • x – 删除当前的e字符,也移动光标到下一个字符h处;
  • p – 在当前的字符h之后粘贴前一个删除的字符e。

xp的含义就是“位置交换”。又到了XP的年代吗,错了。

从文件中插入内容到剪切板

你希望把当前文件的文本插入到剪切板中。当你的文笔传输到剪切板上,你可以粘贴它到另外一个应用中。

剪切板拷贝 描述
:%y+ 拷贝整个文件到剪切板
:y+ 拷贝文件中当前行到剪切板上
:N,My+ 拷贝文件中指定范围的行到剪切板中

为了拷贝可视化选择的行到剪切板上,首先可视化选择行,:y+会显示为:’<,’>y+

在拷贝之后,你可以使用传统的<CTRL+V>操作粘贴这些内容到任意其它应用中。

C语言的作用域

任何一种编程语言,都会存在作用域的概念,比如C++中的private、public等,在C语言里面,主要根据变量所在的位置来区分,主要有:

  • 局部变量
  • 全局变量

局部变量

所谓的局部变量就是只能被局部访问,一般定义在函数内部或者某个块,在函数的外部是不可知且不能被访问的,比如下面的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*beginner/scope/scope1.c*/
#include <stdio.h>

int main()
{
int a = 1;
int b = 3;
int c = 5;

printf("a is %d\n", a);
printf("b is %d\n", b);
printf("c is %d\n", c);

return 0;
}

其中的变量a、b、c,也就是1、3、5就是main函数的局部变量。

全局变量

全局变量按照字母意思就是在整个程序的声明周期都存在,也就是说所有的函数都可以访问到,一般而言,都会位于函数的外面,通常在程序的顶部,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*beginner/scope/scope2.c*/
#include <stdio.h>

int g = 7;

int main()
{
int a = 1;
int b = 3;
int c = 5;

printf("a is %d\n", a);
printf("b is %d\n", b);
printf("c is %d\n", c);
printf("g is %d\n", g);

g = 9;

printf("g is %d\n", g);

return 0;
}

访问顺序

那么问题就来了,万一全部变量和局部变量定义了相同给的名字,该访问使用哪一个呢,这个记住即可,如果两个名字相同,会使用局部变量,临近原则吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*beginner/scope/scope3.c*/
#include <stdio.h>

int g = 7;

int main()
{
int a = 1;
int b = 3;
int c = 5;

printf("a is %d\n", a);
printf("b is %d\n", b);
printf("c is %d\n", c);
printf("g is %d\n", g);

int g = 3;
printf("g is %d\n", g);

return 0;
}

编译

编译也有所不同,方法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#beginner/scope/Makefile
ALL : scope1 scope2 scope3

scope1: scope1.c
gcc -o scope1 scope1.c

scope2: scope2.c
gcc -o scope2 scope2.c

scope3: scope3.c
gcc -o scope3 scope3.c

.PHONY : clean

clean:
rm -f scope1 scope2 scope3

这会产生可执行程序,当程序被执行时,它会产生下列结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ./scope1
a is 1
b is 3
c is 5

$ ./scope2
a is 1
b is 3
c is 5
g is 7
g is 9

$ ./scope3
a is 1
b is 3
c is 5
g is 7
g is 3