linux vim配置c,Linux入门学习教程:GNU C及将Vim打造成C/C++的半自动化IDE

C语言在Linux系统中的重要性自然是无与伦比、不可替代,所以我写Linux江湖系列不可能不提C语言。C语言是我的启蒙语言,感谢C语言带领我进入了程序世界。虽然现在不靠它吃饭,但是仍免不了经常和它打交道,特别是在Linux系统下。

Linux系统中普遍使用的是GNU-C,这里有一份Gnu-C语言手册.pdf,下载地址:。

------------------------------------------分割线------------------------------------------

具体下载目录在 /2015年资料/3月/2日/Linux入门学习教程:GNU C及将Vim打造成C&C++的半自动化IDE/

------------------------------------------分割线------------------------------------------

The GNU C Reference Manual的主页在这里:http://www.gnu.org/software/gnu-c-manual/。C语言的内核极其紧凑,该手册总共只有91页,去掉目录、附录和索引后就只有70页。我一般一个多小时就可以将其从头至尾复习一遍。我曾有过将其翻译成中文的想法,后来还是放弃了。翻译这种字斟句酌的事情还是让别人来干吧。我只写写我自己的感悟。

感悟一:C语言标准干不过GNU扩展

最近为了研究X Window的底层协议,开始尝试使用XCB编程。当我打开XCB的头文件的时候,我被大量的__restrict__关键字惊呆了,好在有GNU C语言手册为我答疑解惑。__restrict__又是一个GNU扩展的关键字,后面我会详细讲解该关键字的用途。其实C语言的C99标准中已经引入了restrict关键字,没有前后的下划线,但是在大量的开源代码中,使用最普遍的还是GNU的扩展,而不是C语言标准。

和restrict关键字有相同命运的还有inline、_Complex等,它们都是在C99标准中引入的关键字,但是其实在C99标准出来之前,GNU C中早就有了__inline__、__complex__等扩展关键字。还记得多年前我学习Linux 0.11版的源代码时,看到大量的__inline__曾经疑惑不已,不知道为什么Linus在91年就能用上了如此先进的语言功能,后来才知道,这是GNU的扩展关键字。

C语言的标准有C89和C99,使用GCC的时候甚至要显示指定-std=c99才能全面支持C99标准,所以在开源界,大家还是喜欢首选GNU的扩展关键字。比如__inline__、__complex__和__restrict__。总而言之,C语言标准干不过GNU扩展。

下面来看看__restrict__的真正含义。载过一篇文章《为什么有些语言会比别的快》,其中提到“很长一段时间,相同的两个程序在Fortran和C(或者C++)中运行,Fortran会快一些,因为Fortran的优化做的更好。这是真的,就算C语言和Fortran的编译器用了相同的代码生成器也是一样。这个不同不是因为Fortran的某种特性,事实上恰恰相反,是因为Fortran不具备的特性。”这是因为C语言中的指针给编译器的优化带来了困难,文章中继续说道:“问题就来了。这些指针可以代替任何内存地址。更重要的是,他们可以重叠。输出数组的内存地址也可以同时是输入数组的。甚至可以部分重叠,输出数组可以覆盖一个输入数组的一半。这对编译器优化来说是个大问题,因为之前基于数组的优化不再适用。特别的,添加元素的顺序也成问题,如果输出重叠的数组,计算的结果会变得不确定,取决于输出元素的动作是发生在元素被覆盖之前还是之后。”

有了__restrict__,C语言的该问题将不复存在。用__restrict__修饰一个指针后,①该指针只能在定义的时候被初始化;②不会再有别的指针指向该指针指向的内存,因此编译器可以对算法进行优化。如下代码:

int * __restrict__ p = (int*)malloc(100*sizeof(int));

指针p有__restrict__关键字修饰,所以它只能在定义的时候被初始化,以后不能赋值,而没有__restrict__修饰的指针,可以随时赋值,如下:

int arr[100];int*pArr;

pArr= arr;

指针pArr没有被__restrict__关键字修饰,所以可以将数组的首地址赋值给它。

比如我们定义一个函数对两块数据进行操作,结果放入第3块内存,如下:

void func1(void* p1, void* p2, void* p3, intsize){for(int i=0; i

p3[i]= p1[i] +p2[i];

}

}

很显然,由于编译器没办法判断指针p1、p2、p3指向的内存是否重叠,所以无法进行优化,加上__restrict__关键字后,如下:

void func1(void* __restrict__ p1, void* __restrict__ p2, void* __restrict__ p3, intsize){for(int i=0; i

p3[i]= p1[i] +p2[i];

}

}

相当于明确告诉编译器这几块内存不会重叠,所以编译器就可以放心大胆对程序进行优化。

