go学习笔记

go语言手册教程(中文) go是非常年轻的一门语言,它的主要目标是“兼具Python 等动态语言的开发速度和C/C++等编译型语言的性能与安全性” 很多公司,特别是中国的互联网公司,即将或者已经完成了使用 Go 语言改造旧系统的过程。经过 Go 语言重构的系统能使用更少的硬件资源获得更高的并发和I/O吞吐表现。充分挖掘硬件设备的潜力也满足当前精细化运营的市场大环境。 立即下载

1. 变量

声明单个变量

func main()  {
   
   
    // 1. 指定变量类型,若不赋值,则默认值。(int => 0)
    var a int
    a = 10
    fmt.Println("a =",a)
	// var a int = 10   声明变量并初始化
    
    // 2. 类型推导,系统自动推导变量类型
    var b = 10
    fmt.Println("b =",b)

    // 3. 省略var, 用 : 替代
    c := 10
    fmt.Println("c =",c)

    var n1 float32 = -123.0000901
    var n2 float64 = -123.0000901
    fmt.Println("n1=",n1,"n2=",n2)
}

声明多个变量

func main()  {
   
   
    var width, height int = 100, 50
    fmt.Println("width = ", width, "height = ", height)

    // 在一个语句中声明不同类型的变量
    var (
        name = "golang"
        age = 10
        sex = "man"
    )
    fmt.Println(name, age, sex)

    // 简短声明
    width, h := 500, 100   //错误,要求 := 操作符的左边至少有一个变量是尚未声明的
    fmt.Println(width, h)

    // 变量也可以在运行时进行赋值
    a, b := 145.8, 543.8
    c := math.Min(a, b)
    fmt.Println("minimum value is ", c)
}

2. 类型

Go支持的基本类型:

  • bool
  • 数字类型
    • 有符号整型 : int8, int16, int32, int64, int
    • 无符号整型 : uint8, uint16, unint32, unint64, uint
    • 浮点型 : float32, float64
    • 复数 : complex64, complex128
    • int8的别名 : byte
    • int32的别名 : rune
  • string

类型转换

把 v 转换为 T 类型的语法是 T(v)

func main()  {
   
   
    i := 55
    j := 67.8

    //sum = i + j 错误
    sum := i + int(j)

    fmt.Println(sum)  // 122 
}

3. 常量

定义

var a int = 50  
var b string = "I love Go"

在上面的代码中,变量 a 和 b 分别被赋值为常量 50 和 I love GO。

关键字 const 被用于表示常量,比如 50 和 I love Go。

即使在上面的代码中我们没有明确的使用关键字 const,但是在 Go 的内部,它们是常量。

package main

func main() {
   
     
    const a = 55 // 允许
    a = 89       // 不允许重新赋值
}

常量的值会在编译的时候确定。

因为函数调用发生在运行时,所以不能将函数的返回值赋值给常量。

package main

import (  
    "fmt"
    "math"
)

func main() {
   
     
    fmt.Println("Hello, playground")
    var a = math.Sqrt(4)   // 允许
    const b = math.Sqrt(4) // 不允许
}

查看常量类型

func main() {
   
   
    var name = "Sam"
    fmt.Printf("type %T value %v", name, name)
    // type string value Sam
}

4. 函数

函数声明

func functionname(parameter1 type, parameter2 type) returntype {
   
   
    // 函数体
}

[可选] 参数,返回值

例如:计算 商品总价 = 商品单价 * 商品数量

func totalPrice(price int, number int) int {
   
   
    var res = price * number
    return res
}

func main()  {
   
   
    price, number := 2, 3
    ans := totalprice(price, number)
    fmt.Println(ans)
}

如果有连续若干个参数,它们的类型一致,那么我们无须一一罗列,只需在最后一个参数后添加该类型。 例如,price int, no int 可以简写为 price, no int

多个返回值

Go 语言支持一个函数可以有多个返回值。

func rectProps(length, width float64) (float64, float64) {
   
   
    var area = length * width
    var perimeter = (length + width) * 2

    return area, perimeter
}

