js数组初始化时 fill 填充的小bug

本文通过一个JavaScript数组初始化的例子,揭示了使用new Array(n).fill([])与直接定义[[],[],[]]的区别,解释了前者会产生引用同一对象的问题及如何避免。

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

数组初始化代码如下:

const arr = new Array(3).fill([])
const arr2 = [[],[],[]]

console.log(JSON.stringify(arr)) //输出:[[],[],[]]
console.log(JSON.stringify(arr2)) //输出:[[],[],[]]
console.log(arr==arr2) //输出 false !! 明明stringify打印出来的内容都一样,为何这里却不等?

var arrIndex = 0
arr[arrIndex].push(123)
arr2[arrIndex].push(123)
console.log(JSON.stringify(arr)) //输出[[123],[123],[123]],为什么每一个元素都添加了123?
console.log(JSON.stringify(arr2)) //输出[[123],[],[]],这才是预期

console.log(arr) //输出[[123], [circular object Array], [circular object Array]],这是什么?引起怀疑
console.log(arr2) //输出[[123], [], []],这是预期

原本以为如下两种数组的初始化结果是相同的:

const arr = new Array(3).fill([])
const arr2 = [[],[],[]]

原来不是。第一种方式其实是:

const emptyArray = new Array([]) //这里只生成了一个对象
const arr = new Array(3).fill(emptyArray) //这里反复用同一个对象填充新生成数组的三个元素

实际结果:[emptyArray, emptyArray, emptyArray]

第二种方式是:

const arr2 = [emptyArray, emptyArray_2, emptyArray_3] //这才是我们原本期望的

结论和提醒:
    []也是一个对象
    Stringify相同,并不能说明两个对象相同,有可能两个对象其实是一个

<think>好的,用户想了解C++中fill函数和memset函数的区别及用法比较。首先,我需要回忆这两个函数的基本信息。 首先,memset是C标准库中的函数,在C++中也可以使用,它按字节来填充内存。函数原型是void *memset(void *s, int ch, size_t n),将s指向的内存区域的前n个字节设置为ch的ASCII值。例如,memset(arr, 0, sizeof(arr))会把arr的所有字节设为0。但需要注意的是,如果用memset来设置非字符类型的数组,比如int数组,设置为1的话,每个int的四个字节都会被设为0x01,结果每个int的值其实是0x01010101,而是1。所以memset通常用于初始化为0或-1(补码情况下全1)。 然后是fill函数,这是C++标准模板库中的算法,位于<algorithm>头文件中。它接受一个区间,可以是任意迭代器,比如数组指针或容器迭代器,并将该区间内的每个元素赋值为指定的值。因为fill是按元素类型来赋值的,所以可以正确设置每个元素的值,比如fill(arr, arr+10, 1)会把每个int元素设为1,而是按字节填充。 用户提供的引用资料也提到,fill是按照单元赋值的,而memset是按字节。引用[1]和[3]都说明了这一点。需要强调的是,memset在处理非字符类型可能有问题,而fill更安全。此外,fill可以用于任何容器,比如vector,而memset主要适用于数组或结构体。 接下来,我需要比较它们的用法和适用场景。比如,初始化数组为0,两者都可以,但memset更快。但如果要初始化非0的值,特别是非单字节兼容的值,必须用fill。例如,对于double数组初始化为3.14,只能用fill。 另外,性能方面,memset因为是底层操作,可能更快,特别是处理大块内存。但fill更灵活,适用范围广,尤其是对于非连续或复杂数据结构。 用户可能还想知道如何正确使用这两个函数,比如参数传递、头文件包含等。比如,memset需要<cstring>头文件,而fill需要<algorithm>。错误使用memset可能导致难以发现的bug,比如上面提到的int数组初始化为1的问题。 最后,总结两者区别,要分点说明:原理(字节vs元素)、适用范围(数组/结构体vs各种容器)、初始化值类型(单字节值vs任意值)、性能(memset可能更快)等。</think>### C++中`fill`与`memset`函数的区别及用法比较 #### 1. **实现原理** - **`memset`函数** 基于**字节填充**,将内存块的每个字节设置为指定值。例如: ```cpp int arr[10]; memset(arr, 0, sizeof(arr)); // 每个字节设为0,结果arr全为0 memset(arr, 1, sizeof(arr)); // 每个字节设为0x01,但int元素实际值为0x01010101 ``` 仅适用于`0`、`-1`(补码全1)或字符类型初始化[^2][^3]。 - **`fill`函数** 基于**元素类型赋值**,对区间内每个元素直接赋值。例如: ```cpp int arr[10]; fill(arr, arr + 10, 1); // 每个int元素被正确赋值为1 ``` 适用于任意数据类型(包括类对象)[^1][^3]。 --- #### 2. **适用范围** - **`memset`适用场景** - 原始数组(如`int[]`、`char[]`) - 结构体或内存块初始化(如清空缓冲区) - 仅支持**连续内存**的操作 - **`fill`适用场景** - 标准容器(如`vector`、`deque`) - 非连续内存结构(如链表支持) - 可操作任意迭代器定义的区间 --- #### 3. **初始化值类型** | 函数 | 支持的值类型 | 示例 | |-----------|----------------------------------|-------------------------------| | `memset` | 单字节值(如`0`, `-1`, ASCII字符) | `memset(str, 'A', 5)` | | `fill` | 任意类型值(包括对象、浮点数) | `fill(vec.begin(), vec.end(), 3.14)` | --- #### 4. **性能对比** - **`memset`更快**:直接操作内存,适合大规模连续内存初始化(如`int[10000]`)。 - **`fill`更灵活**:对非连续内存或复杂类型更安全,但可能有额外开销。 --- #### 5. **代码示例** ```cpp #include <algorithm> #include <cstring> int main() { // 使用memset初始化int数组(仅限0/-1) int arr1[10]; memset(arr1, 0, sizeof(arr1)); // 正确:全0 // 使用fill初始化int数组 int arr2[10]; fill(arr2, arr2 + 10, 100); // 正确:全100 // 错误示例:用memset初始化非0/-1的int数组 memset(arr1, 1, sizeof(arr1)); // 错误:元素值变为0x01010101 } ``` --- #### 6. **总结对比表** | 特性 | `memset` | `fill` | |--------------|-------------------------------|-------------------------------| | 填充单位 | 字节 | 元素 | | 头文件 | `<cstring>` | `<algorithm>` | | 适用容器 | 原始数组、结构体 | 数组、STL容器 | | 初始化值限制 | 单字节兼容值(如0、-1) | 任意类型值 | | 性能 | 更高(适合大规模连续内存) | 较低(但更通用) | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值