Go学习第八章——面向“对象”编程(结构体与方法)

news2024/11/15 10:51:33

Go面向“对象”编程(结构体与方法)

      • 1 结构体
        • 1.1 快速入门
        • 1.2 内存解析
        • 1.3 创建结构体四种方法
        • 1.4 注意事项和使用细节
      • 2 方法
        • 2.1 方法的声明和调用
        • 2.2 快速入门案例
        • 2.3 调用机制和传参原理
        • 2.4 注意事项和细节
        • 2.5 方法和函数区别
      • 3 工厂模式

Golang语言面向对象编程说明:

  1. Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
  2. Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Gelang是基于struct来实现OOP特性的。
  3. Golang面向对象编程非常简洁,去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等。
  4. Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
  5. Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang中面向接口编程是非常重要的特性。

1 结构体

1.1 快速入门

假设定义结构体是老师,属性:名字,年龄,学校

type Teacher struct {
	//变量名字大写外界可以访问这个属性
	Name   string
	Age    int
	School string
}

下面我们尝试赋值,并打印出来:

type Teacher struct {
	//变量名字大写外界可以访问这个属性
	Name   string
	Age    int
	School string
}

func main() {
	var tea1 Teacher
	tea1.Name = "张三"
	tea1.Age = 28
	tea1.School = "深圳大学"
	fmt.Println("tea1=", tea1)

	fmt.Println("老师的信息如下:")
	fmt.Println("name=", tea1.Name)
	fmt.Println("age=", tea1.Age)
	fmt.Println("school=", tea1.School)
}

输出结果:

tea1= {张三 28 深圳大学}
老师的信息如下:
name= 张三      
age= 28         
school= 深圳大学

通过上面的案例和讲解可以看出:

  1. 结构体是自定义的数据类型,代表一类事务。
  2. 结构体变量(实例)是具体的,实际的,代表一个具体变量。
1.2 内存解析

在Go语言中,结构体的存储是在堆上。

当我们创建一个结构体实例时,它的内存将被分配在堆上。这意味着结构体的生命周期可以超出创建它的函数的生命周期。

例如,当我们使用new关键字或make函数创建一个结构体实例时,它将被分配在堆上。

type Person struct {
    Name string
    Age  int
}

func main() {
    // 使用 new 关键字创建结构体实例
    p := new(Person)
    p.Name = "Alice"
    p.Age = 25

    // 使用 make 函数创建结构体实例(只适用于为某些类型分配内存,如 map、slice 和 channel)
    m := make(map[string]int)
    m["key"] = 42
    
    // 结构体实例被分配在堆上,可以在其他函数中继续使用
    anotherFunc(p)
    yetAnotherFunc(m)
}

这是因为在Go语言中,所有的变量都是通过传值而不是通过引用传递。在堆上分配结构体实例可以确保结构体数据的持久性和可用性。

image-20231025144249457

Go语言中结构体的内存布局

Go语言中的结构体是一组值的集合,这些值被存储在内存中的一段连续的区域。结构体的内存布局取决于结构体中的成员变量顺序和类型,以及对齐方式。

结构体的对齐方式

Go语言中使用的是一种称为Packing的方式进行对齐。这种方式默认对齐到最多8字节的倍数,即8字节对齐。可以通过在结构体成员变量的后面添加逗号数字的形式手动调节对齐方式。

type Person struct {
	Name   string
	Age    int
	Height float64
}

