位置: 编程技术 - 正文

Linux折腾记(六):感悟GNU C及把Vim打造成C/C++的半自动化IDE(,linux)

编辑:rootadmin

推荐整理分享Linux折腾记(六):感悟GNU C及把Vim打造成C/C++的半自动化IDE(,linux),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:linux !,linux 746,linux iv,linux iv,linux !,linux 746,linuxu,linux !,内容如对您有帮助,希望把文章链接给更多的朋友!

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

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

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

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

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

  有了__restrict__,C语言的该问题将不复存在。用__restrict__修饰一个指针后,①该指针只能在定义的时候被初始化;②不会再有别的指针指向该指针指向的内存,因此编译器可以对算法进行优化。如下代码:复制代码代码如下:int * __restrict__ p = (int*)malloc(*sizeof(int));指针p有__restrict__关键字修饰,所以它只能在定义的时候被初始化,以后不能赋值,而没有__restrict__修饰的指针,可以随时赋值,如下:复制代码代码如下:int arr[];int* pArr;pArr = arr;指针pArr没有被__restrict__关键字修饰,所以可以将数组的首地址赋值给它。

比如我们定义一个函数对两块数据进行操作,结果放入第3块内存,如下:复制代码代码如下:void func1(void* p1, void* p2, void* p3, int size){ for(int i=0; i<size; i++){ p3[i] = p1[i] + p2[i]; }}很显然,由于编译器没办法判断指针p1、p2、p3指向的内存是否重叠,所以无法进行优化,加上__restrict__关键字后,如下:复制代码代码如下:void func1(void* __restrict__ p1, void* __restrict__ p2, void* __restrict__ p3, int size){ for(int i=0; i<size; i++){ p3[i] = p1[i] + p2[i]; }}

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

  另一个关键字是_Complex,C才引入,而且需要包含<complex.h>头文件。其实在GNU C中,早就有__complex__、__real__、__imag__等扩展关键字。如下代码: 复制代码代码如下: #include <stdlib.h>#include <stdio.h></p><p>int main(){ __complex__ a = 3 + 4i; __complex__ b = 5 + 6i; __complex__ c = a + b; __complex__ d = a * b; __complex__ e = a / b; printf("a + b = %f + %fin", __real__ c, __imag__ c); printf("a * b = %f + %fin", __real__ d, __imag__ d); printf("a / b = %f + %fin", __real__ e, __imag__ e); return 0;}

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

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

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

  比如,指针不保存数据的长度信息,而数组有,如下代码:复制代码代码如下:#include <stdlib.h>#include <stdio.h></p><p>int main(){ int* p = (int*)malloc(*sizeof(int)); int arr[] = {0}; printf("The size of p: %dn", sizeof(p)); printf("The size of arr: %dn", sizeof(arr)); return 0;}

这段代码的运行结果为:复制代码代码如下:The size of p: 8The size of arr:   我们经常可以使用如下的代码片段来获得一个数组中有多少个元素,如下:复制代码代码如下:int arr[];size_t length = sizeof(arr)/sizeof(int);  但是,当使用数组作为函数的参数的时候,数组会退化成指针。如下代码:复制代码代码如下:#include <stdlib.h>#include <stdio.h></p><p>void test_array(int arr[]){ printf("The size of arr in function: %dn", sizeof(arr)); return;}</p><p>int main(){ int arr[] = {0}; printf("The size of arr in main: %dn", sizeof(arr)); test_array(arr); return 0;}

这段代码的运行结果为:复制代码代码如下:The size of arr in main: The size of arr in function: 8

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

Linux折腾记(六):感悟GNU C及把Vim打造成C/C++的半自动化IDE(,linux)

  在GNU C中可以定义不完全类型,不完全类型主要有两种,一种是空的结构,一种是空的数组,比如:复制代码代码如下:struct point;char name[0];空的结构不能定义变量,只能使用空结构的指针。空结构可以在后面再将它补充完整,如下:复制代码代码如下:struct point{ int x,y;};空结构在定义链表的时候经常用到,如下:复制代码代码如下:struct linked_list{ struct linked_list* next; int x; /*other elements here perhaps */}struct linked_list* head;

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

复制代码代码如下:#include <stdlib.h>#include <stdio.h></p><p>typedef struct { int length; int arr[0];} incomplete_type;</p><p>int main(){ char hello[] = "Hello, world!"; int length = sizeof(hello) / sizeof(char); incomplete_type* p = (incomplete_type*)malloc(sizeof(int) + length*sizeof(char)); p->length = length; for(int i=0; i<p->length; i++){ p->arr[i] = hello[i]; } printf("p->length=%dn", p->length); printf("p->arr=%sn", p->arr);}