func main()  {
   
   
    area, perimeter := rectProps(10.8, 5.6)
    fmt.Printf("Area %f Perimeter %f", area, perimeter)
}

命名返回值

从函数中可以返回一个命名值。

一旦命名了返回值,可以认为这些值在函数第一行就被声明为变量了。

上面的 rectProps 函数也可用这个方式写成:

func rectProps(length, width float64)(area, perimeter float64) {
   
     
    area = length * width
    perimeter = (length + width) * 2
    return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}

请注意, 函数中的 return 语句没有显式返回任何值。由于 area 和 perimeter 在函数声明中指定为返回值, 因此当遇到 return 语句时, 它们将自动从函数返回。

空白符

_ 在 Go 中被用作空白符,可以用作表示任何类型的任何值。

我们继续以 rectProps 函数为例,该函数计算的是面积和周长。假使我们只需要计算面积,而并不关心周长的计算结果,该怎么调用这个函数呢?这时,空白符 _ 就上场了。

下面的程序我们只用到了函数 rectProps 的一个返回值 area

func rectProps(length, width float64) (float64, float64) {
   
   
    var area = length * width
    var perimeter = (length + width) * 2

    return area, perimeter
}

func main()  {
   
   
    area, _ := rectProps(10.8, 5.6)
    fmt.Printf("Area %f", area)
}

5. 包

所有可执行的 Go 程序都必须包含一个 main 函数。

这个函数是程序运行的入口。

main 函数应该放置于 main 包中。

创建自定义的包

package rectangle

import "math"

// 计算矩形面积
func Area(len, wid float64) float64 {
   
   
    area := len * wid
    return area
}

// 计算矩形对角线的长度
func Diagonal(len, wid float64) float64 {
   
   
    diagonal := math.Sqrt((len * len) + (wid * wid))  // 平方根
    return diagonal
}

在 Go 中,任何以大写字母开头的变量或者函数都是被导出的名字。

其它包只能访问被导出的函数和变量。

因此,Area 和 Diagonal 函数的首字母大写。

导入自定义包

package main  // 指定该文件属于main包

import "fmt"  // 导入其他包
import "5包/geometry/rectangle"

func main()  {
   
   
    var rectLen, rectWidth float64 = 6, 7
    fmt.Println("几何参数")
    fmt.Printf("area %.2f \n", rectangle.Area(rectLen, rectWidth))
    fmt.Printf("rectangle %.2f", rectangle.Diagonal(rectLen, rectWidth))
}

init 函数

init 函数可用于执行初始化任务,也可用于在开始执行之前验证程序的正确性。

包的初始化顺序如下:

  1. 首先初始化包级别(Package Level)的变量
  2. 紧接着调用 init 函数。包可以有多个 init 函数(在一个文件或分布于多个文件中),它们按照编译器解析它们的顺序进行调用。
package main  // 指定该文件属于main包

// 导入其他包
import (
    "5包/geometry/rectangle"
    "fmt"
    "log"
)

// 包级别变量
var rectLen, rectWidth float64 = 6, 7

// init 函数
func init()  {
   
   
    println("main package initialized")
    if rectLen < 0{
   
   
        log.Fatal("length is less than zero")
    }
    if rectWidth < 0{
   
   
        log.Fatal("width is less than zero")
    }
}


func main()  {
   
   
    fmt.Println("几何参数")
    fmt.Printf("area %.2f \n", rectangle.Area(rectLen, rectWidth))
    fmt.Printf("rectangle %.2f", rectangle.Diagonal(rectLen, rectWidth))
}

main 包的初始化顺序为:

  1. 首先初始化被导入的包。因此,首先初始化了 rectangle 包。
  2. 接着初始化了包级别的变量 rectLen 和 rectWidth。
  3. 调用 init 函数。
  4. 最后调用 main 函数。

空白标识符

导入了包,却不在代码中使用它,这在 Go 中是非法的。

然而,在程序开发的活跃阶段,又常常会先导入包,而暂不使用它。遇到这种情况就可以使用空白标识符 _。

package main

import (  
    "geometry/rectangle"
    _ "fmt"                  // 错误屏蔽器
)

var _ = rectangle.Area      // 错误屏蔽器