func main() {
	var p Person

	// 获取结构体的大小
	size := unsafe.Sizeof(p)
	fmt.Printf("结构体大小:%d 字节\n", size)

	// 获取结构体字段的偏移量
	nameOffset := unsafe.Offsetof(p.Name)
	ageOffset := unsafe.Offsetof(p.Age)
	heightOffset := unsafe.Offsetof(p.Height)
	fmt.Printf("Name 字段的偏移量:%d 字节\n", nameOffset)
	fmt.Printf("Age 字段的偏移量:%d 字节\n", ageOffset)
	fmt.Printf("Height 字段的偏移量:%d 字节\n", heightOffset)

	// 结构体的对齐方式
	packed := unsafe.Alignof(p)
	fmt.Printf("结构体的对齐方式:%d 字节\n", packed)

输出结果:

结构体大小:20 字节
Name 字段的偏移量:0 字节   
Age 字段的偏移量:8 字节    
Height 字段的偏移量:12 字节
结构体的对齐方式:4 字节    

在这个示例中,我们定义了一个Person结构体,它包括名字、年龄和身高三个字段。我们通过unsafe包中的函数来获取结构体的大小、字段的偏移量以及对齐方式。结构体的大小为20字节,字段的偏移量分别为0字节、8字节和12字节,结构体的对齐方式为4字节。

1.3 创建结构体四种方法

基本介绍

  1. 从概念或叫法上:结构体字段 = 属性 = field
  2. 字段是结构体的一个组成部分,一般是基本数据类型、数组、也可以是引用数据类型。
  3. 指针,slice和map的零值都是nil,即还没有分配空间。

方式一:直接声明

案例:var person Person

方式二:{}

案例:var person Person = Person{“Tom”, 18} => person := Person{“Tom”, 18}

方式三:&

案例:var person *Person = new (Person)

type Person struct {
	Name string
	Age  int
}

func main() {
	var p *Person = new(Person)
	// (*p).Name = "smith" 标准写法
	// go设计者,为了程序使用方便,底层对下面这个做了优化,实现了这种简单的写法
	// 会给 p 加上 取值运算 =》 (*p).Name = "smith" 
	p.Name = "smith" 
	p.Age = 18
	fmt.Println(p)
	fmt.Println(*p)
}

输出结果:

&{smith 18}
{smith 18}

方式四:{}

案例:var person *Person = &Person{}

type Person struct {
	Name string
	Age  int
}

func main() {
	var p *Person = &Person{}
	// 标准方式:(*person).Name = "scott"
	p.Name = "scott"
	p.Age = 18
	fmt.Println(p)
}

输出结果:&{scott 18}

1.4 注意事项和使用细节
  1. 结构体的所有字段在内存中是连续的,指针本身的地址是连续的,但是指向的地址不一定是连续的。

image-20231025153910544

//结构体
type Point struct {
    x int
    y int
}

//结构体
type Rect struct {
    leftUp, rightDown Point
}

//结构体
type Rect2 struct {
    leftUp, rightDown *Point
}

func main() {

    r1 := Rect{Point{1,2}, Point{3,4}} 

    //r1有四个int, 在内存中是连续分布
    //打印地址
    fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n", 
    &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)

    //r2有两个 *Point类型,这个两个*Point类型的本身地址也是连续的,
    //但是他们指向的地址不一定是连续

    r2 := Rect2{&Point{10,20}, &Point{30,40}} 

    //打印地址
    fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n", 
       &r2.leftUp, &r2.rightDown)

    //他们指向的地址不一定是连续..., 这个要看系统在运行时是如何分配
    fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n", 
       r2.leftUp, r2.rightDown)
}

输出结果:

r1.leftUp.x 地址=0x9496080 r1.leftUp.y 地址=0x9496084 r1.rightDown.x 地址=0x9496088 r1.rightDown.y 地址=0x949608c
r2.leftUp 本身地址=0x948a038 r2.rightDown 本身地址=0x948a03c
r2.leftUp 指向地址=0x9496068 r2.rightDown 指向地址=0x94960a0
  1. 结构体之间可以转换,但是有要求,就是结构体的字段要完全一样(包括:名字、类型,个数)

    type A struct {
    	Num int
    }
    
    type B struct {
    	Num int
    }
    
    func main() {
    	var a A
    	var b B
    	a = A(b)
    	fmt.Println(a, b)
    }
    
  2. 结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转。

type Student struct {
	Name string
	Age  int
}

type Stu Student

