C語言之編譯和debug工具

gcc

gcc是GUN項目為C和C++提供的編譯器

入門案例

gcc編譯器最簡單的使用案例:gcc hello.c -o hello,hello.c是源文件,-o參數指定了結果文件的名稱

gcc命令的選項:

  • -v:打印編譯細節
  • -E:僅僅進行預處理,把預處理結果輸出到控制臺
  • -S:編譯源文件
  • -c:編譯并匯編源文件
  • -o:指定輸出文件
  • -I <dir>:指定某個文件夾作為搜索路徑
  • -L:為gcc增加一個搜索鏈接庫的目錄

工作機制

一般高級語言的編譯過程,可以分為預處理、編譯、匯編、鏈接四個階段。

預處理

預處理是編譯過程中的第一步,處理各種預處理命令,包括頭文件的包含(#include)、宏定義的擴展(#define)、條件編譯的選擇(#if #endif)等

打印出預處理的結果:gcc -E hello.c

源代碼:hello.c

#include <stdio.h>#define HELLO_LINUX "hello Linux!\n"
#define HELLO_MAC "hello MAC!\n"
#define HELLO_WINDOWS "hello Windows!\n"int main(int argc, char const *argv[]) {#if defined(__linux__)printf(HELLO_LINUX);#elif defined(__APPLE__) || defined(__MACH__)printf(HELLO_MAC)#elif defined(__WIN32__)printf(HELLO_WINDOWS);#endifint a = 1;int b = 2;#if a > bprintf("111\n");#elseprintf("222\n");#endifreturn 0;
}

打印出的結果:

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<命令行>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<命令行>" 2
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 375 "/usr/include/features.h" 3 4
# 1 "/usr/include/sys/cdefs.h" 1 3 4
# 392 "/usr/include/sys/cdefs.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 393 "/usr/include/sys/cdefs.h" 2 3 4
# 376 "/usr/include/features.h" 2 3 4
# 399 "/usr/include/features.h" 3 4
# 1 "/usr/include/gnu/stubs.h" 1 3 4
# 10 "/usr/include/gnu/stubs.h" 3 4
# 1 "/usr/include/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/gnu/stubs.h" 2 3 4
# 400 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4# 1 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 1 3 4
# 212 "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 34 "/usr/include/stdio.h" 2 3 4# 1 "/usr/include/bits/types.h" 1 3 4
# 27 "/usr/include/bits/types.h" 3 4
# 1 "/usr/include/bits/wordsize.h" 1 3 4
# 28 "/usr/include/bits/types.h" 2 3 4typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;// 省略代碼int main(int argc, char const *argv[]) {printf("hello Linux!\n");int a = 1;int b = 2;printf("222\n");return 0;
}

從預處理的結果中觀察預處理究竟做了什么:

  • 文件包含:#include 頭文件:會把頭文件整個復制到當前源碼文件中
  • 宏展開:#define 宏名 常量:預處理會進行宏替換,把宏名提換為它所代表的常量
  • 條件編譯:#if #endif:判斷應該保留哪個分支,裁剪到多余的分支

編譯

編譯:編譯器進行詞法分析、語法分析,把源代碼翻譯成匯編語言。

打印出gcc編譯器生成的匯編語言:gcc -S hello.c,會在當前目錄下生成hello.s文件

生成的結果:

        .file   "hello.c".section        .rodata
.LC0:.string "hello Linux!"
.LC1:.string "222".text.globl  main.type   main, @function
main:
.LFB0:.cfi_startprocpushq   %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp.cfi_def_cfa_register 6subq    $32, %rspmovl    %edi, -20(%rbp)movq    %rsi, -32(%rbp)movl    $.LC0, %edicall    putsmovl    $1, -4(%rbp)movl    $2, -8(%rbp)movl    $.LC1, %edicall    putsmovl    $0, %eaxleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE0:.size   main, .-main.ident  "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-44)".section        .note.GNU-stack,"",@progbits

匯編

匯編:把匯編代碼翻譯成機器代碼,即目標代碼

查看生成的機器代碼:

  • 第一種方式:gcc - c hello.s,自動生成hello.o文件
  • 第二種方式:as -o hello.o hello.s,-o參數指定生成的目標文件

查看生成的目標文件的文件類型:file hello.o,結果:

hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
ELF文件

鏈接