打造C/C++的IDE

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

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

分别介绍一下这几个插件。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 中,只需要使用yum install ctags即可自动安装。

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

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

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

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

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

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

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

Linux折腾记(七):硬盘GPT分区和MBR分区爬坑记 之所以想到写这篇,是因为本人在折腾Linux系统的过程中,有多次掉入硬盘分区的陷阱的经历。最近几天,再一次掉入坑中,折腾了两天才从坑中爬出来

Linux折腾记(八):使用GCC和GNU Binutils编写能在x实模式运行的位代码 不可否认,这次的标题有点长。之所以把标题写得这么详细,主要是为了搜索引擎能够准确地把确实需要了解GCC生成位实模式代码方法的朋友带到我的

Linux折腾记(九):在Ubuntu 中使用搜狗拼音输入法 在UbuntuKylin系统中,默认安装搜狗拼音输入法,但是在原生Ubuntu系统中则不是。这可以理解,毕竟搜狗输入法的Linux版有Kylin团队的不小功劳。由于搜狗

标签: ,linux

本文链接地址:https://www.jiuchutong.com/biancheng/368240.html 转载请保留说明!

上一篇:Linux折腾记(五):在Ubuntu 14.10中使用Windows字体(linux 746)

下一篇:Linux折腾记(七):硬盘GPT分区和MBR分区爬坑记(linux k)

  • 信息化投入包括手机吗
  • 车辆购置税计入固定资产入账价值吗
  • 合伙企业投资收益做账
  • 预提费用在资产负债表里写在哪
  • 联营企业属于企业吗
  • 印花税是根据什么征收的
  • 一般纳税人免征教育费附加的文件
  • 以前年度成本多入了怎样调整
  • 用现金购买办公设备会计分录
  • 融资租赁承租方会计账务处理流程
  • 员工无息借款个人所得税
  • 如何查询增值税申报表
  • 网上申购发票怎么撤销
  • 免租与转租分别什么意思
  • 房地产开发企业资质等级有几个
  • 专用发票过期了还能冲红发票吗
  • 购车保险属于什么费用
  • 主营业务收入会计分录怎样写
  • 移动网速测试软件
  • microsoft阻止应用启动
  • 有限责任公司出资方式有哪些
  • 代开专票计提附加税吗
  • 手把手教你linux
  • 入库前的准备
  • 事业单位专项款不能购买资产吗
  • linux系统中pwd命令的作用
  • 搭建自己的php mvc框架
  • 保险赔款的会计分录
  • 企业所得税会计利润
  • 职工福利费入账依据
  • php获取ipv6地址
  • vue.$el
  • vuecli配置代理
  • js数组方法大全
  • php加密技术
  • input输入框限制最大字数
  • phpcms文档
  • cms访问出错
  • 预交的企业所得税需要计提吗?
  • 股权转让分期付款风险
  • 间接费用会计科目
  • 业务招待费是否计入管理费用
  • 土增尾盘销售应该如何申报收入
  • 资产总额是指营业收入和营业支出吗
  • 职业年金是什么意思?退休后怎么算职业年金?
  • 固定资产为什么要交税
  • 补贴收入属于其他收益吗
  • 预付账款期末可能有余额吗
  • 折扣 会计处理
  • 物业公司支出费用
  • 实际费用以什么为准
  • 有利息费用这个科目吗
  • 购买的商品属于什么会计科目
  • 何为三证合一
  • 汽车贷款利息计算公式计算器
  • sqlserver恢复模式简单和完整
  • sql server中触发器的作用是什么?
  • CentOS7下MySQL5.7安装配置方法图文教程(YUM)
  • win7蓝屏咋办
  • 如何用u盘装系统 知乎
  • win8系统找不到wifi
  • 为什么电脑显示windows10即将终止服务
  • xp安装盘复制u盘
  • win10 edge浏览器设置信任站点
  • w10版本更新
  • win8开始菜单全屏怎么调回去
  • cocos2dx场景切换
  • css vh兼容性
  • fw开发
  • jquery中遍历指定的对象和数组是哪个方法
  • 从最近两会看苹果和谷歌的最大梦魇
  • android拖拽
  • 6种javascript显示当前系统时间代码
  • 工会经费计提比例0.8%
  • 山东税务社保费用缴纳
  • 进口汽车增值税怎么算
  • 公司购买的汽车可以一次进入费用吗
  • 小规模纳税人30万免税政策
  • 国税合并地税
  • 河北省社保证明网上查询
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设