热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux中gcc的编译、静态库和动态库的制作

欢迎大家关注笔者,你的关注是我持续更博的最大动力Linux中gcc的编译、静态库、动态库文章目录:1gcc的编译过程1.1gcc的编译过程1.2gcc的



欢迎大家关注笔者,你的关注是我持续更博的最大动力





Linux中gcc的编译、静态库、动态库

文章目录:

  • 1 gcc的编译过程
    • 1.1 gcc的编译过程
    • 1.2 gcc的常用参数
  • 2 gcc 静态库的制作
    • 2.1 静态库的制作流程
    • 2.2 静态库的优缺点
  • 3 gcc 动态库 / 共享库 的制作
    • 3.1 动态库 / 共享库的制作流程
    • 3.2 动态库查找不到解决方法
    • 3.3 动态库的优缺点

gcc是文本编译器,就是编译代码的工具,下面介绍gcc编译C语言(.c文件)的流程。


1 gcc的编译过程


1.1 gcc的编译过程

在这里插入图片描述

gcc的编译分为以下四个阶段:


  • gcc预处理器:把.c文件编译成预处理.i文件
  • gcc编译器:把预处理.i文件编译成.s的汇编文件
  • gcc汇编器:把.s汇编文件编译成.o二进制文件
  • gcc链接器:把.o二进制文件链接成一个可执行文件


四个阶段的编译命令:


  • 预处理:gcc -E hello.c -o hello.i
  • 编译:gcc -S hello.i -o hello.s
  • 汇编:gcc -c hello.s -o hello.o
  • 链接:gcc hello.o -o hello

上面的四个过程也可以用一个命令执行,直接生成可执行的文件:

gcc hello.c -o hello
# 或
gcc hello.c # 没有指定输出问价名,默认是生成一个a.out可执行文件。

注意:

1、记忆参数可以用ESc,-o参数是指定输出文件的名字
2、在windows下,如果gcc hello.c,默认生成的可执行文件为a.exe;如果gcc hello.c -o myapp,会直接生成可执行文件myapp.exe,自动添加后缀。
3、在第二阶段把预处理.i文件编译成.s汇编文件浪费时间
4、即使是直接生成可执行文件,但是也是经过了预处理编译汇编链接这些过程,只是没有生成中间的这些文件。



四个阶段的具体功能:


  • 预处理:1)把.c文件中的头文件展开添加到.i预处理文件的开头;2)然后把.c文件代码添加到.i的头文件内容之后;3)把宏定义的量值替换为具体的值,去掉原代码中的注释

  • 编译:把c文件翻译汇编文件,就是两种程序语法的转化。

  • 汇编:把汇编文件编程二进制文件,此时的文件已经看不出具体的内容。

  • 链接:将函数库中相应的代码组合到目标文件中。


1.2 gcc的常用参数

下面具体实例:

一、执行文件和头文件同级目录

1、创建一个sum.c文件,内容如下:

#include
// 双引号导入的头文件是自己写的
#include "head.h"
#define DEBUG// main是入口函数
int main(void)
{int a = NUM1;int aa;int b = NUM2;int sum = a + b;// 这是一个加法运算
#ifdef DEBUGprintf("The sum value is : %d + %d = %d\n", a, b, sum);
#endifreturn 0;
}

2、在sum.c的同级创建head.h头文件,内容如下:

#ifndef __HEAD_H_
#define __HEAD_H_#define NUM1 10
#define NUM2 20
#endif

两个文件的层级结构,同级目录:

├── head.h
├── sum.c

3、预处理:gcc -E sum.c -o sum.i

执行完之后用vi sum.i查看预处理之后sum.i内容,如下:

在这里插入图片描述

从文件中可以看到,文件内容很长,之前的导入的头文件,被替换为具体的头文件代码内容,代码中的宏定义量被替换为具体的值,代码中的注释去掉。(相当于做菜食材的准备阶段)

4、编译:gcc -S sum.i -o sum.s

编译就是把预处理的.i文件编译成.s的汇编语言,编译之后的sum.s内容,如下:

在这里插入图片描述

从文件中可以看出,这个文件显示的已经不是C语言编写的代码,已经被转换为汇编语言的代码,如果你对单片机了解,你可能也对汇编语言的语法有所了解。(编译:就是把C语言翻译成汇编语言)