另一个关键字是_Complex,C99才引入,而且需要包含头文件。其实在GNU C中,早就有__complex__、__real__、__imag__等扩展关键字。如下代码:

1 #include

2 #include

3

4 intmain(){5 __complex__ a = 3 +4i;6 __complex__ b = 5 +6i;7 __complex__ c = a +b;8 __complex__ d = a *b;9 __complex__ e = a /b;10 printf("a + b = %f + %fi\n", __real__ c, __imag__ c);11 printf("a * b = %f + %fi\n", __real__ d, __imag__ d);12 printf("a / b = %f + %fi\n", __real__ e, __imag__ e);13 return 0;14 }

可以看到,在C语言中也可以直接对复数进行计算。数值计算再也不是Fortran的专利。

感悟二:指针和数组还真是不一样

从学C语言开始,老师就教导我们说指针和数组是一样的,它们可以用同样的方式进行操作。而事实上,指针和数组还是有差别的。直到多年后读《C专家编程》,才直到所谓指针和数组一样是一个美丽的错误,只是因为在《The C Programming Language》这本书里,把“作为函数参数时,指针和数组一样”这样一句话前后分开分别印到了两页而已。

比如,指针不保存数据的长度信息,而数组有,如下代码:

1 #include

2 #include

3

4 intmain(){5 int* p = (int*)malloc(100*sizeof(int));6 int arr[100] = {0};7 printf("The size of p: %d\n", sizeof(p));8 printf("The size of arr: %d\n", sizeof(arr));9 return 0;10 }

这段代码的运行结果为:

The size of p: 8The size of arr:400

我们经常可以使用如下的代码片段来获得一个数组中有多少个元素,如下:

int arr[100];

size_t length= sizeof(arr)/sizeof(int);

但是,当使用数组作为函数的参数的时候,数组会退化成指针。如下代码:

1 #include

2 #include

3

4 void test_array(intarr[]){5 printf("The size of arr in function: %d\n", sizeof(arr));6 return;7 }8

9 intmain(){10 int arr[100] = {0};11 printf("The size of arr in main: %d\n", sizeof(arr));12 test_array(arr);13 return 0;14 }

这段代码的运行结果为:

The size of arr in main: 400The size of arr in function:8

感悟三:C语言中的不完全类型(Incomplete Types)

在GNU C中可以定义不完全类型,不完全类型主要有两种,一种是空的结构,一种是空的数组,比如:

structpoint;char name[0];

空的结构不能定义变量,只能使用空结构的指针。空结构可以在后面再将它补充完整,如下:

structpoint{intx,y;

};

空结构在定义链表的时候经常用到,如下:

structlinked_list{struct linked_list*next;intx;/*other elements here perhaps*/}struct linked_list* head;

还有一种不完全类型就是将一个结构的最后一项定义为一个空的数组,这样可以用来表示一个可变长度的结构或数组,演示该技术的代码如下:

1 #include

2 #include

3

4 typedef struct{5 intlength;6 int arr[0];7 } incomplete_type;8

9 intmain(){10 char hello[] = "Hello, world!";11 int length = sizeof(hello) / sizeof(char);12 incomplete_type* p = (incomplete_type*)malloc(sizeof(int) + length*sizeof(char));13 p->length =length;14 for(int i=0; ilength; i++){15 p->arr[i] =hello[i];16 }17 printf("p->length=%d\n", p->length);18 printf("p->arr=%s\n", p->arr);19 }

打造C/C++的IDE

后面的内容展示如何将Vim打造成一个半自动的C/C++ IDE。读过我的Java博客的朋友应该知道,其实我更喜欢用Eclipse。只有在需要写非常简单的程序(比如做习题)的情况下,我才会用Vim。这在我的《打造属于自己的Vim》中有论述。在这篇文章中我展示了怎么使用Vundle管理插件以及怎么怎么阅读帮助文档,同时展示了taglist.vim的简单用法。如果要用Vim来写C/C++程序,还需要做少许扩展。

第一,安装以下几个插件,由于使用Vundle管理插件,所以只需要把插件名写入.vimrc配置文件,然后运行:BundleInstall即可,如下图:

f90271e7c4dc76fafb1cee1da4cdfb89.png

分别介绍一下这几个插件。The-NERD-tree是一个浏览目录和文件的插件,可以使用:help NERD_tree.txt查看它的帮助文档。taglist.vim是浏览符号以及在符号之间跳转的插件,使用:help taglist.txt查看它的帮助文档。a.vim是在源代码文件和头文件之间跳转的插件,不需要帮助文档,它的命令就是:A。c.vim是提供IDE功能的主要插件,它提供的功能有自动注释、反注释、自动插入代码块及自动运行,如果安装了splint,还可以对代码进行静态检查,使用:help csupport.txt查看它的文档。OmniCppComplete是一个提供自动补全功能的插件,使用:help omnicppcomplete.txt查看它的文档。