func main() {
   
   

}

6. if else

语法

if condition {
   
     
    // 代码
} else if condition {
   
   
    // 代码
} else {
   
                 // }和else 必须在同一行,避免插入分号
    // 代码
}

在 Go 语言规则中,它指定在 } 之后插入一个分号,如果这是该行的最终标记。因此,在if语句后面的 } 会自动插入一个分号。

实际上我们的程序变成了

if num%2 == 0 {
   
     
      fmt.Println("the number is even") 
};  //semicolon inserted by Go
else {
   
     
      fmt.Println("the number is odd")
}

分号插入之后。从上面代码片段可以看出第三行插入了分号。因此会报错。

举例:

package main

import "fmt"

func main() {
   
   
    num := 99
    if num <= 50 {
   
   
        fmt.Println("number is less than or equal to 50")
    } else if num >= 51 && num <= 100 {
   
   
        fmt.Println("number is between 51 and 100")
    } else {
   
   
        fmt.Println("number is greater than 100")
    }
}

7. 循环

for 是 Go 语言唯一的循环语句。

Go 语言中并没有其他语言比如 C 语言中的 while 和 do while 循环。

for 循环语法

for 循环的三部分,初始化语句、条件语句、post 语句都是可选的。

for initialisation; condition; post {
   
     
}

eg.

func main() {
   
     
    for i := 1; i <= 10; i++ {
   
   
        fmt.Printf(" %d",i)
    }
}

or.

func main() {
   
     
    i := 0
    for i <= 10 {
   
    
        fmt.Printf("%d ", i)
        i += 2
    }
}

多个变量

在 for 循环中可以声明和操作多个变量。

func main()  {
   
   
    for no, i := 10, 1; i <= 10 && no <= 19; i, no = i+1, no+1{
   
   
        fmt.Printf("%d * %d = %d \n", no, i ,no*i)
    }
}

break

break 语句用于在完成正常执行之前突然终止 for 循环,之后程序将会在 for 循环下一行代码开始执行。

continue

continue 语句用来跳出 for 循环中当前循环。在 continue 语句后的所有的 for 循环语句都不会在本次循环中执行。循环体会在一下次循环中继续执行。

无限循环

无限循环的语法是:

for {
   
     
}

8. switch

switch是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行相应的代码块。

func main(){
   
   
    num := 3
    switch num {
   
   
    case 1:
        fmt.Println(1)
    case 2:
        fmt.Println(2)
    case 3:
        fmt.Println(3)
    default:
        fmt.Println("5")
    }
}

当匹配不成功时,执行default.

多表达式判断

通过用逗号分隔,可以在一个 case 中包含多个表达式。

func main() {
   
   
    letter := "i"
    switch letter {
   
   
    case "a", "e", "i", "o", "u": // 一个选项多个表达式
        fmt.Println("vowel")
    default:
        fmt.Println("not a vowel")
    }
}

无表达式

在 switch 语句中,表达式是可选的,可以被省略。

如果省略表达式,则表示这个 switch 语句等同于 switch true,并且每个 case 表达式都被认定为有效,相应的代码块也会被执行。

func main() {
   
   
    num := 75
    switch {
   
    // 表达式被省略了
    case num >= 0 && num <= 50:
        fmt.Println("num is greater than 0 and less than 50")
    case num >= 51 && num <= 100:
        fmt.Println("num is greater than 51 and less than 100")
    case num >= 101:
        fmt.Println("num is greater than 100")
    }
}

Fallthrough 语句

在 Go 中,每执行完一个 case 后,会从 switch 语句中跳出来,不再做后续 case 的判断和执行。

使用 fallthrough 语句可以在已经执行完成的 case 之后,把控制权转移到下一个 case 的执行代码中。

func main() {
   
   

    switch num := number(); {
   
    // num is not a constant
    case num < 50:
        fmt.Printf("%d is lesser than 50\n", num)
        fallthrough
    case num < 100:
        fmt.Printf("%d is lesser than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is lesser than 200", num)
    }
}

fallthrough 语句应该是 case 子句的最后一个语句。

9. 数组和切片

数组

