C++-Golang的数组类型异同

文章对比了Golang和C++的数组。指出C++将底层细节暴露,Golang提供“最佳实践”。两者数组内存模型都是线性结构,长度在编译期确定且不可修改。参数传递上,C++靠指针且无限制,Golang有长度检查机制,还可自由控制传拷贝或指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 本文章的必要性

C++的存在像一把瑞士军刀,继承了C语言的设计理念——“充分相信程序员”,几乎将所有的底层细节都暴露在外,程序员可以自由控制。在最近详细学习Golang的过程中,发现Golang中的常用结构的设计理念与C++截然不同,为许多常用的操作都提供了语言设计者所认为的“最佳实践”。本文章仅讨论Golang中数组和切片。

2. Golang数组 和 C++数组(Array)

例子

C++ version

// const常量长度初始化
// 显然非常量无法作为初始化的长度,因为编译器在编译期无法确定要为此数组分配的内存长度
const int len = 10;
int cpp_arr[len];

// 初始化赋值
int cpp_arr[] = {1, 2, 3, 4, 5};

Golang version

// const常量长度初始化
// 显然非常量无法作为初始化的长度,理由同上
const len := 10
var golang_arr [len]int

// 初始化赋值
golang_arr := [...]int{1, 2, 3, 4, 5}

// Golang额外提供了语法,用于初始化数组为同一元素
// 将golang_arr初始化为具有100个元素-1的数组
golang_arr := [...]int{100:-1}

内存模型

两者一样,都是线性的内存结构。数组的本质,即为编译器在编译期在数据段分配常量长度的内存,再填充上指定的数据。从这方面很容易理解,两者的数组的长度是不容许修改的。

参数传递

1658072-20190618193921734-1247277430.png

C++ version
C++的数组传参都只能靠指针
但对于指针的移动偏移无任何限制,只能靠程序员手动管理,如果读写越界,可能会破坏堆栈

int passArray(int arr[])
{
    // 合法
    arr[0] = 0;   
    // 不合法!可能会破坏进程地址空间中的内存布局
    arr[-1000] = 0;
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    passArray(&arr[0]);
    return 0;
}

golang version

// 通过改变ptr的指向,将ptr所指的内存置为空
// 原来的内存,GC可通过引用计数探测,将其释放
func passArrayByPointer(ptr *[32]int) {
  *ptr = [32]int{}
}

// 无效,只是对拷贝进行了一次赋值
func passArrayByValue(arr [32]int) {
    arr = [32]int{}
}
  • Golang对C++传递数组进行了限制和优化,将数组类型作为函数的形参时,必须显式的指定该数组的长度。在编译期,编译器也会对传入数组的长度与声明的长度进行比对,如果不相等则无法编译通过。
  • 和其他高级语言不同(比如Java),语言中内置的类型有的为引用类型(在作为函数参数时直接传递引用),有的为值类型(在作为函数参数时传递一份拷贝),Golang在这方面引入了指针,可以自由控制给函数传递的数组是拷贝,还是指针(这是传参时,与C++最大的不同)

    总结

  1. 语法格式不同(废话)
  2. 作为参数传递时,长度Golang有严格的长度检查机制,防止写坏内存
  3. Golang中的数组是值语义,数组名即代表整个数组,而不像C/C++隐含为第一个元素的地址

  4. 内存布局一致
  5. 分配特性一致,即数组长度在编译期即确定,无法改变

转载于:https://www.cnblogs.com/jo3yzhu/p/11047177.html