鏈接:將符號引用和符號定義替換為可執行文件的虛擬內存地址

總結

編譯過程分為四步:預處理、編譯、匯編、鏈接

  • 預處理:處理預處理命令,包括文件包含、宏定義、條件編譯

    • 查看預處理的結果:gcc -E 源碼文件,會把預處理的結果打印到屏幕上
  • 編譯:把預處理的結果編譯為匯編文件

    • 查看編譯的結果:gcc -S 源文件,會在當前目錄下生成.s文件,.s文件中存儲了匯編代碼
  • 匯編:把匯編代碼編譯為機器代碼

    • 查看匯編結果:gcc -c 匯編代碼,會在當前目錄下自動生成.o文件,.o文件中存儲了機器代碼
  • 鏈接:將機器代碼中的符號引用和符號定義替換為可執行文件的虛擬內存地址,最終生成可執行文件

Makefile

簡介

Makefile:一個文本文件,并且文件名就叫"Makefile"。它包含了一系列指令,定義了整個項目的編譯規則。

優點:自動化編譯,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件開發的效率

make命令:解釋Makefile文件的命令

make命令的工作原則:

  • 一個目標文件,只有在它依賴的目標文件被更改后,它才會被重新編譯。如果依賴文件的修改時間比目標文件的創建時間晚,證明依賴文件在目標文件創建后又進行了修改
  • make工具會遍歷所有的依賴文件,并且把它們對應的目標文件進行更新。編譯的命令和這些目標文件及它們對應的依賴文件的關系則全部儲存在 Makefile 中。

入門案例

案例1

不使用Makefile的情況下對一個項目進行編譯,在這里使用到了靜態鏈接庫和動態鏈接庫

項目的源代碼:

main.c:主程序的入口

#include <stdio.h>
#include "add_minus.h"
#include "multi_div.h"int main(int argc, char const *argv[]) {printf("hello Cacu\n");int rst = 0;rst = add(3, 2);printf("3 + 2 = %d\n",rst);rst = minus(3, 2);printf("3 - 2 = %d\n",rst);rst = multi(3, 2);printf("3 * 2 = %d\n",rst);rst = div(3, 2);printf("3 / 2 = %d\n",rst);return 0;
}

add_minus.h:聲明了加法和減法的接口

int add(int, int);
int minus(int, int);

multi_div.h:聲明了乘法和除法的接口

int multi(int, int);
int div(int, int);

add_minus.c:實現了加法和減法的功能

#include "add_minus.h"int add(int x, int y) {return x + y;
}int minus(int x, int y) {return x - y;
}

multi_div.h:實現了乘法和除法的功能

#include "multi_div.h"int multi(int x, int y) {return x * y;
}int div(int x, int y) {return x / y;
}

從源代碼到可執行文件的過程:

  • 編譯源文件,生成目標文件:在這里分別編譯3個".c"文件,生成".o"文件,".o"文件是目標文件,目標文件是二進制文件
    • gcc -c add_minus.c
    • gcc -c multi_div.c
    • gcc -c main.c
  • 鏈接目標文件,生成可執行文件:gcc -o main main.o add_minus.o multi_div.o,選項"-o"指定了目標文件的名稱,這一步會把三個".o"文件鏈接為一個可執行文件
  • 清理:清理掉所有的目標文件:rm *.o

上面的案例演示了將源代碼編譯為可執行文件的基本流程,接下來還是以上面的源代碼,演示靜態鏈接庫和動態鏈接庫

  • 將每個.c文件都編譯為目標文件:gcc -c main.c; gcc -c add_minus.c; gcc -c multi_div.c
  • 將add_minus.c編譯為靜態鏈接庫:ar -r libadd_minus.a add_minus.o,ar命令用于生成靜態鏈接庫,-r 選項指定靜態鏈接庫的位置和名稱,最好使用".o"文件來生成靜態鏈接庫,這樣和平臺是相關的。
  • 將multi_div.c編譯為動態鏈接庫:gcc multi_div.c -shared -o libmulti_div.so,-shared表示生成動態鏈接庫,-o用于指定輸出文件
  • 生成目標文件,同時連接靜態庫和動態庫:gcc -o main main.o -L . -l multi_div -l add_minus,-L. 表示在當前目錄下尋找庫,-l指定庫的名稱,聲明前綴lib和后綴.a、.so需要省略

案例2