数组是同一类型元素的集合。Go 语言中不允许混合不同类型的元素。

如果是 interface{} 类型数组,可以包含任意类型

数组的声明

一个数组的表示形式为 [n]T。n 表示数组中元素的数量,T 代表每个元素的类型。

func main()  {
   
   
    var a [3]int
    fmt.Println(a)     // [0 0 0]
    a[0] = 1
    a[1] = 2
    a[2] = 3
    fmt.Println(a)     // [1 2 3]
}

简略声明:

func main()  {
   
   
    a := [3]int{
   
   1,2,3}
    fmt.Println(a)       // [1 2 3]

    b := [3]int{
   
   1}
    fmt.Println(b)       // [1 0 0]
}

省略数组长度,用 ... 代替:

func main()  {
    a := [...]int{1,2,3}
    fmt.Println(a)       // [1 2 3]
}

数组的大小是类型的一部分。因此 [5]int 和 [25]int 是不同类型。数组不能调整大小。

Go 中的数组是值类型而不是引用类型。这意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的副本。如果对新变量进行更改,则不会影响原始数组。

func main()  {
   
   
    a := [...]int{
   
   1,2,3,4,5}
    b := a
    b[0] = 6
    fmt.Println(a)    // [1 2 3 4 5]
    fmt.Println(b)    // [6 2 3 4 5]
}

同样,当数组作为参数传递给函数时,它们是按值传递,而原始数组保持不变。

func changeLocal(num [5]int) {
   
   
    num[0] = 55
    fmt.Println("inside function ", num)    // [55 6 7 8 8]
}
func main() {
   
   
    num := [...]int{
   
   5, 6, 7, 8, 8}
    fmt.Println("before passing to function ", num)    // [5 6 7 8 8]
    changeLocal(num)
    fmt.Println("after passing to function ", num)    // [5 6 7 8 8]
}
数组的长度

通过将数组作为参数传递给 len() 函数, len(Array)可以得到数组的长度。

range

通过使用 for 循环的 range 方法来遍历数组,返回索引和该索引处的值

func main()  {
   
   
    a := [...]float64{
   
   12.3, 23.4, 34.5, 45.6, 56.7}
    sum := float64(0)
    for i,v := range a{
   
   
        fmt.Printf("数组a中第%d位元素是%.2f \n", i, v)
        sum += v
    }
    fmt.Println("数组a的元素和为:", sum)
}

如果只需要值并希望忽略索引,则可以通过用 _ 空白标识符替换索引来执行。

for _, v := range a {
   
    // ignores index  
}
多维数组
func main() {
   
   
    a := [3][2]string{
   
   
        {
   
   "lion", "tiger"},
        {
   
   "cat", "dog"},
        {
   
   "pigeon", "peacock"},  //逗号必须
    }
}

切片

切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper)。切片本身不拥有任何数据。它们只是对现有数组的引用。

创建一个切片

带有 T 类型元素的切片由 []T 表示。

使用语法 a[start:end] 创建一个从 a 数组索引 start 开始到 end - 1 结束的切片:

func main()  {
   
   
    a := [5]int{
   
   1,2,3,4,5}
    var b []int = a[1:4]
    fmt.Println(b)
}

or

创建一个有 3 个整型元素的数组,并返回一个存储在 c 中的切片引用:

func main() {
   
     
    c := []int{
   
   6, 7, 8} 
    fmt.Println(c)
}
切片的修改

切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。

func main() {
   
   
    darr := [...]int{
   
   57, 89, 90, 82, 100, 78, 67, 69, 59}
    dslice := darr[2:5]
    fmt.Println("array before", darr)   // [57 89 90 82 100 78 67 69 59]  
    for i := range dslice {
   
   
        dslice[i]++
    }
    fmt.Println("array after", darr)    // [57 89 91 83 101 78 67 69 59]
}

当多个切片共享同一个数组时,每个切片所做的修改都会反映在数组中。

切片的长度和容量

切片的长度是切片中的元素数。切片的容量是从创建切片索引开始的底层数组中元素数。

