编译
安装 gcc
sudo apt-get install gcc
gcc
gcc -o main.so main.c
# 然后执行 main 文件
./main.so
# (或者)编译+链接
gcc -o main.o main.c -c
gcc -o main main.o
# 编译优化
gcc -o main.so main.c -O3
O0
(默认)不进行任何优化,- 优点:编译消耗低(时间,空间)。debug方便,可以打断点打断,给任意的给变量赋值。
O1
会消耗稍多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。O2
会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。O3
在O2
的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。Os
一般不用。主要是对代码大小的优化。会关闭一些性能优化选项。会打乱程序的结构,无法调试。会打乱执行顺序,依赖内存操作顺序的程序无法正确执行。实际基本不用这个选项。Ofast
一般不用。用一些非国际标准,进一步做非常规的优化。
include
#include <stdio.h> // 表示文件在系统目录下
#include "a.h" // 表示文件在当前目录下
知识
- main函数是执行的起点,有且只能有1个
- printf 是向标准设备输出,未必是向屏幕输出,例如
./main > save.txt
- 建议用
int main
,而不是void main
,后者不支持c++ - 约定
return 0;
代表执行成功,return -1;
代表执行失败 - 预编译:把 include 替换进来,删除注释,等操作。
gcc -o main1.c main.c -E
- 汇编
gcc -o main.s main.c -S
cc
也可以,但gcc
多操作系统,多语言,所以一般用gcc
多文件
// my_lib.h
// 防止重复导入
#ifndef LEARN_C_MY_LIB_H
#define LEARN_C_MY_LIB_H
int my_func1(int x);
#endif //LEARN_C_MY_LIB_H
// my_lib.c
int my_func1(x) {
return x + 1;
}
// main.c
#include <stdio.h>
#include "my_lib.h"
int main() {
printf("%d\n", my_func1(5));
return 0;
}
// 编译
gcc -o main.o main.c my_lib.c
// 执行
./main.o
方式2
适合 C++ 的头文件 my_lib.h
//防止头文件被重复包含
#pragma once
//兼容C++编译器,如果遇到C++编译器,按C编译
#ifdef __cplusplus
extern "C"
{
#endif
// 这里声明你的函数
int my_func1();
#ifdef __cplusplus
}
#endif
实现文件 my_lib.c
int my_func1(){
return 0;
}
引用 my_test.c
#include <stdio.h>
#include "my_lib.h"
int main(void) {
printf("%d", my_func1());
}
如何编译
gcc -o my_test my_test.c my_lib.c
方式3
不用 .h
文件
// my_lib.c
int func1(int x) {
return x + 1;
}
// main.c
// 定义一下
int func1(int x);
int main() {
printf("%d\n", func1(25));
}
// 编译
gcc -o main.o main.c my_lib.c
// 执行
./main.o
引入库文件
编译步骤
- 准备
.c/.cpp
源文件 - 预编译。把代码展开到
#include
,#define
位置。生成.i
文件gcc -E hello.c -o hello.i
- 或者
cpp hello.c > hello.i
- 编译。生成
.s
汇编语言gcc -S hello.i -o hello.s
- 汇编。将汇编代码转变成机器可以执行的指令。生成
.o
文件(object)gcc -c hello.s -o hello.o
- 或者
as hello.s -o hello.o
- 链接。引用别的库。两种类型:
- 静态库。链接后直接注入目标程序(所以文件会更大,但是链接后就不需要再保留被引用的库了)。库文件名字通常为
libxxx.a
。 - 动态库。运行目标程序时加载,库文件通常以
.so
结尾
- 静态库。链接后直接注入目标程序(所以文件会更大,但是链接后就不需要再保留被引用的库了)。库文件名字通常为
- 完成以上步骤后,得到最终的可执行程序
静态库
编译静态库
gcc -c hello.c -o hello.o # .c 文件编译为 .o 文件
ar rcs libhello.a hello.o # 生成静态库文件 libhello.a
关于 ar 命令
ar archivefile objfile
archivefile:archivefile是静态库的名称
objfile: objfile是已.o为扩展名的中间目标文件名,可以多个并列
参数 意义
-r 将objfile文件插入静态库尾或者替换静态库中同名文件
-x 从静态库文件中抽取文件objfile
-t 打印静态库的成员文件列表
-d 从静态库中删除文件objfile
-s 重置静态库文件索引
-v 创建文件冗余信息
-c 创建静态库文件
链接静态库
gcc tst.c -o tst -L . -lhello
解读一下: -L 后面是静态库文件所在的目录,我这里 . 就是指当前目录的意思。也就是库文件就和源文件在同一路径。真正编译的时候,这个路径还是要填绝对路径要好,这个需要注意一下。后面的-l加上库名,这个库名是去掉lib和后面的.a。静态库的链接就是这样的。
动态库
使用下面的命令就可以生成一个动态库文件 libhello.so
gcc -fPIC -shared -o libhello.so hello.c
- -fPIC 是创建与地址无关的编译程序(pic,position independent code),是为了能够在多个应用程序间共享。
- -shared指定生成动态链接库。
调用动态库
gcc tst.c -o tst -L ./ -lhello
同样,-L后面是库文件的路径,最好是用绝对路径。-l加上去掉lib的库名。然后直接执行可执行文件就可以了。
参考文档:https://cloud.tencent.com/developer/article/1656728?from=15425
CMakeLists.txt
安装
- 下载安装cmake:https://cmake.org/download/
- 如果
cmake
找不到: https://blog.csdn.net/songarpore/article/details/101901766export PATH="/Applications/CMake.app/Contents/bin":"$PATH"
- 或者
brew install cmake
- 更新:
brew update
更新 brew 工具,然后brew upgrade cmake
- 更新:
- linux 也可以这样安装:
yum install cmake
或者sudo apt install cmake
使用
cmake --version
cmake .
# 生成了一个 Makefile 文件
make
# 编译。结束后生成了一个可执行文件
make install
# 编译并安装到(CMakeLists.txt指定的)系统目录中
CMakeLists.txt 的内容
# 1. 基本配置
# 指定最小版本
cmake_minimum_required(VERSION 3.20)
# C99 语言
set(CMAKE_C_STANDARD 99)
# 编译优化等级为O3
set(CMAKE_C_FLAGS "-O3")
# 2. 设置项目名称
project(my_project_name C)
# 它会引入两个变量 demo_BINARY_DIR 和 demo_SOURCE_DIR,同时,cmake 自动定义了两个等价的变量 PROJECT_BINARY_DIR 和 PROJECT_SOURCE_DIR。
# 支持 C
project(my_project_name C)
# 支持 C++
project(my_project_name CXX)
# 支持 C 和 C++
project(my_project_name C CXX)
# 3. MESSAGE 输出信息
MESSAGE(STATUS "执行 cmake 。。。")
# STATUS:加入 -- 前缀
MESSAGE(SEND_ERROR "产生错误,跳过")
MESSAGE(FATAL_ERROR "产生错误,终止 cmake")
add_executable:定义如何生成可执行文件
# 定义 SRC_LIST ,可以在后面用
SET(SRC_LIST main.c
DynamicArray/int/DynamicArray.c
DynamicArray/int/DynamicArray.h
)
# 可执行文件名字是 c_algorithm
add_executable(c_algorithm
${SRC_LIST}
)
# 额外:变量的用法:
# ${SRC_LIST} 来使用
# 在 if 语句里面,直接用 SRC_LIST
INSTALL:在执行 make install
时的行为
add_subdirectory(src bin)
# 安装文件
INSTALL(FILES COPYRIGHT.txt README.md
DESTINATION share/doc/cmake/)
#DESTINATION:绝对路径
#相对路径:$(CMAKE_INSTALL_PREFIX)/<路径>
#CMAKE_INSTALL_PREFIX 默认是 /usr/local/
#cmake -D CMAKE_INSTALL_PREFIX=/usr .. 可以在 cmake 时指定CMAKE_INSTALL_PREFIX的值
#因此,以上配置会安装到 /usr/local/share/doc/cmake/ 中
#安装sh文件
INSTALL(PROGRAMS runhello.sh DESTINATION bin)
#会安装到 /usr/local/bin
#安装目录
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
# 用doc,会把目录安装过去
# 用 doc/ ,会把目录中的内容安装过去
# 安装可执行二进制(下面有详细)
# TARGETS
安装:意思是把相关的文件复制到系统目录中
# 构建
cd build
cmake ..
make
# build 到 ./build 文件夹中,可以防止大量临时文件污染当前目录
cmake --build ./build
# 安装
make install
构建可执行项目
- 构建后的文件自动放入
build/bin/
doc/*
和README.md
和COPYRIGHT
放入/usr/share/doc/cmake/
目录结构
├─src # 这个文件夹中放入源代码
│ ├─main.c
│ └─CMakeLists.txt
├─build # 在这个目录中执行 cmake ..,防止大量临时文件污染
├─doc
│ └─用来放文档.md
├─runhello.sh
├─README.md
├─LICENSE
└─CMakeLists.txt
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "-O3")
project(HELLO C)
add_subdirectory(src bin)
# 1. 把子目录 src 关联过来
# 2. 生成的可执行文件放到 bin 里面
# src/CMakeLists.txt 内容:
# (基本配置之类的同上)
add_executable(hello main.c)
构建
# 我们希望把它构建到 build 文件夹中,防止大量临时文件污染
cd build
cmake ..
# 生成了一个 Makefile 文件
make
# 编译。结束后生成了一个可执行文件
# 执行它
./bin/hello
构建动态库和静态库
动态库和静态库的区别
- 静态库结尾是
.a
,.lib
。动态库结尾是.so
,.dll
,.dylib
- 静态库编译时直接整合到目标里面,因此编译后的目标可以独立运行。
- 动态库编译时只是做个引用标志,编译后的目标还是需要动态库才能运行
目录结构
.
├── CMakeLists.txt
├── README.md
├── lib
│ ├── CMakeLists.txt
│ └── DynamicArray
│ ├── DynamicArray.c
│ └── DynamicArray.h
└── tests
├── CMakeLists.txt
├── main.c
└── tests
├── Test_DynamicArray.c
└── Test_DynamicArray.h
./CMakeLists.txt
这样写:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "-O3")
project(c_algorithm C)
add_subdirectory(lib bin)
./lib/Cmakelists.txt
这样写:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "-O3")
project(c_algorithm C)
include_directories(DynamicArray)
# 把子文件夹包含进来
# 把头文件放到变量 C_ALGO_H 里面,方便之后使用
set(C_ALGO_H
DynamicArray/DynamicArray.h)
# 把代码放到变量 C_ALGO_SRC 里面,方便之后使用
set(C_ALGO_SRC
DynamicArray/DynamicArray.c)
#构建动态库,这里生成 libc_algo.so
ADD_LIBRARY(c_algo SHARED ${C_ALGO_H} ${C_ALGO_SRC})
#c_algo 是 lib 的名字。生成的名字自动加前缀 lib,后缀 .so。
#SHARED 表示共享库。换成 STATIC 就是静态库,如下:
#构建静态库,这里生成 libhello_static.a
ADD_LIBRARY(c_algo_static STATIC ${C_ALGO_H} ${C_ALGO_SRC})
# 名字不能相同,否则报错,因此改为 c_algo_static
# 有方法可以让名字一样,略复杂:https://www.bilibili.com/video/BV1vR4y1u77h?p=8
# 安装头文件
INSTALL(FILES ${C_ALGO_H} DESTINATION include/c_algo)
# 安装动态库二进制
INSTALL(TARGETS c_algo LIBRARY DESTINATION lib)
# 安装静态库二进制
INSTALL(TARGETS c_algo_static ARCHIVE DESTINATION lib)
# 如果没有 INSTALL,也可以在子目录中生成动态文件,但实测不能用
# 如何安装:
# linux下,可以安装到系统目录 /usr,但是 MacBook 系统不允许。
# cmake -D CMAKE_INSTALL_PREFIX=/usr ..
# 否则,DESTINATION 这个变量默认就是 /usr/local,直接 cmake 即可
# cmake之后,执行下面的命令即可安装
make install
另一个项目引用它(other_project/src/CMakeLists.txt里面):
#头文件路径
include_directories(/usr/local/include/hello)
#链接动态库
target_link_libraries(main /usr/local/lib/libhello.dylib)
#或者链接静态库
#target_link_libraries(main /Users/guofei/git/my_lib/build/bin/libhello_static.a)
关于路径
- 取决于你实际 install 的位置
- 你可以指定任意位置,不一定非要 install
ctest
ctest 是 cmake 的一个组件
用法:
- 建立一个 tests 子文件夹(或者其它名字)
- 建立
tests/CMakeLists.txt
,内容为一般的内容,要有add_executable
- 外面的
CMakeLists.txt
是这样的:
add_subdirectory(tests c_algorithm_tests)
# 可执行文件放到了 c_algorithm_tests 文件夹下
enable_testing()
add_test(NAME c_algorithm_tests COMMAND c_algorithm_tests/c_algorithm_tests)
# 这里面的 `NAME c_algorithm_tests` 定义了测试名称
# `COMMAND c_algorithm_tests/c_algorithm_tests` 指定了哪个文件夹中的哪个可执行文件
编译优化
set(CMAKE_C_FLAGS "-O3")
# 类似的:
# CMAKE_C_FLAGS_DEBUG
# CMAKE_CXX_FLAGS
一些变量
# 项目路径
${PROJECT_SOURCE_DIR}
如何传参:
cmake -DSOME_VARIABLE="new value" /path/to/source
# 这个命令有些奇怪,1)传入的参数名为 SOME_VARIABLE,2)如果传参就必须写路径
# CMakeLists.txt 里面这样写:
IF (NOT DEFINED SOME_VARIABLE)
message("没传入这个值")
ELSEIF (SOME_VARIABLE STREQUAL "some value")
message("传入值为: ${SOME_VARIABLE}")
ELSE ()
message("没传入some value,而是 ${SOME_VARIABLE}")
ENDIF ()
判断是否存在一个文件
if (EXISTS /usr/local/lib/libc_algo.dylib OR EXISTS /usr/local/lib/libc_algo.so OR EXISTS /usr/local/lib/libc_algo.dll)
# do something
endif ()
在某个目录中 make
make -C /path/to/directory