5、汇编:gcc -c sum.s -o sum.o

汇编就是把汇编文件变成二进制文件,汇编之后的sum.o内容,如下:

在这里插入图片描述

从文件中可以看出,汇编成二进制文件之后,里面的内容已经看不出来了。

6、链接:gcc sum.o -o sum

使用gcc链接器二进制文件链接成一个可执行文件,将函数库中相应的代码组合到目标文件中。通过./sum即可执行该可执行文件,执行结果如下:

在这里插入图片描述

如果你打开可执行文件sum,显示的内容和sum.o差不多。

二、执行文件和头文件同级目录

目录层级结构:

├── include
│ └── head.h
├── sum.c

如果直接编译(gcc sum.c -o sum),会提示找不到头文件,如下:
在这里插入图片描述

找不到头文件有两种解决方法:


  • 直接在程序编写的时候指定头文件的位置
  • 在编译的时候用-I参数,指定头文件所在的文件夹位置

gcc sum.c -I ./include -o sum


三、gcc的其他参数使用

1、参数-D:指定一个宏定义

上面的程序中有printf()打印程序调试的log信息,但是程序发布的时候,我们是不需要这些log信息的,当然我们可以通过加调试的#define DEBUG宏的声明,但是,程序中需要调试输出的log信息比较多的时候,这种方法显然不合适。

现在我们把DEBUG的宏定义注释掉

#include
// 双引号导入的头文件是自己写的
#include "head.h"
//#define DEBUG// main是入口函数
int main(void)
{int a = NUM1;int aa;int b = NUM2;int sum = a + b;// 这是一个加法运算// 程序有 DEBUG宏定义,程序才会执行prinf()
#ifdef DEBUGprintf("The sum value is : %d + %d = %d\n", a, b, sum);
#endifreturn 0;
}

然后再执行:

>>>gcc sum.c -o sum
>>>./sum

结果:


并不会输出print打印的信息了,如果再次打印出信息呢,此时可以通过参数-D,在执行命令的时候给程序指定一个宏,如下:


>>>gcc sum.c -o sum -D DEBUG
>>>./sum

此时就可以打印出printf()信息了。

总结:


-D参数的作用:不在程序中定义宏,在程序编译的时候定义。不指定,在程序预处理的时候,printf()就会被删掉了。


2、-O参数:程序预处理的时候对代码优化

在程序预处理的时候对代码进行优化,把冗余的代码去掉,有三个优化等级:


  • -O1:优化等级低
  • -O2:优化等级中
  • -O3:优化等级高

举个例子:

int a = 10
int b = a
int c = b
int d = c# 优化完之后就是
int d = 10 // 就是对d的一个赋值操作

3、-Wall参数:输出程序中的警告信息

例如我们在程序中定义一个变量int aa;,但是没有使用,此时就会输出警告信息。

4、-g参数:在程序中添加一些调试信息


gcc sum.c -o sum -g



  • -g参数之后,输出的可执行文件会比不加的大(因为包含调试信息)
  • 程序发布是不需要加-g参数
  • 调试需要加-g参数,否则没有调试信息不可以调试。(gdb调试的时候必须加此参数)

总结:
在这里插入图片描述
参数:-E-S,不是很重要,-c比较重要,后面我们在制作静态库和动态库的时候需要用到生成的.o二进制值文件


2 gcc 静态库的制作

比如你和别人做项目合作,你不可能直接把源代码给别人,那样别人就可以自己开发,因为源代码就是你的核心技术。你不应该卖给他源代码,而是应该是程序,这样你就可以根据他有什么需求进行改或添加什么功能模块等,就可以改一次就可以收费一次,这样就可以有一个长期合作。

那应该给到客户的是什么呢?


  • 生成的库
  • 头文件

这样把生成的库头文件给客户也能够使用,只是他不知道里面具体怎么实现的。这样二者才能维持一个长期的合作


头文件对应的.c文件都被打包到了静态库动态库里面了。



2.1 静态库的制作流程

一、静态库的制作

1、命名规则


  • 1)lib + 库的名字 + .a
  • 2)例如:libmytest.a