func main() {
   
   
    fruitarray := [...]string{
   
   "apple", "orange", "grape", "mango", "water melon", "pine apple", "chikoo"}
    fruitslice := fruitarray[1:3]
    fmt.Println(len(fruitslice))    // 2
    fmt.Println(cap(fruitslice))    // 6
}
使用 make 创建一个切片

make 函数创建一个数组,并返回引用该数组的切片:

func make([]类型,长度,容量)

容量是可选参数, 默认值为切片长度。

func main() {
   
   
    i := make([]int, 5, 5)
    fmt.Println(i)    // [0 0 0 0 0]
}
追加切片元素

数组的长度是固定的,不能增加。

切片是动态的,使用 append 可以将新元素追加到切片上。append 函数的定义是

func append(s[]T,x ... T)。

如果切片由数组支持,并且数组本身的长度是固定的,那么切片如何具有动态长度。以及内部发生了什么?

当新的元素被添加到切片时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用。现在新切片的容量是旧切片的两倍(不同版本扩容方式不同)。

func main() {
   
   
    cars := []string{
   
   "Ferrari", "Honda", "Ford"}
    fmt.Println("长度是:", len(cars), ", 容量是:", cap(cars))    // 长度是: 3 , 容量是: 3
    cars = append(cars, "Toyota")
    fmt.Println("长度是:", len(cars), ", 容量是:", cap(cars))    // 长度是: 4 , 容量是: 6
}

切片类型的零值为 nil。一个 nil 切片的长度和容量为 0。

也可以使用 ... 运算符将一个切片添加到另一个切片。

