目录

  1、静态库和动态库

一、静态库

1)制作静态库

2)使用静态库

3)静态库的概念

4)静态库的特点

二、动态库

1)制作动态库

2)使用动态库

3)动态库的概念

4)动态库的特点

三、优先使用动态库

2、makefile

一.测试代码1(makefile文件)

测试代码2:

3、main函数的参数

一、main函数的参数

二、操作环境变量

1)设置环境变量

2)获取环境变量的值

三、示例

4、gdb的常用命令

一、安装gdb

二、gdb常用命令

三、示例

5、gdb调试core文件

6、gdb调试正在运行中的程序


  1、静态库和动态库

在实际开发中,我们把通用的函数和类分文件编写,称之为库。在其它的程序中,可以使用库中的函数和类。

一般来说,通用的函数和类不提供源代码文件(安全性、商业机密),而是编译成二进制文件。

库的二进制文件有两种:静态库和动态库。

一、静态库

1)制作静态库

g++ -c -o lib库名.a 源代码文件清单

2)使用静态库

不规范的做法:

g++ 选项 源代码文件名清单 静态库文件名

规范的做法:

g++ 选项 源代码文件名清单 -l库名 -L库文件所在的目录名

3)静态库的概念

程序在编译时会把库文件的二进制代码链接到目标程序中这种方式称为静态链接.

如果多个程序中用到了同一静态库中的函数或类,就会存在多份拷贝.

4)静态库的特点

  1. 静态库的链接是在编译时期完成的,执行的时候代码加载速度快.
  2. 目标程序的可执行文件比较大,浪费空间.
  3. 程序的更新和发布不方便,如果某一个静态库更新了,所有使用它的程序都需要重新编译.

二、动态库

1)制作动态库

g++ -fPIC -shared -o lib库名.so 源代码文件清单

2)使用动态库

不规范的做法:

g++ 选项 源代码文件名清单 动态库文件名

规范的做法:

g++ 选项 源代码文件名清单 -l库名 -L库文件所在的目录名

运行可执行程序的时候,需要提前设置

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:要追加的文件所在目录的完整路径

echo $LD_LIBRARY_PATH #查看是否成功追加

环境变量。

3)动态库的概念

程序在编译时不会把库文件的二进制代码链接到目标程序中,而是在运行时候才被载入.

如果多个进程中用到了同一动态库中的函数或类那么在内存中只有一份,避免了空间浪费问题.

4)动态库的特点

  1. 程序在运行的过程中,需要用到动态库的时候才把动态库的二进制代码载入内存。
  2. 可以实现进程之间的代码共享,因此动态库也称为共享库。
  3. 程序升级比较简单,不需要重新编译程序,只需要更新动态库就行了。

三、优先使用动态库

如果动态库和静态库同时存在,编译器将优先使用动态.

2、makefile

在实际开发中,项目的源代码文件比较多,按类型、功能、模块分别存放在不同的目录和文件中,哪些文件需要先编译,那些文件后编译,那些文件需要重新编译,还有更多更复杂的操作。

make是一个强大的实用工具,用于管理项目的编译和链接。make需要一个编译规则文件makefile,可实现自动化编译。

一.测试代码1(makefile文件)

INCLUDEDIR=-I/home/wucz/tools -I/home/wucz/api

LIBDIR=-L/home/wucz/tools -L/home/wucz/api



all:demo01 demo02 demo03



demo01:demo01.cpp

        g++ -o demo01 demo01.cpp $(INCLUDEDIR) $(LIBDIR) -lpublic -lmyapi

        cp demo01 /tmp/.



demo02:demo02.cpp

        g++ -o demo02 demo02.cpp $(INCLUDEDIR) $(LIBDIR) -lpublic -lmyapi



demo03:demo03.cpp

        g++ -o demo03 demo03.cpp $(INCLUDEDIR) $(LIBDIR) -lpublic -lmyapi



clean:

        rm -f demo01 demo02 demo03

测试代码2:

# 指定编译的目标文件是libpublic.a和libpublic.so

all:libpublic.a \

    libpublic.so



# 编译libpublic.a需要依赖public.h和public.cpp

# 如果被依赖文件内容发生了变化,将重新编译libpublic.a

libpublic.a:public.h public.cpp

        g++ -c -o libpublic.a public.cpp



libpublic.so:public.h public.cpp

        g++ -fPIC -shared -o libpublic.so public.cpp



# clean用于清理编译目标文件,仅在make clean才会执行。

clean:

        rm -f libpublic.a libpublic.so

3、main函数的参数

一、main函数的参数

 main函数有三个参数,argcargvenvp,它的标准写法如下:

int main(int argc,char *argv[],char *envp[])

{

    return 0;

}

argc 存放了程序参数的个数,包括程序本身。

argv 字符串的数组,存放了每个参数的值,包括程序本身。

envp 字符串的数组,存放了环境变量,数组的最后一个元素是空。

在程序中,如果不关心main()函数的参数,可以省略不写。

二、操作环境变量

1)设置环境变量

int setenv(const char *name, const char *value, int overwrite);

name 环境变量名。

value 环境变量的值。

overwrite 0-如果环境不存在,增加新的环境变量,如果环境变量已存在,不替换其值;非0-如果环境不存在,增加新的环境变量,如果环境变量已存在,替换其值。

返回值:0-成功;-1-失败(失败的情况极少见)。