2、制作步骤:


  • 1)生成对应的.o二进制文件 .c --> .o eg:gcc sum.c -c sum.o
  • 2)将生成的.o文件打包,使用ar rcs + 静态库的名字(libMytest.a) + 生成的所有的.o
  • 3)发布和使用静态库:
    • 发布静态库
    • 头文件

说明:


  • .c文件,也就是源代码转化成.o二进制文件之后,客户就不知道到你的核心技术具体是怎么实现的了。
  • ar是对.o的二进制文件进行打包,rcs是打包参数,把所有.o二进制文件打包成一个.a文件,即:静态库。因此:静态库是一个打包了二进制文件的集合
  • 接口API是在头文件中体现出来的。

实例:

目录结构:

Calc
├── include
│ └── head.h
├── lib
├── main.c
└── src├── add.c├── div.c├── mul.c└── sub.c

说明:


  • include文件夹:存放头文件,提供给用户调用的接口API
  • lib文件夹:存放库文件,即:生成的静态库、动态库
  • src文件夹:存放源文件
  • main.c程序:是用户调用head.h头文件里面的接口,然后在调用静态库里面我们实现的算法(只不过已经不是源码,而是被编译成二进制文件)

下面开始吧:

源代码 src/add.c实现的是加法运算:

#include "head.h"int add(int a, int b)
{int result = a + b;return result;
}

头文件 include/head.h实现是对源代码调用的接口API:

#ifndef __HEAD_H_
#define __HEAD_H_
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
#endif

main.c是对头文件调用,然后调用静态文件,对算法的使用,但是并不知道算法的具体实现源代码

#include
#include "head.h"int main(void)
{int sum = add(2, 24);printf("sum = %d\n", sum);return 0;
}

用户在main.c中引入头文件#include "head.h",即在./include/head.h,就可以使用./include/head.h中定义的接口int add(int a, int b);,当main.c程序执行到add(int a, int b);接口时,就会到./src文件夹下找静态文件(打包的二进制文件——即:加法算法的具体实现)




下面是具体的制作流程:
在这里插入图片描述

shliang@shliang-vm:~/shliang/gcc_learn/Calc$ tree
.
├── include
│ └── head.h
├── lib
├── main.c
└── src├── add.c├── div.c├── mul.c└── sub.c3 directories, 6 files
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ cd src
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c div.c mul.c sub.c1、源代码生成二进制文件(.o文件)
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ gcc *.c -c -I ../include
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c add.o div.c div.o mul.c mul.o sub.c sub.o2、对生成的二进制文件(.o文件),打包成静态文件(.a文件),并移动到lib目录下
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ar rcs libMyCalc.a *.o
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c add.o div.c div.o libMyCalc.a mul.c mul.o sub.c sub.o
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ mv libMyCalc.a ../lib
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ cd ..
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ ls3、调用include目录下的头文件(即:封装的API接口)
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ gcc main.c lib/libMyCalc.a -I ./include -o sum
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ ls
include lib main.c src sum
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ ./sum
sum = 26
shliang@shliang-vm:~/shliang/gcc_learn/Calc$

主要:


  • 制作好的静态文件要放到lib目录下
  • 调用头文件中的接口API,然后用gcc编译的自己调用的main.c文件,需要加上静态文件(.a文件)
  • 程序发布的时候只需要给用户的文件:
    • 1)include目录下的头文件(head.h):封装的是具体算法实现的接口API
    • 2)lib目录下的静态文件(.a文件):是源代码编译的之后的二进制文件(.o文件),然后被打包成静态文件(.a文件)

用于另外一种调用静态库的方法为:


gcc main.c -Iinclude -L lib -l MyCalc -o myapp


在这里插入图片描述

参数说明:


  • -I参数:指定头文所在的文件夹名,文件夹名可以和参数贴着写在一起
  • -L参数:指定静态库的文件夹名
  • -l参数:指定静态库的名字,但名字要掐头去尾,eg:原静态库名字为libMyCalc.a,在指定-l参数值的时候为:-l MyCalc
  • -o参数:输出编译之后可执行文件的名字

注意:


之所以用-l指定静态库的名字,是因为lib目录下可能有多个静态库文件,但是我们只需要使用其中的某一个,此时可以用这种方法指定相应的静态库文件。


二、静态库相关文件查看

1、nm命令查看静态库