func main() {
    veggies := []string{"potatoes", "tomatoes", "brinjal"}
    fruits := []string{"oranges", "apples"}
    food := append(veggies, fruits...)    //将fruits添加到veggies中
    fmt.Println("food:",food)   // food: [potatoes tomatoes brinjal oranges apples]
切片的函数传递

我们可以认为,切片在内部可由一个结构体类型表示。这是它的表现形式,

type slice struct {
   
     
    Length        int
    Capacity      int
    ZerothElement *byte
}

切片包含长度、容量和指向数组第零个元素的指针。

当切片传递给函数时,即使它通过值传递,指针变量也将引用相同的底层数组。

因此,当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见,区别于数组。

func subtactOne(numbers []int) {
   
   
    for i := range numbers {
   
   
        numbers[i] -= 2
    }
}
func main() {
   
   
    nos := []int{
   
   8, 7, 6}
    fmt.Println("slice before function call", nos)  // [8 7 6]
    subtactOne(nos)
    fmt.Println("slice after function call", nos)   // [6 5 4]
}
多维切片

类似于数组,切片可以有多个维度。

func main() {
   
     
     pls := [][]string {
   
   
            {
   
   "C", "C++"},
            {
   
   "JavaScript"},
            {
   
   "Go", "Rust"},
            }
    for _, v1 := range pls {
   
   
        for _, v2 := range v1 {
   
   
            fmt.Printf("%s ", v2)
        }
        fmt.Printf("\n")
    }
}
内存优化

切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。

假设有一个非常大的数组,我们只想处理它的一小部分。由这个数组创建一个切片,并开始处理切片,在切片引用时数组仍然存在内存中。

一种解决方法是使用 copy函数 func copy(dst,src[]T)int 来生成一个切片的副本。这样我们可以使用新的切片,原始数组可以被垃圾回收。

func countries() []string {
   
   
    countries := []string{
   
   "USA", "Singapore", "Germany", "India", "Australia"}
    neededCountries := countries[:len(countries)-2]
    countriesCpy := make([]string, len(neededCountries))
    copy(countriesCpy, neededCountries) // 将 neededCountries 复制到 countriesCpy
    return countriesCpy
}
func main() {
   
   
    countriesNeeded := countries()
    fmt.Println(countriesNeeded)
}

现在 countries 数组可以被垃圾回收, 因为 neededCountries 不再被引用。

10. 可变参数函数

可变参数函数是一种参数个数可变的函数。

语法

如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最后一个参数。

请注意只有函数的最后一个参数才允许是可变的。

拿append函数举例:

func append(slice []Type, elems ...Type)

上面是 append 函数的定义。在定义中 elems 是可变参数。这样 append 函数可以接受可变化的参数。

可变参数函数的工作原理是把可变参数转换为一个新的切片。

func find(num int, nums ...int) {
   
   
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
   
   
        if v == num {
   
   
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
   
   
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {
   
   
    find(89, 89, 90, 95)
    find(45, 56, 67, 45, 90, 109)
    find(78, 38, 56, 98)
    find(87)
}

以上面程序中为例,find 函数中的可变参数是 89,90,95 。 find 函数接受一个 int 类型的可变参数。因此这三个参数被编译器转换为一个 int 类型切片 int []int{89, 90, 95} 然后被传入 find函数。

给可变参数函数传入切片

在切片后加上 ... 后缀,切片将直接传入函数,不再创建新的切片

func find(num int, nums ...int) {
   
   
    fmt.Printf("type of nums is %T\n", nums)
    found := false
    for i, v := range nums {
   
   
        if v == num {
   
   
            fmt.Println(num, "found at index", i, "in", nums)
            found = true
        }
    }
    if !found {
   
   
        fmt.Println(num, "not found in ", nums)
    }
    fmt.Printf("\n")
}
func main() {
   
   
    nums := []int{
   
   89, 90, 95}
    find(89, nums...)
}

11. map

map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值。

创建map

语法: make(map[type of key]type of value)

personSalary := make(map[string]int)

上面的代码创建了一个名为 personSalary 的 map,其中键是 string 类型,值是 int 类型。

map 必须使用 make 函数初始化,未初始化的 map 的值是 nil。

给 map 添加元素

func main() {
   
   
    personSalary := make(map[string]int)
    personSalary["steve"] = 12000
    personSalary["jamie"] = 15000
    personSalary["mike"] = 9000
    fmt.Println(personSalary)	//map[jamie:15000 mike:9000 steve:12000]
}

or:

func main() {
   
   
    personSalary := map[string]int {
   
   
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    fmt.Println(personSalary)   // map[jamie:15000 mike:9000 steve:12000]
}

获取 map 中的元素

获取值

获取 map 元素的语法是 map[key]

func main() {
    personSalary := map[string]int{
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    employee := "jamie"
    fmt.Println(employee, "is", personSalary[employee])    // jamie is 15000
    fmt.Println("joe is", personSalary["joe"])    //joe is 0
}

如果我们获取一个不存在的元素,会返回 int 类型的零值 0

map 中 key 是否存在?

如果想知道 map 中到底是不是存在这个 key,该怎么做 ?

value, ok := map[key]

上面就是获取 map 中某个 key 是否存在的语法。如果 ok 是 true,表示 key 存在,key 对应的值就是 value ,反之表示 key 不存在。

func main() {
   
   
    personSalary := map[string]int{
   
   
        "steve": 12000,
        "jamie": 15000,
    }
    personSalary["mike"] = 9000
    newEmp := "joe"
    value, ok := personSalary[newEmp]
    if ok == true {
   
   
        fmt.Println("Salary of", newEmp, "is", value)
    } else 
【08】C#实战篇——C#调用C++ 动态库dll,报错 “无法加载DLL“xxx.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。” 摘要: 本文详细分析了C#调用C++封装的DLL时出现“无法加载DLL”错误的排查与解决方法。通过案例演示,列举了常见原因(如平台不匹配、路径错误、依赖缺失等),并重点介绍了使用VS的dumpbin工具检测DLL依赖项的方法。最终定位问题为缺失依赖的LineApi.dll和opencv_world455d.dll,将其放入C#的exe目录后解决。文章还对比了C++与C#的报错差异,提供了通用解决方案,包括检查运行时库、环境变量等,帮助开发者高效解决类似问题。 关键词: C#调用C++ DLL、dumpbin 阅读详情

相关推荐

网格自适应_Comsol案例解析17:自适应网格的使用

本期导读自适应网格细化有许多优势,比如可以不用对网格进行全局加密,增加计算精度,又会降低求解自由度。本期仍然以气泡上升案例为例,在两相流模块中使用自适应网格。【打开案例模型】【修改网格】适当让气泡边界加密,注意,曲率因子越小,曲线上的网格越密【添加自适应网格】使用默认的参数即可,这里的误差指示器用的是水平集phi。可以根据具体求解模型选择适当的自变量作为误差指示器。【口诀】默认的一般是最...

weixin_28726871的博客 5680

拼多多/Temu反爬参数逆向踩坑记:从浏览器调试到补环境完整复盘

本文详细解析了拼多多/Temu反爬参数`anti_content`的逆向工程实战,从浏览器调试到补环境完整复盘。通过JavaScript逆向技术,拆解关键函数定位、反混淆算法还原及补环境技术应用,帮助开发者突破电商平台复杂反爬系统,提升数据采集效率。

weixin_33978722的博客 912

AIOps探索 | 如何用大模型构建高效运维知识及智能问答平台(1)

好久没有分享AIOps相关干货了,最近大模型这么火,而且我们有客户已经基于大模型构建了知识及智能问答平台,需要在运维领域结合使用场景进行应用。为此,楼主从头到尾声梳理了一遍大模型在运维领域构建知识及智能问答平台的应用场景、技术实现,总结出来供大家一同探索,希望对大家理解和运用大模型有一定帮助~

智能运维及数据中台探索 2808

基于Spring Boot在线远程考试系统的设计与实现

结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。目前的信息时代,对信息的管理趋于高效化,便捷化,这也是计算机大力普及所带来的便利,此程序软件在设备选用上,依靠的是比较大众的电脑设备,对电脑的配置没有过多要求,一般学校的计算机机房的电脑都可以满足程序开发需求,另外,开发出此款程序,让信息处理变得高效率,其所带来的高效益是远超程序开发的低成本的,因此程序开发的资金投入是可以忽略不计的。

ITbiyesheji的博客 1215

TC3XX - MCAL知识点(一):新建Tc3xx EB-tresos工程

基于EB-tresos建立Hightec或者Tasking工程的时候,第一个步骤就是需要新建一个EB-tresos的工程,本文记录一下如何新建一个EB-tresos工程,以及对其进行简单的操作。那么Tc3xx的MCAL包含哪些内容呢?

梦想技术家 2968

无限泛化!AI背景文字识别,腾讯文字六宫格

本文介绍了生成大规模文字验证码数据集的方法,通过使用上百种字体和2万+字库,结合随机背景和数十种图像增强技术,生成了2000万+的多样化样本。该方法采用二阶段训练策略,确保模型对字体、形态、形变等具有强泛化能力,可适配腾讯、咸鱼等多种平台的文字验证码识别,包括生僻字和复杂形变情况。生成的模型在多种验证码场景下表现优异,但强调该方法仅用于技术研究,严禁非法用途。

码王吴彦祖的博客 562

OpenClaw 自定义 Skill 开发完整指南(最新版)

OpenClaw 中的每个 Skill,本质是一套“能力描述 + 执行逻辑”的组合包,核心特征如下:核心载体:以文件夹为单位,文件夹内必须包含SKILL.md文件(能力描述核心);扩展文件:可选agent.py(Python 执行逻辑)、index.ts(TypeScript 执行逻辑)、(辅助文档/示例资源)等;核心优势:AI 通过阅读 SKILL.md 的自然语言描述理解技能,无需严格遵循代码接口,灵活度极高;

haolove527的专栏 2万+

[Dify] 多步对话流程的实现技巧:构建连贯自然的多轮交互系统

摘要:本文探讨了AI系统中实现多轮对话的核心技术与应用方法。文章首先分析了多步对话的典型场景(如机票预订、财务报告生成)及其价值,提出了Dify平台实现该功能的三大机制:变量系统、条件控制和状态管理。随后详细介绍了顺序型、条件型和确认型三种基本对话模式,并通过"会议预约助手"案例展示了实际应用流程。文章还提供了提示词设计、变量引用等实用建议,并强调多轮对话成功的关键在于清晰的结构设计而非单纯依赖模型能力,最终实现自然流畅的人机交互体验。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 1343

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

上一篇: Nginx
下一篇: goGin框架教程
sumatch
博客等级 码龄8年 251粉丝 59原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值