func main() {
	var stu1 Student
	var stu2 Stu
	// stu2 = stu1 // 错误,系统认为这是两个不一样的类型
	stu2 = Stu(stu1)
	fmt.Println(stu1, stu2)
}
  1. struct的每个字段上,可以写上一个标签tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化。

    type Student struct {
    	Name string `json:"name"` // 这里就是结构体的标签tag
    	Age  int    `json:"age"`
    }
    
    func main() {
    	// 1.创建一个student变量
    	student := Student{"张三", 18}
    	// 2.将monster变量序列化为json格式字符串
    	jsonStr, err := json.Marshal(student) // 这里json.Marshal底层用到了反射
    	if err != nil {
    		fmt.Println("jsonStr报错")
    	}
    	fmt.Println("jsonStr:", jsonStr)
    	fmt.Println("string(jsonStr):", string(jsonStr))
    }
    

    如果这里不加上标签,生成的json格式,就变成:string(jsonStr): {"Name":"张三","Age":18}

    会发现,这里的Name的首字母是大写,这又是不可避免,因为小写就调用不了

    所以这里通过标签,设置一个别名,底层用了反射解决这个问题~~~

    输出结果:string(jsonStr): {"name":"张三","age":18}

2 方法

在某些情况下,我们需要声明(定义)方法。比如Person结构体:除了有一些字段外(年龄,姓名…),Person结构体还有一些行为。比如:可以说好,跑步,学习等,还可以做算术题。这时就要用方法才能完成

Golang中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅只是struct。

2.1 方法的声明和调用
type A struct {
    Num int
}

func (a A) test() {
    fmt.Println(a.Num)
}

对上面的语法说明:

  1. func (a A) test() {} 表示A结构体有一方法,方法名为 test
  2. (a A) 体现test方法是和A类型绑定的
type Person struct {
	Name string
}

func (p Person) test() {
	p.Name = "Tom"
	fmt.Println("test():", p.Name)
}

func main() {
	person := Person{Name: "张三"}
	person.test() // 调用方法
	fmt.Println("main p.Name=", person.Name)
}

输出结果:

test(): Tom
main p.Name= 张三

对上面的总结:

  1. test方法和Person类型绑定

  2. test方法只能通过Person类型的遍历来调用,而不能直接调用,也不嫩更实用其他类型变量来调用。

  3. func (p Person) test() {} 这个p是它的副本,进行的是值传递,与函数很像。

2.2 快速入门案例
  1. 给Person结构体添加speak 方法,输出 xxx是一个好人

    type Person struct{
    	Name string
    }
    
    //给Person结构体添加speak 方法,输出  xxx是一个好人
    func (p Person) speak() {
    	fmt.Println(p.Name, "是一个goodman~")
    }
    
  2. 给Person结构体添加jisuan 方法,可以计算从 1+…+1000的结果,

    type Person struct {
    	Name string
    }
    
    func (p Person) jisuan() {
    	res := 0
    	for i := 1; i <= 1000; i++ {
    		res += i
    	}
    	fmt.Println(p.Name, "计算的结果是=", res)
    }
    
    func main() {
    	p := Person{Name: "张三"}
    	p.jisuan() // 输出:张三 计算的结果是= 500500
    }
    
  3. 给Person结构体jisuan2 方法,该方法可以接收一个参数n,计算从 1+…+n 的结果

    type Person struct {
    	Name string
    }
    
    // 给Person结构体jisuan2 方法,该方法可以接收一个参数n,计算从 1+..+n 的结果
    func (p Person) jisuan2(n int) {
    	res := 0
    	for i := 1; i <= n; i++ {
    		res += i
    	}
    	fmt.Println(p.Name, "计算的结果是=", res)
    }
    
    func main() {
    	p := Person{Name: "张三"}
    	p.jisuan2(10) // 输出:张三 计算的结果是= 55
    }
    
  4. 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果

    type Person struct {
    	Name string
    }
    
    // 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
    func (p Person) getSum(n1 int, n2 int) int {
    	return n1 + n2
    }
    
    func main() {
    	p := Person{Name: "张三"}
    	sum := p.getSum(1, 1)
    	fmt.Printf("%v sum=%v", p.Name, sum) //输出:张三 sum=2
    }
    
2.3 调用机制和传参原理