这些插件中,taglist.vim和OmniCppComplete需要ctags软件的支持,所以需要安装exuberant-ctags软件包,在Fedora 20中,只需要使用yum install ctags即可自动安装。

第二,生成tags数据库,并将其加入到Vim中。

我们写C程序的时候,使用到的文件主要存在于两个地方,一个是我们工作的当前目录,另外一个是/usr/include。所以要到/usr/include目录下使用ctags命令生成tags数据库文件。为了使tags数据库中包含尽可能多的信息(结构、枚举、类、函数、宏定义等等),需要指定ctags的参数,如下:

1dad08dcb26e726b9cbeebaaf1a31d0b.png

然后将该tags文件的路径加入到.vimrc配置文件中,同时设置一个键盘映射,使得按Ctrl+F12时,在工作目录中调用ctags命令。如下配置文件的最后两行:

21debecf0c68ed34d77dc3fe33ebcbc9.png

然后,在使用Vim写C程序的时候,如果输入了.、->这样的元素,则其成员会自动补全。如果输入的是一个字符串(比如函数名),可以按Ctrl-X Ctrl-O调用自动补全,如下图:

7e266187719c91f4ba72d0125db0ecfb.png

不仅会弹出候选窗口,而且在最上面的窗口中会显示函数的完整的签名,及其所在的文件。这对于我们经常记不全函数名、记不清函数签名的人来说,已经是莫大的福音了。

taglist.vim和OmniCppComplete插件提供的功能用起来都只需要一个命令,而c.vim提供的命令就比较多了。而且在c.vim的帮助文档中并没有列出所有功能的命令,有一个办法可以学习这些命令,那就是打开GVim,通过GVim菜单中的C/C++菜单来学习c.vim提供的功能和命令。

相比网上其它的将Vim打造成IDE的文章,我的配置比较简单,基本上只安装了几个插件,而没有做过多的设置。当我需要某个功能的时候,我会使用命令显式地调用它,所以,称它为半自动化IDE吧。

--------------------------------------分割线 --------------------------------------

--------------------------------------分割线 --------------------------------------

0b1331709591d260c1c78e86d0c51c18.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/471806.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode MySQL 1661. 每台机器的进程平均运行时间

文章目录1. 题目2. 解题1. 题目 表: Activity ------------------------- | Column Name | Type | ------------------------- | machine_id | int | | process_id | int | | activity_type | enum | | timestamp | float | --------------…

LeetCode MySQL 1741. 查找每个员工花费的总时间

文章目录1. 题目2. 解题1. 题目 表: Employees ------------------- | Column Name | Type | ------------------- | emp_id | int | | event_day | date | | in_time | int | | out_time | int | -------------------(emp_id, event_day, in_time) 是这个表…

LeetCode MySQL 1777. 每家商店的产品价格(行列转换)

文章目录1. 题目2. 解题1. 题目 表:Products ---------------------- | Column Name | Type | ---------------------- | product_id | int | | store | enum | | price | int | ----------------------(product_id,store) 是这个表的…

记事本linux命令换行符,Windows 10版记事本应用终于支持Linux/Mac换行符 排版不再辣眼睛...

记事本(Notepad)是微软 Windows 操作系统中相当经典的一款工具,其在最新的 Windows 10 操作系统中也得到了保留,命运比被 Photos 和 Paint 3D 取代的画图(MsPaint)程序要好得多。不过最近,Windows10 版记事本应用迎来了一项技能更新&#xff…

LeetCode 1885. Count Pairs in Two Arrays(二分查找)

文章目录1. 题目2. 解题1. 题目 Given two integer arrays nums1 and nums2 of length n, count the pairs of indices (i, j) such that i < j and nums1[i] nums1[j] > nums2[i] nums2[j]. Return the number of pairs satisfying the condition. Example 1: Inpu…

How to Avoid Producing Legacy Code at the Speed of Typing

英语不好翻译很烂。英语好的去看原文。 About the Author I am a software architect/developer/programmer.I have a rather pragmatic approach towards programming, but I have realized that it takes a lot of discipline to be agile. I try to practice good craftsman…

c语言程序做成可执行文件,windows环境下C程序生成可执行文件

windows环境下&#xff0c;编写C程序&#xff0c;生成.exe&#xff0c;用于操作某个文件。包含三部分&#xff1a;搭建环境、程序实现、程序分析。1、搭建程序编写和编译环境在windows下安装Git Bash(下载页面)。安装完成后&#xff0c;可以在windows的任意文件夹下&#xff0c…