最簡單的Makefile文件,還是以案例1的代碼作為源代碼

main: main.c add_minus.c multi_div.cgcc -o main main.c add_minus.c multi_div.c

在控制臺執行make命令,就會生成可執行文件main

語法

基本語法

Makefile文件的基本格式:

目標: 依賴命令   # 注意命令前必須是一個TAB鍵

make命令解析Makefile的工作機制:

  • 檢測目標的依賴是否存在:生成目標前,會檢查目標的依賴文件是否存在,如果不存在,會向下檢索,檢測是否有生成依賴的規則,如果沒有,則報錯
  • 檢測目標是否需要更新:如果目標的依賴有一個被更新, 則目標會被更新

在上面的入門案例2中,可執行文件依賴所有的源文件,如果一個源文件被修改,那么所有的源文件都需要被重新編譯,接下來對案例2進行優化:

main: main.o add_minus.o multi_div.ogcc -o main main.o add_minus.o multi_div.omain.o: main.cgcc -c main.cadd_minus.o: add_minus.cgcc -c add_minus.cmulti_div.o: multi_div.cgcc -c multi_div.c

在當前Makefile文件中,每個源文件都是獨立的,修改一個源文件,其它源文件不需要一同編譯。

變量和模式

Makefile中支持定義變量,使用 “變量=值” 的形式來定義變量,使用 “${變量}” 的形式來引用變量

變量的類型:普通變量、自帶變量、自動變量

普通變量:變量=值,定義完直接使用即可

自帶變量:Makefile提供的內置變量,變量名大寫

  • CC:編譯器名稱,例如:CC=gcc
  • CPPFLAGS: 預處理的選項
  • CFLAGS:編譯器的選項
  • LDFLAGS:鏈接器的選項

自動變量:只能在命令中使用,Makefile根據上下文自動賦值的變量。

在學習自動變量的含義前,首先回顧一下Makefile的基本規則:

目標: 依賴命令   # 注意命令前必須是一個TAB鍵

自動變量是對于上述格式的簡化:

  • $@: 表示目標
  • $<: 表示第一個依賴
  • $^: 表示所有依賴, 組成一個列表, 以空格隔開, 如果這個列表中有重復的項則消除重復項。

模式:%,表示一個或多個字符,目標和依賴必須同時使用 %,表示它們有相同的名稱。比如: main.o:main.c,可以寫為 %.o: %.c

使用變量和模式來優化入門案例:

target=main
object=main.o add_minus.o multi_div.oCC=gcc
CPPFLAGS=-I ./${target}:${object}${CC} -o $@ $^%.o: %.c${CC} -o $@ -c $^ ${CPPFLAGS}

函數

makefile中的函數有很多,在這里學習兩個最常用的。

wildcard:查找指定目錄下的指定類型的文件

  • 案例:src=$(wildcard *.c),找到當前目錄下所有后綴為.c的文件,賦值給src,等價于src=main.c fun1.c fun2.c

patsubst:匹配替換

  • 案例:obj=$(patsubst %.c, %.o, $(src)),把src變量里所有后綴為.c的文件替換成.o,等價于obj=main.o fun1.o fun2.o

使用函數來優化入門案例:

target=mainsrc=${wildcard ./*.c}
object=${patsubst %.c, %.o, ${src}CC=gcc
CPPFLAGS=-I ./${target}:${object}${CC} -o $@ $^%.o: %.c${CC} -o $@ -c $^ ${CPPFLAGS}

makefile清理操作

清理編譯過程中產生的中間文件,make會把makefile里出現的第一個target當作缺省target。其他的除非是生成缺省target需要,不會執行。

Makefile中的clean命令通常不會被執行,需要用戶手動執行make clean命令,來刪除編譯過程中產生的 “.o” 文件

clean命令的案例:

cleanrm -f *.o

實戰案例

案例1:

target=abooksrc=${wildcard ./*.c}                # 查找當前目錄下所有的.c文件
object=${patsubst %.c, %.o, ${src}}  # 將.c替換為.oCC=gcc
CPPFLAGS= -Wall -Wextra -pedantic -std=c99   # 指定編譯時的參數${target}:${object}                 # $@ 目標  $^  全部參數${CC} -o $@ $^%.o: %.c${CC} -o $@ -c $^ ${CPPFLAGS}clean:                # 需要執行 make clean命令,如果刪掉.o文件,make命令會重新執行rm -f *.o

在命令行執行make && make clean命令

GDB

GDB:GNU Debugger,Linux下的調試工具,可以調試C、Java、PHP等語言。

初步使用:

  • 在命令行輸入gdb,即可進入gdb提供的交互式界面
  • 在交互式界面輸入help,即可查看gdb提供的幫助文檔

入門案例

程序的源代碼:gdb3.c

#include <stdio.h>int main(int argc, char const *argv[]) {int a = 0;printf("請輸入一個數字:");scanf("%d", &a);printf("%d的平方:%d\n", a, a * a);return 0;
}

編譯:gcc -g gdb3.c -o gdb3,使用gdb調試程序,在編譯時,必須要加上-g參數。

調試:

  • 進入gdb的交互式界面:gdb
  • 加載程序:file 可執行文件
  • 設置斷點:break gdb3.c:4
  • 查看源代碼:list
  • 運行程序:run,遇到斷點后會自動停下
  • 單步執行:step,遇到用戶的自定義函數,會進入自定義函數。在這里,單步執行時,如果程序卡住,表示用戶需要在控制臺輸入數據
  • 查看變量:print 變量
  • 繼續向下執行:continue,遇到斷點會停下

被調試的程序編譯時必須加入-g參數

使用gdb調試的C程序,在編譯時,必須加上 -g 參數,加上調試信息,-g選項的作用是在可執行文件中加入源代碼的信息,比如可執行文件中第幾條機器指令對應源代碼的第幾行,但并不是把整個源文件嵌入到可執行文件中,所以在調試時必須保證gdb能找到源文件。

判斷文件是否帶有調試信息:`readelf -S 可執行程序 | grep debug

交互式界面下的基本命令

運行程序:

  • run(簡寫r): 運行程序,當遇到斷點后,程序會在斷點處停止運行,等待用戶輸入下一步命令
  • start:啟動程序并立即停止在程序的入口處
  • continue(簡寫c) : 繼續執行,到下一個斷點停止(或運行結束)
  • next(簡寫n) : 單步跟蹤程序,當遇到函數調用時,也不進入此函數體;此命令同step的主要區別是,step遇到用戶自定義的函數,將步進到函數中去運行,而next則直接調用函數,不會進入到函數體內。
  • step (簡寫s):單步調試如果有函數調用,則進入函數;與命令n不同,n是不進入調用的函數的
  • until(簡寫u):可以運行程序直到退出循環體
  • until+行號: 運行至某行,不僅僅用來跳出循環
  • finish: 運行程序,直到當前函數完成返回,并打印函數返回時的堆棧地址和返回值及參數值等信息。
  • call 函數(參數):調用程序中可見的函數,并傳遞“參數”,如:call gdb_test(55)
  • quit(簡寫q) : 退出gdb

設置斷點:

  • break 文件名:n (簡寫b n):在指定文件的第n行處設置斷點
  • b fn1 if a>b:條件斷點設置
  • break func(break縮寫為b):在函數func()的入口處設置斷點,如:break cb_button
  • delete 斷點號n:刪除第n個斷點
  • disable 斷點號n:暫停第n個斷點
  • enable 斷點號n:開啟第n個斷點
  • clear 行號n:清除第n行的斷點
  • info b (info breakpoints) :顯示當前程序的斷點設置情況
  • delete breakpoints:清除所有斷點:

查看源代碼:

  • list :簡記為 l ,其作用就是列出程序的源代碼,默認每次顯示10行。
  • list 行號:將顯示當前文件以“行號”為中心的前后10行代碼,如:list 12
  • list 函數名:將顯示“函數名”所在函數的源代碼,如:list main
  • list :不帶參數,將接著上一次 list 命令的,輸出下邊的內容。

打印表達式

  • print 表達式(簡記p): 其中“表達式”可以是任何當前正在被測試程序的有效表達式,比如當前正在調試C語言的程序,那么“表達式”可以是任何C語言的有效表達式,包括數字,變量甚至是函數調用。
    • print a:將顯示整數 a 的值
    • print ++a:將把 a 中的值加1,并顯示出來
    • print name:將顯示字符串 name 的值
    • print gdb_test(22):將以整數22作為參數調用 gdb_test() 函數
    • print gdb_test(a):將以變量 a 作為參數調用 gdb_test() 函數
  • display 表達式:在單步運行時將非常有用,使用display命令設置一個表達式后,它將在每次單步進行指令后,緊接著輸出被設置的表達式及值。如: display a
  • watch 表達式:設置一個監視點,一旦被監視的“表達式”的值改變,gdb將強行終止正在被調試的程序。如: watch a
  • whatis :查詢變量或函數
  • info function: 查詢函數
  • info locals: 顯示當前堆棧頁的所有變量

查詢運行信息

  • where/bt :當前運行的堆棧列表
  • bt backtrace 顯示當前調用堆棧
  • up/down 改變堆棧顯示的深度
  • set args 參數:指定運行時的參數
  • show args:查看設置好的參數
  • info program: 來查看程序的是否在運行,進程號,被暫停的原因。

使用經驗

在上面的章節中,學習了gdb的基本知識,在這里,結合平時對于gdb的使用,總結一些常用的操作

查看函數調用棧中的棧幀

  • breaktrace命令,簡寫為bt
  • frame命令:簡寫為f,參數是一個數字,代表當前函數調用棧中第幾個棧幀,棧幀由下到上從1開始編碼。f 1,查看當前函數調用棧內main函數的情況
  • where命令:查看當前函數調用棧

當前函數內局部變量的值

info locals

讓程序一直運行到從當前函數返回為止

finish命令

修改變量的值

set var 變量 = 值

監控某個變量的值

  • display命令:監控某個變量的值,程序每運行一行,自動顯示該變量的值
  • undisplay命令:取消監控

啟動程序時向程序中傳遞參數

第一種方式:在命令行中直接傳遞參數:可以在GDB命令行中使用run命令后面跟上程序的參數

gdb ./my_program
(gdb) run arg1 arg2

第二種方式:使用set args命令設置參數

gdb ./my_program
(gdb) set args arg1 arg2
(gdb) run

調試時指定跳到第幾行

jump 行號,需要先在該行設置斷點

debug模式下無法獲取用戶輸入

在使用gdb進行調試時,gdb默認是運行在交互模式(interactive mode)下的,而交互模式會導致gdb無法正確地獲取用戶的輸入。這是因為gdb會將標準輸入重定向到自己的輸入流,而不是終端。

解決這個問題的一種方法是將gdb設置為非交互模式(non-interactive mode),這樣gdb就能夠正確地獲取用戶的輸入。可以在調試程序之前,在gdb中使用以下命令來設置非交互模式:

set pagination off
set non-stop on

這樣設置后,gdb會禁用分頁輸出(pagination)和停止在非關鍵點(non-stop mode),從而允許程序在調試過程中正常運行,而不會暫停等待輸入。

容易混淆的知識點

start和run的區別

start和run是兩個用于啟動程序的命令,它們有一些區別和不同的使用場景

  • start命令:start命令用于啟動程序并立即停止在程序的入口處,然后等待調試器的進一步指令。它可以讓你在程序剛剛開始執行時暫停,以便設置斷點或進行其他調試操作。在使用start命令后,你可以使用continue命令繼續程序的執行,或者使用其他GDB調試命令進行進一步的調試操作。
  • run命令:run命令用于啟動程序并從程序的起始位置開始連續執行。它會一直執行程序,直到遇到斷點、程序結束或其他調試終止條件。如果你不需要在程序剛開始執行時暫停,而是希望直接開始連續執行程序,則可以使用run命令。

常用操作

在程序執行過程中監視某個變量的改變

watch命令

執行完當前方法后返回到上一個方法

finish命令

程序中有fork函數,想要debug子進程,該怎么辦?

在程序開始執行之前,輸入命令 set follow-fork-mode child,表示fork后將進入子進程調試,子進程調試完成,輸入命令 detach 脫離子進程,然后,set follow-fork-mode parent,設置模式為調試父進程。

調試指定線程

info threads:列出當前程序中的線程信息
thread <thread_number>:切換到指定線程

valgrind

用于檢測內存泄漏的工具

安裝:yum install -y valgrind

使用:valgrind --leak-check=full --show-leak-kinds=all -s <程序>,通過valgrind來執行程序,執行完成后,會顯示程序中出現的內存錯誤。-s參數用于打印出檢測到的錯誤

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76813.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76813.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76813.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Altshuller矛盾矩陣查詢:基于python和streamlit

基于python和streamlit實現的Altshuller矛盾矩陣查詢 import streamlit as st import json# 加載數據 st.cache_resource def load_data():with open(parameter.json, encodingutf-8) as f:parameters json.load(f)with open(way.json, encodingutf-8) as f:contradictions …

Maven的下載配置及在Idea中的配置

編寫項目管理中存在的問題 在大型Java項目開發中&#xff0c;依賴管理是一個極其復雜的挑戰。傳統方式下&#xff0c;開發者需要手動下載并引入數十甚至上百個JAR包到項目中&#xff0c;這一過程不僅繁瑣低效&#xff0c;還存在諸多痛點&#xff1a; 依賴傳遞性問題&#xff1a…

來聊聊C++中的vector

一.vector簡介 vector是什么 C 中的 vector 是一種序列容器&#xff0c;它允許你在運行時動態地插入和刪除元素。 vector 是基于數組的數據結構&#xff0c;但它可以自動管理內存&#xff0c;這意味著你不需要手動分配和釋放內存。 與 C 數組相比&#xff0c;vector 具有更多的…

WVP-GB28181攝像頭管理平臺存在弱口令

免責聲明&#xff1a;本號提供的網絡安全信息僅供參考&#xff0c;不構成專業建議。作者不對任何由于使用本文信息而導致的直接或間接損害承擔責任。如涉及侵權&#xff0c;請及時與我聯系&#xff0c;我將盡快處理并刪除相關內容。 漏洞描述 攻擊者可利用漏洞獲取當前系統管…

訊飛語音聽寫(流式版)開發指南

語音交互大模型的功能越來越受到重視。訊飛語音聽寫&#xff08;流式版&#xff09;為開發者提供了一種高效、準確的語音識別解決方案。本文將基于 Home.vue、iat_xfyun.js 和 sparkChat.js 這三個文檔&#xff0c;詳細闡述訊飛語音聽寫&#xff08;流式版&#xff09;的開發邏…

基于kotlin native的C與kotlin互相調用

本文測試環境為ubuntu&#xff0c;沒有使用IDE&#xff1b;從基本層面了解kotlin native環境中&#xff0c;C和kotlin的編譯&#xff0c;互相調用。 1. kotlin 動態庫 1.1 動態庫編譯 源碼文件libktest.kt&#xff1a; //file name:libktest.kt OptIn(kotlin.experimental.…

【教學類-102-02】自制剪紙圖案(留白邊、沿線剪)02——Python+PS自動化添加虛線邊框

背景需求: 01版本實現了對透明背景png圖案邊界線的擴展,黑線實線描邊 【教學類-102-01】自制剪紙圖案(留白邊、沿線剪)01-CSDN博客文章瀏覽閱讀974次,點贊15次,收藏7次。【教學類-102-01】自制剪紙圖案(留白邊、沿線剪)01https://blog.csdn.net/reasonsummer/article…

Python-函數參數

1. 參數基礎 函數參數是向函數傳遞數據的主要方式&#xff0c;Python 提供了多種參數傳遞機制。 基本用法 def greet(name): # name 是形式參數print(f"Hello, {name}!")greet("Alice") # "Alice" 是實際參數使用場景&#xff1a;當函數需要…

《在 Ubuntu 22.04 上安裝 CUDA 11.8 和 Anaconda,并配置環境變量》

安裝 CUDA 11.8 和 Anaconda 并配置環境變量 在本教程中&#xff0c;我們將介紹如何在 Ubuntu 22.04 上安裝 CUDA 11.8 和 Anaconda&#xff0c;并配置相應的環境變量。我們還將配置使用 阿里云鏡像源 來加速軟件包更新。以下是具體步驟。 步驟 1&#xff1a;更新軟件源 首先…

Ubuntu環境基于Ollama部署DeepSeek+Open-Webui實現本地部署大模型-無腦部署

Ollama介紹 Ollama是一款簡單好用的模型部署工具,不僅可以部署DeepSeek,市面上開源模型大部分都可以一鍵部署,這里以DeepSeek為例 官網 DeepSeek 版本硬件要求 安裝Ollama 環境 sudo apt update sudo apt install curl sudo apt install lsof1.命令一鍵安裝 在官網點擊…

Angular 項目 PDF 批注插件庫在線版 API 示例教程

本文章介紹 Angular 項目中 PDF 批注插件庫 ElasticPDF 在線版 API 示例教程&#xff0c;API 包含 ① 導出批注后PDF數據&#xff1b;② 導出純批注 json 數據&#xff1b;③ 加載舊批注&#xff1b;④ 切換文檔&#xff1b;⑤ 切換用戶&#xff1b;⑥ 清空批注 等數據處理功能…

Spring Boot 中利用 Jasypt 實現數據庫字段的透明加密解密

1. 引言 1.1 什么是 Jasypt Jasypt(Java Simplified Encryption)是一個用于簡化 Java 應用程序中加密操作的庫。 1.2 為什么使用 Jasypt 簡化加密操作:提供簡單的 API 進行加密和解密。透明加密:自動處理加密和解密過程,無需手動干預。多種加密算法:支持多種加密算法,…

Linux的: /proc/sys/net/ipv6/conf/ 筆記250405

Linux的: /proc/sys/net/ipv6/conf/ /proc/sys/net/ipv6/conf/ 是 Linux 系統中用于 動態配置 IPv6 網絡接口參數 的核心目錄。它允許針對不同網絡接口&#xff08;如 eth0、wlan0&#xff09;或全局設置&#xff08;all&#xff09;調整 IPv6 協議棧的行為。 它通過虛擬文件系…

Spring Cloud 框架為什么能處理高并發

Spring Cloud框架能夠有效處理高并發場景&#xff0c;核心在于其微服務架構設計及多組件的協同作用&#xff0c;具體機制如下&#xff1a; 一、分布式架構設計支撐高擴展性 服務拆分與集群部署 Spring Cloud通過微服務拆分將單體系統解耦為獨立子服務&#xff0c;每個服務可獨…

無人機智慧路燈桿:智慧城市的‘全能助手’

在城市發展的進程中&#xff0c;智慧路燈桿作為智慧城市建設的關鍵載體&#xff0c;正逐步從傳統的照明設備轉型為集多種功能于一體的智能基礎設施。無人機與智慧路燈桿的創新性融合&#xff0c;為城市管理和服務帶來了全新的變革與機遇。 一、無人機智慧路燈桿的功能概述 照…

Libevent UDP開發指南

UDP 與 TCP 的核心區別 無連接:不需要建立/維護連接 不可靠:不保證數據包順序和到達 高效:頭部開銷小,沒有連接管理負擔 支持廣播/多播:可以向多個目標同時發送數據 一、基礎UDP服務器實現 1. 創建 UDP 套接字 #include <event2/event.h> #include <event2/lis…

基于阿里云可觀測產品構建企業級告警體系的通用路徑與最佳實踐

前言 1.1 日常生活中的告警 任何連續穩定運行的生產系統都離不開有效的監控與報警機制。通過監控&#xff0c;我們可以實時掌握系統和業務的運行狀態&#xff1b;而報警則幫助我們及時發現并響應監控指標及業務中的異常情況。 在日常生活中&#xff0c;我們也經常遇到各種各樣…

智能多媒體處理流水線——基于虎躍辦公API的自動化解決方案

在內容爆炸的時代&#xff0c;多媒體文件處理&#xff08;圖片壓縮、視頻轉碼、音頻降噪&#xff09;已成為內容生產者的日常挑戰。本文將演示如何基于虎躍辦公的多媒體處理API&#xff0c;構建自動化處理流水線&#xff0c;實現&#xff1a; 批量文件智能分類格式自動轉換質量…

01-STM32(介紹、工具準備、新建工程)p1-4

文章目錄 工具準備和介紹硬件設備stm32簡介和arm簡介stm32簡介STM32命名規則STM32選型STM32F103C8T6最小系統板引腳定義STM32啟動配置STM32最小系統電路ARM簡介 軟件安裝注冊器件支持包安裝ST-LINK驅動安裝USB轉串口驅動 新建工程創建stm32工程STM32工程編譯和下載型號分類及縮…

【ABAP】REST/HTTP技術(一)

1、概念 1.1、SAP 如何提供 Http Service 如果要將 SAP 應用程序服務器 &#xff08;application server&#xff09;作為 http 服務提供者&#xff0c;需要定義一個類&#xff0c;這個類必須實現 IF_HTTP_EXTENSION 接口。IF_HTTP_EXTENSION 接口只有一個方法 HANDLE_REQUEST。…