说明:方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当作实参也传递给方法。下面举例说明:

案例:

image-20231025165316278

type Person struct {
	Name string
}

// 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
func (p Person) getSum(n1 int, n2 int) int {
	return n1 + n2
}

func main() {
	p := Person{Name: "张三"}
	n1 := 10
	n2 := 20
	res := p.getSum(n1, n2)
	fmt.Println("res=", res)
}
  1. 在通过一个变量去调用方法时,其调用机制和函数一样
  2. 不一样的地方是,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果是引用类型,则进行地质拷贝)
2.4 注意事项和细节
  1. 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
  2. 如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
  3. Golang中的方法作用在指定的数据类型上的(即: 和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct,比如int ,float32等都可以有方法
  4. 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包4访问,方法首字母大写,可以在本包和其它包访问。
  5. 如果一个类型实现了String这个方法,那么fmt.Println默认会调用这个变量的String()进行输出

上面这些注意事项都比较简单,就代码展示一下最后一条:

type Student struct {
	Name string
	Age  int
}

// 给*Student实现方法String()
func (stu *Student) String() string {
	str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
	return str
}

func main() {
	//定义一个Student变量
	stu := Student{
		Name: "tom",
		Age:  20,
	}
	//如果你实现了 *Student 类型的 String方法,就会自动调用
	fmt.Println(stu)
	fmt.Println(&stu)
}

输出结果:

{tom 20}
Name=[tom] Age=[20]
2.5 方法和函数区别
  1. 调用方式不一样
  • 函数的调用方式:函数名(实参列表)
  • 方法的调用方式:变量.方法名(实参列表)
  1. 数据传递的限制不一样
  • 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。
  • 对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的遍历调用方法,反过来同样也可以。

这一点很容易迷糊,下面用段代码解释一下:

type Person struct {
    Name string
}

//函数
//对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然

func test01(p Person) {
    fmt.Println(p.Name)
}

func test02(p *Person) {
    fmt.Println(p.Name)
}

//对于方法(如struct的方法),
//接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以

func (p Person) test03() {
    p.Name = "jack"
    fmt.Println("test03() =", p.Name) // jack
}

func (p *Person) test04() {
    p.Name = "mary"
    fmt.Println("test03() =", p.Name) // mary
}

func main() {

    p := Person{"tom"}
    test01(p)
    test02(&p)

    p.test03()
    fmt.Println("main() p.name=", p.Name) // tom

    (&p).test03() // 从形式上是传入地址,但是本质仍然是值拷贝

    fmt.Println("main() p.name=", p.Name) // tom

    (&p).test04()
    fmt.Println("main() p.name=", p.Name) // mary
    p.test04()   // 等价 (&p).test04 , 从形式上是传入值类型,但是本质仍然是地址拷贝
}

输出结果:

tom
tom
test03() = jack
main() p.name= tom
test03() = jack
main() p.name= tom
test03() = mary
main() p.name= mary
test03() = mary

从代码会发现,仅管传递的是一个地址,但是编译器进行了内部优化,实际上还是值传递,只是支持这种写法,但并不是进行一个地址值的修改。

3 工厂模式

说明:Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题

相当于,这个工厂模式,就是以前构造函数的功能。

看个需求:

一个结构体的声明如下:

package model

type Student struct {
	Name string
}

因为这里的 Student 的首字母S是大写的,如果我们想在其他包串接Student的实例(比如:main包),引入model包后,就可以直接创建Student结构体的变量(实例)。

但是问题来了,如果搜字母是小写的,比如是: type student struct {…}就不行了,咋办?-》工厂模式来解决,

Model包

package model

type student struct {
	name  string
	score float64
}

// 因为student结构体首字母是小写,因此是只能在model使用
// 通过工厂模式来解决
func NewStudent(n string, s float64) *student {
	return &student{
		name:  n,
		score: s,
	}
}

// 如果score字段首字母小写,则,在其他包不可以直接方法,我们可以提供一个方法
func (s *student) GetScore() float64 {
	return s.score
}

// 如果score字段首字母小写,则,在其他包不可以直接方法,我们可以提供一个方法
func (n *student) GetName() string {
	return n.name
}

main包

import (
	"GoStudy_Day1/model"
	"fmt"
)

func main() {
	// 定student结构体是首字母小写,我们可以通过工厂模式来解决
	stu := model.NewStudent("Tom", 88.8)
	fmt.Println(stu)
	fmt.Println(*stu)
	// fmt.Println("name=", stu.name) // 报错,因为是私密的
	fmt.Println("name=", stu.GetName())
	fmt.Println("score=", stu.GetScore())
}

输出结果:

&{Tom 88.8}
{Tom 88.8}
name= Tom
score= 88.8

其实,这就看出来,就是Java的GetSet,绕了一圈,又回来啦~~~!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1132603.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录打卡第五十天|198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

198.打家劫舍 题目&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 …

MySQL——九、SQL编程

MySQL 一、触发器1、触发器简介2、创建触发器3、一些常见示例 二、存储过程1、什么是存储过程或者函数2、优点3、存储过程创建与调用 三、存储函数1、存储函数创建和调用2、修改存储函数3、删除存储函数 四、游标1、声明游标2、打开游标3、使用游标4、关闭游标游标案例 一、触发…

算法通关村第三关|青铜|线性表数组热身

1.线性表 1.1 线性表 线性表就是具有相同特征数据元素的一个有限序列。 1.2 数组存储元素的初始化 数组初始化时必须对从前向后的连续空间初始化&#xff0c;不可以出现空缺的情况。写 0 也是初始化&#xff0c;也要从前向后写。 2.热身-单调数组 判断是否为单调数组&am…

【Gensim概念】02/3 NLP玩转 word2vec

第二部分 句法 六、句法模型&#xff08;类对象和参数&#xff09; 6.1 数据集的句子查看 classgensim.models.word2vec.BrownCorpus(dirname) Bases: object 迭代句子 Brown corpus (part of NLTK data). 6.2 数据集的句子和gram classgensim.models.word2vec.Heapitem(c…

springboot源码refreshContext精细化阅读

注&#xff1a;该流程图表达了在refreshContext的过程中主要方法的展示以及重点类的标注。

家用洗地机什么牌子好?清洁力强的家用洗地机推荐

如今人们工作繁忙&#xff0c;很少有人还有多余的精力回到家之后去清扫地面卫生&#xff0c;这个时候拥有一款优质的家用洗地机就可以很好帮助人们完成地面清洁工作。那么家用洗地机什么牌子好?双十一将临&#xff0c;下面介绍清洁力强的家用洗地机。 洗地机选购要点 1.清洁…

Anaconda安装教程(Windows环境下)

下面的步骤是根据一个大佬的博文操作的&#xff0c;然后自己写了操作步骤和遇到的一些问题&#xff0c; 大佬博客在这&#xff1a; 【精选】Anaconda超详细安装教程&#xff08;Windows环境下&#xff09;_conda安装-CSDN博客 1、 Anaconda的下载&#xff1a; 一般两个选择&a…

个人创业新机遇,零成本开启真人手办定制项目

桌上的日历变薄&#xff0c;2023年已经接近尾声&#xff0c;浅观这一年&#xff0c;您是否发现大家日常关注的重点有明显的变化&#xff0c;诸多社会事件和宏观数字的背后&#xff0c;潜藏着对经济的“不托底”&#xff0c;而当我们真正开始关注起用个人经济积累去对冲未来的不…

JavaScript禁用网页回退

//防止页面后退 history.pushState(null, null, document.URL); window.addEventListener(popstate, function() { history.pushState(null, null, document.URL); }); 这个就是大家在做一个登录页面的时候退回登录页面之后&#xff0c;防止再返回“首页”&#xff0c;&…

如何构建成功的计算机视觉应用

引导计算机视觉项目从原型进入生产 随着现有图像数据量的增长以及人工智能 (AI) 对全球企业重要性的日益增强&#xff0c;计算机视觉正在一些行业快速发展。计算机视觉是机器学习 (ML) 的一种形式&#xff0c;它帮助计算机“看到”与人眼所见类似的图像&#xff0c;并对图像进…

竞赛 深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习&#xff1f;5.1.2 为什么要迁移学习&#xff1f; 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

博通BCM575系列RDMA网卡驱动分析(一)

简介 整个BCM系列驱动分成以太网部分(bnxt_en.ko)和RDMA部分(bnxt_re.ko), 两个模块之间通过内核的auxiliary_bus进行管理.我们主要分析下bnxt_re驱动. 代码结构 这个驱动的核心是 qplib_fp.c, 这个文件主要包含了驱动的数据路径, 包括Post Send, Post Recv, Poll CQ流程的实…

迭代器的封装与反向迭代器

一、反向迭代器 在list模拟实现的过程中&#xff0c;第一次接触了迭代器的封装&#xff0c;将list的指针封装成了一个新的类型&#xff0c;并且以迭代器的基本功能对其进行了运算符重载 反向迭代器是对正向迭代器的封装&#xff0c;并且体现了泛型编程的思想&#xff0c;任意…

PX30 android8.1添加RTL8723DU

将8723du复制到kernel/drivers/net/wireless/rockchip_wlan/rtl8723du 并修改makefile 修改平台 CONFIG_PLATFORM_I386_PC nCONFIG_PLATFORM_ARM_RK3188 y 修改 ifeq ($(CONFIG_PLATFORM_ARM_RK3188), y) EXTRA_CFLAGS -DCONFIG_LITTLE_ENDIAN -DCONFIG_PLATFORM_ANDRO…

【C语言】指针那些事之数组传参和指针传参的区别

C语言系列 文章目录 目录 C语言系列 文章目录 前言 一&#xff0c;数组传参和指针传参 1.1一维数组传参 1.2二维数组传参 1.3一级指针传参 1.4当一个函数的参数部分为一级指针的时候&#xff0c;函数可以接收什么参数 1.5二级指针传参 1.6当函数的参数为二级指针的时…

服务器动态/静态/住宅/原生IP都是什么意思

​  在互联网的世界中&#xff0c;我们经常会听到关于IP地址的各种说法&#xff0c;比如服务器动态IP、静态IP、住宅IP和原生IP。那么这些术语究竟代表着什么意思呢?让我们一起来了解一下。 动态IP 动态IP(Dynamic IP)是指互联网服务提供商(ISP)在每次用户上网时&#xff0c…

智安网络|探索语音合成技术的未来:揭秘人工智能配音技术的背后

随着人工智能技术的迅猛发展&#xff0c;配音行业也迎来了人工智能配音技术的崭新时代。人工智能配音技术通过语音合成和自然语言处理等技术手段&#xff0c;实现了逼真的语音合成&#xff0c;为影视、广告和游戏等领域带来了新的可能性。 第一部分&#xff1a;语音合成技术的…

求臻医学:肺癌患者就诊指南及基因检测意义

2023年国家癌症中心公布的最新的数据显示&#xff0c;中国癌症新发病例数前十的癌症分别是&#xff1a;肺癌82万&#xff0c;结直肠癌56万&#xff0c;胃癌48万&#xff0c;乳腺癌42万&#xff0c;肝癌41万&#xff0c;食管癌32万&#xff0c;甲状腺癌22万&#xff0c;胰腺癌12…

塔式服务器介绍

大家都知道服务器分为机架式服务器、刀片式服务器、塔式服务器三类&#xff0c;今天小编就分别讲一讲这三种服务器&#xff0c;第三篇先来讲一讲塔式服务器的介绍。 塔式服务器定义&#xff1a;塔式服务器的外观和普通电脑差不多&#xff0c;直立放置。机箱比较大&#xff0c;服…

信息安全应急响应小组 诚招

介绍 小组接触领域:攻防领域CISP认证,CTF,工业自动化控制,工业网络,机械制图等 小组成立意义;致力于解决公司内外网安全,高校内外网安全,CTF赛题分析,工控领域内网安全等 小组