注意:此函数设置的环境变量只本进程有效,不会影响shell的环境变量。如果在运行程序时执行了setenv()函数,进程终止后再次运行该程序,上次的设置是无效的。

2)获取环境变量的值

char *getenv(const char *name);

三、示例

#include <iostream>

using namespace std;



int main(int argc,char *argv[],char *envp[])

{

  if (argc!=4)

  {

    cout << "表白神器程序的使用方法:./demo 追求者姓名 被追求者姓名 表白内容\n";

    return -1;

  }



  cout << argv[1] << "开始向" << argv[2] << "表白。\n";

  cout << argv[3] << endl;

  cout << argv[1] << "表白完成。\n";



  return 0;



  cout << "一共有" << argc << "个参数。\n";



  // 显示全部的参数。

  for (int ii=0;ii<argc;ii++)

  {

    cout << "第" << ii << "个参数:" << argv[ii] << endl;

  }

  // 显示全部的环境变量。

  for (int ii=0;envp[ii]!=0;ii++)  // 环境变量数组最后一个元素是0。

  {

    cout << envp[ii] << endl;

  }



  // 设置环境变量AA。

  setenv("AA","aaaa",0);



  // 显示环境变量AA的值。

  cout << "AA=" << getenv("AA") << endl;



  return 0;

}

4、gdb的常用命令

如果程序有问题,不要问别人为什么会这样,而是立即动手调试。

一、安装gdb

sudo apt update
sudo apt install -y gdb

二、gdb常用命令

如果希望程序可调试,编译时需要加-g选项,并且,不能使用-O的优化选项。

gdb 目标程序:

命令

简写

命令说明

set args

设置程序运行的参数。

例如:./demo 张三 西施 我是一只傻傻鸟

设置参数的方法是:

set args 张三 西施 我是一只傻傻鸟

break

b

设置断点,b 20 表示在第20行设置断点,可以设置多个断点。

run

r

开始运行程序, 程序运行到断点的位置会停下来,如果没有遇到断点,程序一直运行下去。

next

n

执行当前行语句,如果该语句为函数调用,不会进入函数内部。 VS的F10

step

s

执行当前行语句,如果该语句为函数调用,则进入函数内部。VS的F11

注意了,如果函数是库函数或第三方提供的函数,用s也是进不去的,因为没有源代码,如果是自定义的函数,只要有源码就可以进去。

print

p

显示变量或表达式的值,如果p后面是表达式,会执行这个表达式。

continue

c

继续运行程序,遇到下一个断点停止,如果没有遇到断点,程序将一直运行。

VS的F5

set var

设置变量的值。

假设程序中定义了两个变量:

int ii;

char name[21];

set var ii=10 把ii的值设置为10;

set var name="西施"。

quit

q

退出gdb。

注意:在gdb中,用上下光标键可以选择执行过的gdb命令。

三、示例

#include <iostream>

using namespace std;



void show(const char *name1,const char *name2,const char *message)

{

  cout << name1 << "开始表白。\n";

  cout << name2 << ":" << message << endl;

}



int main(int argc,char *argv[],char *envp[])

{

  if (argc!=4)

  {

    cout << "表白神器程序的使用方法:./demo 追求者姓名 被追求者姓名 表白内容\n"; return -1;

  }

  cout << "表白前的准备工作一。\n";

  cout << "表白前的准备工作二。\n";

  cout << "表白前的准备工作三。\n";

  cout << "表白前的准备工作四。\n";

  cout << "表白前的准备工作五。\n";



  show(argv[1],argv[2],argv[3]);



  cout << "表白完成。\n";



  for (int ii=0;ii<10;ii++)

  {

    string str="这是第"+to_string(ii)+"个武装直升机";

    cout << str << endl;

  }



  return 0;

}

5、gdb调试core文件

如果程序在运行的过程中发生了内存泄漏,会被内核强行终止,提示“段错误(吐核/核心已转储)”,内存状态将保存在core文件中,便程序员进一步分析。

Linux缺省不会生成core文件,需要修改系统参数。

调试core文件的步骤如下:

1)用ulimit -a查看当前用户的资源限制参数;

2)用ulimit -c unlimited把core file size改为unlimited;(此为临时改法,下次连接时不生效)

3)运行程序,产生core文件;

4)运行gdb 程序名 core文件名;

5)在gdb中,用bt查看函数调用栈。

代码示例:

#include <cstring>

#include <iostream>

using namespace std;

void xc91(const int bh,const string xm)

{

  char *ptr=nullptr;

  *ptr=78;

  //strcpy(ptr,xm.c_str());

}
int main(){
    xc91(69,"库里库里库里库里");
    
    return 0;

}

6、gdb调试正在运行中的程序

gdb 程序名 -p 进程编号

新建一个连接或终端对当前运行的程序调试

被调试的程序会暂停运行,并由调试的那一端控制,调试退出后继续运行.

#进程编号获取
ps -ef |grep 进程名
#展示结果的第二列即为进程ID(编号)

代码示例:

#include <unistd.h>

#include <iostream>

using namespace std;



void xc66(const int bh,const string xm)

{

  for (int ii=0;ii<10000;ii++)

  {

    sleep(1);

    cout << "ii=" << ii << endl;

  }

}
void aa(const int no,const string name)

{

  bb(3,"黑塔");

}



int main()

{

  aa(8,"火花");



  return 0;

}

     

 在gdb中,用bt查看函数调用栈。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