可以使用nm命令查看静态库文件中具体打包了哪些二进制文件.o文件


在这里插入图片描述

2、nm命令查看生成的可执行文件

在这里插入图片描述

T:代表的含义是把add代码会被放到代码区


2.2 静态库的优缺点

1、通过静态库生成可执行文件

在这里插入图片描述


  • 静态库中封装了多个.o文件
  • main.c 中调用静态库中相应可执行文件(二进制文件)中的函数
  • 图中只调用了add.o和sub.o中的函数,因此main.c在生成可执行文件的时候只会把静态文件中的add.osub.o两个文件打包到可执行文件中,静态文件中的其他没有用到的.o文件不会被打包进可执行文件中。
  • 在生成可执行文件的时候也是以.o可执行文件单位打包的,并不会把整个静态文件.a都打包到可执行文件中。

静态库的优点:


  • 1)发布程序的时候,不需要提供对应的库了,因为库已经被打包到了可执行文件中去了。
  • 2)库的加载速度比较快,因为库已经被打包到可执行文件中去了。

静态库的缺点:


  • 1) 库被打包到应用程序(最后生成的可执行文件)中,如果库很多的话就会导致应用程序的体积很大。
  • 2)库发生了改变,需要重新编译程序,如果源代码比较多,可能编译一遍一天就过去了。

3 gcc 动态库 / 共享库 的制作


动态库也叫共享库,在windows中对用.dll文件



3.1 动态库 / 共享库的制作流程

一、动态库相关说明

1、命名规则:


  • 1)lib + 名字 + .so
  • 2)例如:libMyCalc.so

2、制作步骤:


  • 1)生成与位置无关的代码 (生成与位置无关的.o)
  • 2)将.o打包成共享库(动态库)
  • 3)发布和使用共享库:

注意:


  • 静态库生成的.o文件是和位置有关的
  • gcc生成和位置无关的.o文件,需要使用参数-fPIC(常用) 或 -fpic

二、动态库制作相关实例

在了解什么叫生成和位置无关的.o文件,我们来先了解一下虚拟地址空间
在这里插入图片描述


linux上打开一个运行的程序(进程),操作系统就会为其分配一个(针对32位操作系统)0-4G的地址空间(虚拟地址空间),虚拟地址空间不是在内存中,而是来自硬盘的存储空间。


从下到上:

0-3G:是用户区


  • .text 代码段:存放的是代码
  • .data :存放的是已初始化的变量
  • .bss:存放的是未初始化的变量
  • 堆空间:
  • 共享库:动态库的空间,每次程序运行的时候把动态库加载到这个空间
  • 栈空间:我们定义的局部变量都是在栈空间分配的内存
  • 命令行参数
  • 环境变量

在往上 3-4G是内核区


  • 静态库生成与位置有关二进制文件(.o文件)

虚拟地址空间是从0开始的,生成的二进制文件(.o文件)会被放到代码段,即.text代码区。生成的.o代码每次都被放到同一个位置,是因为使用的是绝对地址



  • 动态库生成与位置无关二进制文件(.o文件)

动态库 / 共享库 在程序打包的时候并不会把.o文件打包到可执行文件中,只是做了一个记录,当程序运行之后才去把动态库加载到程序中,也就是加载到上图中的共享库空间,但是每次加载到共享库空间的位置可能不同
还是和上面静态库制作同样的目录结构:




动态库制作实例

Calc
├── include
│ └── head.h
├── lib
├── main.c
└── src├── add.c├── div.c├── mul.c└── sub.c

在这里插入图片描述

shliang@shliang-vm:~/shliang/gcc_learn/Calc$ cd src/
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c div.c mul.c sub.c1、把源码生成和位置无关的二进制文件
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ gcc -fPIC -c *c -I ../include
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c add.o div.c div.o mul.c mul.o sub.c sub.o2、使用gcc把生成的二进制文件(.o文件),打包成动态库(.so文件)
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ gcc -shared -o libMyCalc.so *o -Iinclude
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ ls
add.c add.o div.c div.o libMyCalc.so mul.c mul.o sub.c sub.o3、把生成的动态库文件移动到lib目录下
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ mv libMyCalc.so ../lib
shliang@shliang-vm:~/shliang/gcc_learn/Calc/src$ cd ..
shliang@shliang-vm:~/shliang/gcc_learn/Calc$ ls
include lib main.c src
shliang@shliang-vm:~/shliang/gcc_learn/Calc$