LeetCode MySQL 1890. 2020年最后一次登录(year)

文章目录1. 题目2. 解题1. 题目 表: Logins -------------------------- | 列名 | 类型 | -------------------------- | user_id | int | | time_stamp | datetime | --------------------------(user_id, time_stamp) 是这个表的主键。 每一…

LeetCode MySQL 1873. 计算特殊奖金(case when then else end)

文章目录1. 题目2. 解题1. 题目 表: Employees ---------------------- | 列名 | 类型 | ---------------------- | employee_id | int | | name | varchar | | salary | int | ----------------------employee_id 是这个表的主键。 此表的每…

LeetCode 1868. 两个行程编码数组的积(双指针)

文章目录1. 题目2. 解题2.1 模拟超时2.2 优化1. 题目 行程编码&#xff08;Run-length encoding&#xff09;是一种压缩算法&#xff0c;能让一个含有许多段连续重复数字的整数类型数组 nums 以一个&#xff08;通常更小的&#xff09;二维数组 encoded 表示。 每个 encoded[…

LeetCode MySQL 1587. 银行账户概要 II

文章目录1. 题目2. 解题1. 题目 表: Users ----------------------- | Column Name | Type | ----------------------- | account | int | | name | varchar | -----------------------account 是该表的主键. 表中的每一行包含银行里中每一个用户的账号…

LeetCode MySQL 1667. 修复表中的名字

文章目录1. 题目2. 解题1. 题目 表&#xff1a; Users ------------------------- | Column Name | Type | ------------------------- | user_id | int | | name | varchar | -------------------------user_id 是该表的主键。 该表包含用户的 I…

c语言汇编混合编程写一个乘法,求通过C语言实现矩阵的加、减及乘法。要自己写的,不要复制过来...

满意答案eevfikx22013.11.28采纳率&#xff1a;53% 等级&#xff1a;13已帮助&#xff1a;8891人#include using namespace std;int main(){int am3,bm3,an3,bn3;int a[am][an];int b[bm][bn];for(int i0;i{for(int j0;j{a[i][j]i*amj;}}for(int i0;i{for(int j0;j{b[i][j]i…

LeetCode MySQL 1821. 寻找今年具有正收入的客户

文章目录1. 题目2. 解题1. 题目 表&#xff1a;Customers -------------------- | Column Name | Type | -------------------- | customer_id | int | | year | int | | revenue | int | --------------------(customer_id, year) 是这个表的主键。 这个表…

【Head First Java 读书笔记】(一)基本概念

Java的工作方式 你要做的事情就是会编写源代码 Java的程序结构 类存于源文件里面 方法存在类中 语句存于方法中 剖析类 当Java虚拟机启动执行时&#xff0c;它会寻找你在命令列中所指定的类&#xff0c;然后它会锁定像下面这样一个特定的方法: public static void main(String[…

oid 值 内存使用_[技术干货] zabbix监控项原型组合键值

自动发现中监控项原型使用多个值组合成一个新的键值。这里我们以华为RH5885V3的内存为例&#xff1a;我们先walk出要用来作为组合键值的值&#xff0c;我们称之为VALUE。而OID节点后面延伸出来的数值&#xff0c;例如.1、.2、.3这种&#xff0c;我们称之为INDEX。组合键值的关键…

LeetCode MySQL 1853. 转换日期格式(日期格式化)

文章目录1. 题目2. 解题1. 题目 表: Days ------------------- | Column Name | Type | ------------------- | day | date | -------------------day 是这个表的主键。 给定一个Days表&#xff0c;请你编写SQL查询语句&#xff0c;将Days表中的每一个日期转化为&qu…

自定义计算器 android,自定义公式计算app下载

自定义公式计算器是非常强大的一款计算器软件&#xff0c;可以帮助大家计算各种函数&#xff0c;还能够自定义公式进行保存&#xff0c;便于以后的计算&#xff1b;软件包含了科学计算器的所有功能&#xff0c;而且没有广告&#xff0c;非常的方便和强大&#xff0c;喜欢的朋友…

android 行布局选择器,『自定义View实战』—— 银行种类选择器

在工作中难免遇到自定义 View 的相关需求&#xff0c;本身这方面比较薄弱&#xff0c;因此做个记录&#xff0c;也是自己学习和成长的积累。自定义View实战前言年前的最后一个开发需求&#xff0c;将之前H5开卡界面转变成native。意思就是开卡这个需求做成Android原生的界面&am…

LeetCode 1971. Find if Path Exists in Graph(图的遍历)

文章目录1. 题目2. 解题1. 题目 There is a bi-directional graph with n vertices, where each vertex is labeled from 0 to n - 1 (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] [ui, vi] denotes a bi-directi…