<think>嗯,用户想了解结构体直接赋值在C、C++Golang中的使用方法及注意事项。首先,我需要回忆这三种语言中结构体赋值的基本机制,可能还要比较它们的异同。 先从C语言开始吧。C语言中的结构体是值类型,直接赋值的话应该是内存复制,也就是浅拷贝。那如果结构体里有指针成员,直接赋值可能会导致两个结构体的指针指向同一块内存,这样修改其中一个会影响另一个,需要注意深拷贝的问题。比如,用户提到结构体直接赋值的话,可能需要手动处理指针成员的内存管理,避免悬空指针或者内存泄漏。 然后是C++,和C类似,结构体默认也是值复制。但C++有拷贝构造函数和赋值运算符重载,用户可以自定义这些函数来实现深拷贝或者其他的操作。如果结构体里有动态分配的资源,比如指针,就需要自己实现深拷贝,否则默认的浅拷贝会有问题。比如,引用中的示例可能没有提到C++的拷贝控制,这点需要补充。 接下来是GolangGolang的结构体也是值类型,直接赋值会进行值拷贝。但如果有引用类型的字段,比如切片、map、指针,拷贝后的结构体和原结构体的这些字段会共享底层数据。比如用户提供的例子中,p1和p2在直接赋值后,修改p1的Name不会影响p2,但如果修改Hobby切片,可能会影响p2,因为切片是引用类型。需要提醒用户注意这一点,避免意外的数据共享。 然后用户可能的问题是关于这三种语言在结构体赋值时的注意事项。比如在C中需要注意指针成员,在C++中需要处理拷贝构造函数,而在Golang中需要注意引用类型的字段。另外,性能方面,结构体直接赋值是内存复制,如果结构体很大,可能会影响性能,这时候用指针可能更高效。比如引用[4]提到Go的结构体在内存中是连续块,这可能影响性能,而引用[5]中的例子展示了拷贝赋值和指针赋值的区别。 还要注意不同语言的语法差异。比如在Golang中,可以使用:=来声明并初始化结构体,或者用new关键字生成指针,如引用[3]中的例子。而C和C++可能需要使用不同的语法,比如在C中直接赋值,或者使用memcpy函数。 此外,用户可能关心结构体赋值时的嵌套结构体如何处理。在C和Golang中,嵌套的结构体也会被完整拷贝,而如果嵌套的是指针,则拷贝的是指针地址,不是指向的数据。需要分清楚值类型和引用类型在不同语言中的表现。 总结来说,回答需要分三个部分,分别说明C、C++Golang中的结构体直接赋值用法,注意事项包括浅拷贝深拷贝、引用类型的影响、性能考虑以及语法差异。同时结合用户提供的引用内容,特别是Golang的例子,来具体说明。</think>### 结构体直接赋值在C/C++/Golang中的使用方法及注意事项 #### 一、C语言 1. **基本用法** C语言中结构体是值类型,直接赋值会执行**浅拷贝**(逐成员复制)。例如: ```c struct Point { int x; int y; }; struct Point p1 = {1, 2}; struct Point p2 = p1; // 直接拷贝所有成员的值 ``` 2. **注意事项** - **指针成员问题**:若结构体包含指针成员,直接赋值会导致两个结构体的指针指向同一内存区域: ```c struct Data { int *arr; }; int array[3] = {1, 2, 3}; struct Data d1 = {array}; struct Data d2 = d1; // d2.arr 和 d1.arr 指向同一数组 d1.arr[0] = 100; // d2.arr[0] 也会被修改 ``` - **深拷贝需求**:需手动复制指针指向的内容,避免悬空指针或内存泄漏。 #### 二、C++ 1. **基本用法** C++C类似,但支持更灵活的拷贝控制: ```cpp struct Point { int x; int y; }; Point p1{1, 2}; Point p2 = p1; // 调用默认拷贝构造函数(浅拷贝) ``` 2. **注意事项** - **自定义拷贝构造函数**:若结构体包含动态资源(如堆内存指针),需定义拷贝构造函数实现深拷贝: ```cpp struct Data { int *arr; Data(const Data& other) { // 深拷贝 arr = new int[3]; memcpy(arr, other.arr, sizeof(int)*3); } }; ``` - **赋值运算符重载**:需重载`operator=`避免重复释放内存: ```cpp Data& operator=(const Data& other) { if (this != &other) { delete[] arr; arr = new int[3]; memcpy(arr, other.arr, sizeof(int)*3); } return *this; } ``` #### 三、Golang 1. **基本用法** Golang结构体是值类型,直接赋值会执行**完全拷贝**(包括所有字段): ```go type Person struct { Name string; Age int } p1 := Person{"柯南", 2} p2 := p1 // 完全拷贝,p2p1独立 p1.Name = "路飞" // p2不受影响 ``` 2. **注意事项** - **引用类型字段**:若结构体包含切片、map或指针,拷贝后共享底层数据: ```go type Data struct { Hobby []string } d1 := Data{Hobby: []string{"海盗", "睡觉"}} d2 := d1 d1.Hobby[0] = "游泳" // d2.Hobby[0] 也会变为"游泳" ``` - **性能优化**:大结构体建议使用指针赋值避免内存拷贝: ```go p3 := &Person{"路飞", 2} p4 := p3 // 仅复制指针,共享数据 ``` #### 四、对比总结 | 特性 | C/C++ | Golang | |---------------------|---------------------------|---------------------------| | **默认拷贝行为** | 浅拷贝 | 完全值拷贝 | | **指针成员风险** | 需手动深拷贝 | 共享引用类型字段的底层数据 | | **语法优化** | 需显式定义拷贝构造函数 | 支持指针赋值语法糖(`&`) | | **内存连续性** | 结构体成员连续存储[^4] | 结构体成员连续存储[^4] |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值