参数说明:


  • -PIC:生成和位置无关的.o文件
  • -shared:共享,就是把.o文件,打包成动态库 / 共享库

上面就已经完成动态库的制作,然后把下面的两个文件发布用户即可调用


  • include/head.h: 头文件,定义接口API
  • lib/libMyCalc.so:动态库,封装了编译之后的源代码二进制文件

用户使用动态库

用户使用动态库和静态库一样有两种方法:


  • 用户使用动态库方法一:

gcc main.c lib/libMyCalc.so -o app -Iinclude


在这里插入图片描述


  • 用户使用动态库方法二:

gcc main.c -Iinclude -L lib -l MyCalc -o myapp


在这里插入图片描述


3.2 动态库查找不到解决方法

我们可以看到,第二种方法,至执行可执行程序的时候,提示找不到动态库,这并不一定是动态库文件不存在,可能是由于链接不到

是不是真的链接不到,我们可以通过一个命令ldd:查看可执行文件执行的时候,依赖的所有共享库/动态库(.so文件)

ldd命令使用:


ldd 可执行文件名


在这里插入图片描述

shliang@shliang-vm:~/shliang/gcc_learn/Calc$ ldd myapp linux-vdso.so.1 => (0x00007fff59d26000) # 后面的数字是库的地址# 提示我们自己的动态库 / 共享库 libMyCalc.so没有找到libMyCalc.so => not found# libc.so.6 是linux下的标准C库 (写C程序都会调用标准C库里的一些函数)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1e27462000)# 动态链接器,动态链接器的本质就是一个动态库/lib64/ld-linux-x86-64.so.2 (0x00007f1e2782c000)
shliang@shliang-vm:~/shliang/gcc_learn/Calc$

在这里插入图片描述

如上图:可执行程序./a.out在执行的时候,调用需要调用动态库libmytest.so,但是实际上这个调用是通过动态链接器的来调用的。动态库就是通过动态链接器--/lib64/ld-linux-x86-64.so.2加载到我们的可执行程序(应用程序)中的。

那么动态链接器是-- /lib64/ld-linux-x86-64.so.2是通过什么规则查找可执行文件在执行时,需要的动态文件的呢?


其实就是通过环境变量


在linux下查看环境变量:


echo $PATH


在这里插入图片描述

当然PATH下并不是存放动态库的路径,这里只是做一个演示,如何查看环境变量。

一、动态库查找不到解决方法一(不推荐——不允许使用)

把自己制作的动态库放到根目录下的系统动态库中,即/lib目录下


sudo cp ./lib/libMyCalc.so /lib


在这里插入图片描述
从上面的结果可以看到,把自己制作的动态库拷贝到系统动态库中之后,动态链接器根据环境变量就可以找到这个动态库,然后正确加载到可执行程序中。

注意:


这种方法一般不会使用的,因为如果你的动态库的名字和系统中某个动态库的名字一样,就可能会导致系统奔溃的!!!这里只是做一个演示,证明动态链接器是根据环境变量去查找要加载的动态库。


二、动态库查找不到解决方法二(临时测试设置)


通过把动态库添加到动态库环境变量中,即:LD_LIBRARY_PATH


在这里插入图片描述

使用export添加环境变量,把当前动态库所在的位置(文件夹位置)添加到LD_LIBRARY_PATH变量中,可执行程序在执行的时候会在默认的动态库之前从LD_LIBARAY_PATH变量中查找有没有所需动态库。

# export
export LD_LIBARAY_PATH=./lib

注意:


但是,这种方法只是临时的,当我们关闭终端,下次再执行程序又会提示找不到动态库。因此,这钟方法一般是再开发动态库的过程中,用于临时的测试


三、动态库查找不到解决方法三(永久设置——不常用)

当前用户家目录(home)下的.bashrc文件中配置LD_LIBRARY_PAHT环境变量。

cd ~
vi .bashrc# 然后再最后一行添加一个环境变量,如果没有就创建(Shift+G跳到最后一行)
# 然后把动态库的绝对路径赋值给该变量
export LD_LIBARAY_PATH=/home/shliang/shliang/gcc_learn/Calc/lib# 保存退出,用source激活配置,如果不激活需要重启终端,因为终端每次重启都会从.bashrc中加载一次配置
source .bashrc

