zoukankan      html  css  js  c++  java
  • linux编程环境

    Linux编程环境的学习

    一、vim

    vim编辑器基本上可以分为3种模式,分别是命令模式、插入模式和底行模式,下图所示为Vim各种模式相互转换的关系图。

    命令模式:控制屏幕光标的移动,进行文本的删除、复制等文字编辑工作(不使用[Del]键和[Backspace]键)以及进入插入模式,或者回到底行模式。

    插入模式:只有在插入模式下,才可以输入文字。按[Esc]键可回到命令行模式。很多Vim编辑器使用者希望一打开Vim就可以输入内容,但这是不能成功的,因为刚打开Vim编辑器时处于命令模式。

    底行模式:保存文件或退出Vim,同时也可以设置编辑环境和一些编译工作,如列出行号、寻找字符串等。

    下图演示了三种模式之间的切换。

     

    在命令模式下,有如下常用命令:

    1. 插入

    a      //在当前光标位置的右边添加文本

    i       //在当前光标位置的左边添加文本

    A      //在当前行的末尾位置添加文本

    I       //在当前行的开始处添加文本(非空字符的行首)

    O      //在当前行的上面新建一行

    •  //在当前行的下面新建一行
    1. 删除

    x       //删除当前字符

    nx      //删除从光标开始的n个字符

    dw     //删除从光标开始到一个单词(word)的末尾,实际是剪切

    dd      //删除当前行,剪切

    ndd     //向下删除当前行在内的n行,剪切

    1. 拷贝

    yy       //将当前行复制到缓存区。

    nyy      //将当前行向下n行复制到缓冲区。

    yw       //复制从光标开始到词尾的字符。

    nyw      //复制从光标开始的n个单词。

    y^        //复制从光标到行首的内容。

    y$        //复制从光标到行尾的内容。

    1. 粘帖

    p(小写)        //粘贴剪切板里的内容在光标后。

    P(大写)        //粘贴剪切板里的内容在光标前。

    1. 替换

    r      //替换(覆盖)当前字符

    R      //替换(覆盖)当前光标位置及后面的若干文本

    1. 定位

    h,j,k,l       //上,下,左,右也可用键盘上的方向键。

    n+         //向下跳n行

    n-          //向上跳n行

    nG          //跳到行号为n的行

    G           //跳至文件的底部

    gg          // 跳至文件的头部

    ctrl-f        //下翻一页

    ctrl-b       //上翻一页

    ^           //光标移到行首。

    $           //光标移到行尾。

    1. 搜索文本

    /vpser      //向光标下搜索vpser字符串

    ?vpser      //向光标上搜索vpser字符串

    n           //向下搜索

    N           //向上搜索

    1. 替换

    :s/old/new           //用new替换行中首次出现的old

    :s/old/new/g         //用new替换行中所有的old

    :n,m s/old/new/g     //用new替换从n到m行里所有的old

    :%s/old/new/g        //用new替换当前文件里所有的old

    替换的时候注意,加’g’表示涉及的中所有匹配都替换,若不加’g’则替换行中匹配的第一个,%则相当于所有行。

    1. 撤消操作

    u       //撤销上一步操作

    U       //撤销对当前行的所有操作

    1. 合并

    J       //合并光标所在行及下一行为一行(依然在命令模式)

    当然vim还有很多常用的命令,这需要在以后的使用中再去总结。

    二、makefile

    对于单个源文件的项目我们可以在命令行通过g++命令一步一步的执行,比如源文件test.cpp,最终要生成可执行文件test。

    我们在shell中输入如下命令

    $ g++ -c test.cpp   生成test.o

    $ g++ -o test test.o   生成可执行文件test

    如果此时项目有两个源文件test.cpp test2.cpp 那编译就需要多加一行。会使整个过程变复杂的情况有:

    (1)    源文件很多

    (2)    需要很多库文件

    (3)    g++命令需要很多参数

    (4)    文件之间的依赖关系变的复杂

    (5)    需要修改某一个源文件时

    当这些情况发生时,我们再从命令行去敲就会很不方便,makefile就可以根据规则和依赖关系来简化我们的工作。在文件修改时,也只需要敲入make或gmake命令即可。总体上makefile的结构是:

    比如生成test.o的那一步可以写成

    同时,makefile还可以定义伪目标,比如clean就是最常用的目标,它用来清理生成的临时文件以及可执行文件。下面是一个简单完整的makefile文件

    执行 $ make时就可以生成test文件

    执行 $ make clean 就可以清除目标文件和可执行文件

    当然makefile还提供很多便利的语法,比如一些宏替换和函数。

    对于一个工程来说,该makefile很简洁,因为它所有的生成目标文件的命令只需要一行,一个SRCFILE可能相当于很多源文件。CFLAGS替换了一些编译选项,用了wildcard函数和通配符*后,$(wildcard *.cpp) 就等同于当前目录下所有源文件。同样,patsubst就是按格式替换,在第7行中是将SRCFILE宏中所有.cpp替换为.o,这正是我们需要的所有目标文件。12、13行会将目录下的cpp文件挨个编译。当我们需要修改时,只需要修改很少的一部分,比如要给编译选项加入Werror,只需要修改第三行即可。当然一个好的makefile文件还需要恰当的注释,makefile采用’#’符号进行行注释。

    三、g++

    g++是GNU C++编译器。它在执行编译工作需要4步:

    1. 预处理,生成.i的文件[预处理器cpp]
    2. 将预处理后的文件转换成汇编语言,生成文件.s[编译器egcs]
    3. 由汇编变为目标代码(机器代码)生成.o的文件[汇编器as]
    4. 连接目标代码,生成可执行程序[链接器ld]

    在我们的平时使用过程中,前三步都是一步完成。比如我们执行

           $ g++ -c test.cpp

    就可以直接生成了test.o。g++在使用过程中经常需要根据自己的需求加入一些选项,常用的选项和解释如下:

    -g/-ggdb  -g是在编译的时候生成调试信息,只有加入此选项生成可执行文件才能用gdb调试,-ggdb选项是让编译过程尽可能多的生成调试信息。

    -O0/-O1/-O2/-O3 这是编译器的4个优化级别,从左往右优化级别变高。

    -Wall 是warn all的缩写,它会显示所有g++可以告诉我们的警告信息。

    -Werror  将所有的警告变成错误,即该条命令执行失败,和-Wall结合使用。

    -Wunused-parameter 当函数定义的形参在函数中没有用到时,就会产生警告信息。

    -Wformat 该选项用于检查在调用printf和scanf等函数时,格式串是否正确。若不正确则产生警告信息。该选项已经包含在-Wall中了。 例printf("abc\n", i);

    -Wconversion 一些类型转化让一个值发生变化时就产生警告信息,比如浮点数和定点数之间的转化,还有负整数到非负类型的隐式转化(如unsigned int x = -1)。在实际中,感觉加不加该选项都会有同样的警告。

    -Wdeprecated  使用已过时或者弃用的文件产生警告,如在c++程序中采用头文件iostream.h

    -finline-functions  该选项将所有将所有简单函数的代码直接嵌入到调用者的代码中,如果对一个给定函数的所有调用都是采用嵌入代码的方式,并且该函数声明为静态函数,那么该函数将不会输出为汇编代码。该选项只有在-O3优化选项下生效。

    四、gdb

    一般来说,GDB主要帮忙你完成下面四个方面的功能:

    1. 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
    2. 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
    3. 当程序被停住时,可以检查此时你的程序中所发生的事。
    4. 动态的改变你程序的执行环境。

    常用的一些功能及相应命令如下:

    启动gdb: $ gdb  $file test 或者直接 $ gdb test

    退出gdb: $ quit即可

    运行程序: $ run

    查看信息: $ info

           查看断点信息: $ info break

           查看观察点信息: $ info watchpoint

           查看当前源程序: $ info source

           查看堆栈信息: $ info stack

           查看当前参数: $ info args

    列出一段源程序: $ list

           列出某个函数: $ list

           以当前源文件的某行为中间显示一段源程序: $ list LINENUM

           接着前一次继续显示: $ list

           显示前一次之前的源程序: $ list -

           显示另一个文件的一段程序: $ list FILENAME  或$ list FILENAME:LINENUM

    断点操作: $ break

           在函数入口设置断点: $ break

           在当前源文件的某一行设置断点: $ break LINENUM

    在另一个源文件的某一行设置断点: $ break FILENAME:LINENUM

    启用断点: $ enable 2

    禁止断点: $ disable 2

    清除断点: $ delete 2

    观察点操作

    $ watch EXPR

    我们知道断点是当程序执行到某一代码行时中断,而观察点是当程序访问某个存储单元时中断,如果我们不知道某个存储单元是在哪里被改动的,这时候观察点尤其有用。

    $ rwatch EXPR:

           使用EXPR作为表达式设置一个断点,当EXPR被程序读取时,程序被gdb暂停;

    $ awatch EXPR:

           使用EXPR作为表达式设置一个观察点,当EXPR被读出然后被写入时,gdb会暂停程序;这个命令常

    进入下一条语句 next  step可以进入函数

    退出函数, finish

    线程操作

    A、thread THREAD_NO: 该命令用于在线程之间进行切换,把线程号为THREAD_NO(gdb设置的线程号)的线程设置为当前线程;

        B、info threads: 查询当前进程所拥有的所有线程的状态摘要信息;gdb按照顺序显示:

           a、线程号: gdb为被调试进程中的线程设置的顺序号;

           b、目标系统的线程标识;

           3、此线程的当前栈信息;

           一些前面带'*'号的线程,表示该线程是当前线程;

        C、thread apply [THREAD_NO] [ALL] ARGS: 该命令用于向线程提供命令;

    查看变量, print 检查内存值的指令是x,x是examine的意思。用法如下:

    x /NFU ADDR

    其中N代表重复数,F代表输出格式,U代表每个数据单位的大小。U可以取如下值:

    b :字节(byte)

    h :双字节数值

    w :四字节数值

    g :八字节数值

      因此,上面的指令可以这样解释:从ADDR地址开始,以F格式显示N个U数值。例如:

       x/4ub 0x4000

    会以无符号十进制整数格式(u)显示四个字节(b),0x4000,0x4001,0x4002,0x4003。缺省情况下,输出格式依赖于它的数据类型。但你可以改变输出格式。当你使用print命令时,可以用一个参数/F来选择输出的打印格式。F可以是以下的一些值:

      'x' 16进制整数格式

      'd' 有符号十进制整数格式

      'u' 无符号十进制整数格式

      'f' 浮点数格式

    再比如char input[5];

    (gdb) x/7b input

    x命令打印指定存储单元的内容。7b是打印格式,b表示每个字节一组,7表示打印7组,从input数组的第一个字节开始连续打印7个字节。

    切换栈

    bt 查看堆栈信息

    up n 向上n层

    down n  向下n层

    frame  会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。

    用gdb调试core文件

    core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump。 (linux中如果内存越界会收到SIGSEGV信号,然后就会core dump)

    在程序运行的过程中,有的时候我们会遇到Segment fault(段错误)这样的错误。这种看起来比较困难,因为没有任何的栈、trace信息输出。该种类型的错误往往与指针操作相关。往往可以通过这样的方式进行定位。

    造成segment fault,产生core dump的可能原因:

    1、内存访问越界

                  a) 由于使用错误的下标,导致数组访问越界

    b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符

    c) 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。

    2、 多线程程序使用了线程不安全的函数。

    3、 多线程读写的数据未加锁保护。

    对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump

    4、 非法指针

    a) 使用空指针

    b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它 时就很容易因为bus error而core dump.

    5、 堆栈溢出

    不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。

     

    配置操作系统使其产生core文件

    首先通过ulimit命令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a,可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core。可以通过ulimit -c unlimited来打开。若发生了段错误,但没有core dump,是由于系统禁止core文件的生成。

    解决方法:

    $ulimit -c unlimited  (只对当前shell进程有效)

    或在~/.bashrc 的最后加入: ulimit -c unlimited (一劳永逸)

    # ulimit -c

    0

    $ ulimit -a 

    core file size          (blocks, -c) 0

    data seg size           (kbytes, -d) unlimited

    file size               (blocks, -f) unlimited

    用gdb查看core文件

    发生core dump之后, 用gdb进行查看core文件的内容, 以定位文件中引发core dump的行.

    $ gdb [exec file] [core file]

    如:  $ gdb ./test test.core

    然后使用 bt查看调用堆栈。

    五、twiki

    这部分感觉在wiki尝试几次就可以熟悉了。

  • 相关阅读:
    POJ_3176_Cow_Bowling_(数字三角形)_(动态规划)
    POJ_3662_Telephone_Lines_(二分+最短路)
    POJ_2739_Sum_of_Consecutive_Prime_Numbers_(尺取法+素数表)
    POJ_3685_Matrix_(二分,查找第k大的值)
    POJ_3579_Median_(二分,查找第k大的值)
    欧拉回路
    状态压缩dp(hdu2167,poj2411)
    poj1182食物链(种类并查集)
    并查集
    树形动态规划
  • 原文地址:https://www.cnblogs.com/tzhangofseu/p/2586891.html
Copyright © 2011-2022 走看看