在这里插入图片描述

上面添加完环境变量之后就可以找到动态库了。

在这里插入图片描述

四、动态库查找不到解决方法四(永久设置)

这种方法,相对与前三种复杂一些,一定要掌握,可能以后用作中用到的就是这种。做法如下:

1、需要找到动态连接器配置文件:/etc/ld.so.conf
2、把我们自己制作的动态库目录的绝对路径写到配置文件中
3、更新配置文件:sudo ldconfig -v


  • ld:dynamic library 动态库的缩写
  • -v :是更细配置文件的时候输出更新信息。

修改配置文件的路径位置:/etc/ld.so.conf


/home/shliang/shliang/gcc_learn/Calc/lib添加到/etc/ld.so.conf配置文件中


之后就可以找到动态库了,如下:

在这里插入图片描述


3.3 动态库的优缺点

1、动态库的优点


  • 执行程序的体积小:程序在执行的时候采取加载动态库,并没有和可执行程序打包在一起
  • 动态库更新了,不需要重新编译程序(不是绝对的,前提是函数的接口不变,内容便里没事)

2、动态库的缺点


  • 程序发布的时候,需要把动态库提供给用户
  • 动态库没有被打包到应用程序中,加载速度相对较慢

在这里插入图片描述



在这里插入图片描述



在这里插入图片描述
♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠ ⊕ ♠


推荐阅读
  • 本文介绍了一种基于选择排序思想的高效排序方法——堆排序。通过使用堆数据结构,堆排序能够在每次查找最大元素时显著提高效率。文章详细描述了堆排序的工作原理,并提供了完整的C语言代码实现。 ... [详细]
  • 本文介绍如何利用栈数据结构在C++中判断字符串中的括号是否匹配。通过顺序栈和链栈两种方式实现,并详细解释了算法的核心思想和具体实现步骤。 ... [详细]
  • 探索1000以内的完美数:因数和等于自身
    本文探讨了如何在1000以内找到所有完美数,即一个数的因数(不包括自身)之和等于该数本身。例如,6是一个完美数,因为1 + 2 + 3 = 6。通过编程实现这一过程,可以更好地理解完美数的特性。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • ###问题删除目录时遇到错误提示:rm:cannotremoveusrlocaltmp’:Directorynotempty即使用rm-rf,还是会出现 ... [详细]
  • 本文详细探讨了C语言中指针的概念,特别是指针在变量和数组中的应用。通过实例讲解,帮助读者更好地掌握指针的使用方法。 ... [详细]
  • 本文介绍了几种不同的编程方法来计算从1到n的自然数之和,包括循环、递归、面向对象以及模板元编程等技术。每种方法都有其特点和适用场景。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
  • 在Java中,this是一个引用当前对象的关键字。如何通过this获取并显示其所指向的对象的属性和方法?本文详细解释了this的用法及其背后的原理。 ... [详细]
  • C语言基础入门:7个经典小程序助你快速掌握编程技巧
    本文精选了7个经典的C语言小程序,旨在帮助初学者快速掌握编程基础。通过这些程序的实践,你将更深入地理解C语言的核心概念和语法结构。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
  • 本文探讨了如何使用自增和自减运算符遍历二维数组中的元素。通过实例详细解释了指针与二维数组结合使用的正确方法,并解答了常见的错误用法。 ... [详细]
  • 本文详细介绍了C语言的起源、发展及其标准化过程,涵盖了从早期的BCPL和B语言到现代C语言的演变,并探讨了其在操作系统和跨平台编程中的重要地位。 ... [详细]
  • 编程挑战:2019 Nitacm 校赛 D 题 - 雷顿女士与分队(高级版)
    本文深入解析了2019年Nitacm校赛D题——雷顿女士与分队(高级版),详细介绍了问题背景、解题思路及优化方案。 ... [详细]
  • 哈密顿回路问题旨在寻找一个简单回路,该回路包含图中的每个顶点。本文将介绍如何判断给定的路径是否构成哈密顿回路。 ... [详细]
author-avatar
手机用户2502896757
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有