二维数组中的 Memset
Posted
技术标签:
【中文标题】二维数组中的 Memset【英文标题】:Memset in 2D arrays 【发布时间】:2019-05-06 14:54:51 【问题描述】:我对静态二维数组有疑问。我想在第 k + 1 行之后重置元素,我想使用 memset。
我写了这段代码,但它并没有重置第 k + 1 行之后的所有行:
int a[505][505];
..................
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f >> a[i][j];
memset(a + k + 1 , 0 , sizeof(int) * (m + 1) * (n - k));
此代码不会重置第 k + 1 行之后的所有行。
编辑:n = 二维数组有多少行 m = 二维数组有多少列
编辑:我有一个更大的问题,每次我在我的问题中做某事时,我都需要重置第 k + 1 行。
【问题讨论】:
c++ 中的索引是从 0 开始的。所以a[0]
到a[504]
是有效的。如果n
和m
是505
(这是我的猜测),那么您将拥有超出范围的索引。
我不知道。 m
是什么? n
是什么?请分享minimal reproducible example。此示例不够完整,无法回答您的问题。
那么由于我在第一条评论中描述的问题,您有未定义的行为。
您可以像这样进行零初始化,而不是 memset
:int a[505][505];
。
另外请阅读how to ask good questions,以及this question checklist。好问题导致好答案,坏问题导致坏答案(以及大量的猜测和猜测)。
【参考方案1】:
数组的索引从0
开始。因此,如果您有一个 N
元素数组,则索引的有效范围是 [0, N)
。
这是一个演示程序,展示了如何将函数 memset
与整数数组一起使用。
#include <iostream>
#include <iomanip>
#include <cstring>
int main()
const size_t N = 5;
const size_t M = 10;
int a[N][M];
size_t k = 2;
for ( size_t i = 0; i < k; i++ )
for ( size_t j = 0; j < M; j++ ) a[i][j] = M * i + j;
std::memset( a[k], 0, ( N - k ) * M * sizeof( int ) );
for ( const auto &row : a )
for ( const auto &value : row ) std::cout << std::setw( 2 ) << value << ' ';
std::cout << '\n';
它的输出是
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
如果您只想将一行(例如第 k 行)重置为零,那么 memset 的调用将如下所示
std::memset( a[k], 0, M * sizeof( int ) );
更通用的方法是使用标准算法std::fill
。例如
std::fill( std::begin( a[k] ), std::end( a[k] ), 0 );
例如
#include <iostream>
#include <iomanip>
#include <iterator>
#include <algorithm>
int main()
const size_t N = 5;
const size_t M = 10;
int a[N][M];
size_t k = 2;
for ( size_t i = 0; i < N; i++ )
for ( size_t j = 0; j < M; j++ ) a[i][j] = M * i + j;
std::fill( std::begin( a[k] ), std::end( a[k] ), 0 );
for ( const auto &row : a )
for ( const auto &value : row ) std::cout << std::setw( 2 ) << value << ' ';
std::cout << '\n';
程序首先顺序填充数组的所有元素,然后将第 k 行重置为零。
当然,最初用零声明数组初始化它而不调用函数memset
会更简单(编译器会自己做)。
int a[N][M] = ;
【讨论】:
在我的情况下只重置第 k 行 @Sochuu 你用的是什么编译器? @Sochuu 这不重要。我提出了一个通用的方法。 @Sochuu 这是 OP 发布的 memsetstd::memset( a[k], 0, ( N - k ) * M * sizeof( int ) );
。高度怀疑启用优化后填充会变慢。
std::fill
在几乎所有编译器和标准库实现中都被编译为对memset
的调用,因此您声称std::fill
太慢了,因此您想使用memset
来代替逻辑,@Sochuu。非凡的主张需要非凡的证据。使用提供这些性能声明的测试用例更新您的问题,有人会很乐意进行调查。【参考方案2】:
我们这里不做 C ......请使用比 M 和 N 更多字母的标识符。
#include <cstddef>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <iterator>
template<typename T, std::size_t ROWS, std::size_t COLS>
void print_arr(T (&arr)[COLS][ROWS])
for (size_t row; row < ROWS; ++row)
std::copy(&arr[row][0], &arr[row][0] + COLS,
std::ostream_iterator<T> std::cout, "\t" );
std::cout.put('\n');
std::cout.put('\n');
template<typename T, std::size_t ROWS, std::size_t COLS>
void kill_all_from_line_till_last(T (&arr)[COLS][ROWS], std::size_t kill_from)
std::fill(&arr[kill_from][0], &arr[kill_from][0] + (ROWS - kill_from) * COLS, T);
int main()
constexpr size_t rows 10 ;
constexpr size_t columns 10 ;
int arr[rows][columns];
std::iota(&arr[0][0], &arr[0][0] + columns * rows, 1);
print_arr(arr);
kill_all_from_line_till_last(arr, 7);
print_arr(arr);
输出:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
伪 C 版本:
...使用std::memset()
看起来几乎一样:
#include <cstring>
template<typename T, std::size_t ROWS, std::size_t COLS>
void kill_all_from_line_till_last(T (&arr)[COLS][ROWS], std::size_t kill_from)
std::memset(&arr[kill_from][0], 0, (ROWS - kill_from) * COLS * sizeof(T));
但您只能将其用于 POD。
速度:
既然您提到您发现与std::memset()
相比,std::fill()
对于您的需求来说太慢了
@搜楚:
使用填充,可以,但是太慢了。我想要memset
constexpr size_t rows 10 ;
constexpr size_t columns 10 ;
int arr[rows][columns];
std::iota(&arr[0][0], &arr[0][0] + columns * rows, 1);
print_arr(arr);
kill_all_from_line_till_last_fill(arr, 7);
print_arr(arr);
int arr[rows][columns];
std::iota(&arr[0][0], &arr[0][0] + columns * rows, 1);
print_arr(arr);
kill_all_from_line_till_last_memset(arr, 7);
print_arr(arr);
gcc
从 gcc 9.1 (--std=c++14 -O3 -Wall
) 组装:
; ...
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
xor eax, eax
mov ecx, 15
mov rdi, rbx
rep stosq
mov rdi, rsp
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
; ...
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
xor eax, eax
mov rdi, rbx
mov ecx, 15
rep stosq
mov rdi, rsp
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
; ...
正如您所见,这两个版本在调用print_arr()
之间生成完全相同的代码。编译器并不(那么)愚蠢。
完整代码:godbolt Compiler Explorer
叮当
clang 8.3.0 (--std=c++14 -Ofast3 -Wall
) 相同,std::fill()
和 std::memset()
的代码完全相同:
; ...
mov rdi, rbx
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
xorps xmm0, xmm0
movups xmmword ptr [rsp + 376], xmm0
movups xmmword ptr [rsp + 360], xmm0
movups xmmword ptr [rsp + 344], xmm0
movups xmmword ptr [rsp + 328], xmm0
movups xmmword ptr [rsp + 312], xmm0
movups xmmword ptr [rsp + 296], xmm0
movups xmmword ptr [rsp + 280], xmm0
mov qword ptr [rsp + 392], 0
mov rdi, rbx
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
; ...
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
xorps xmm0, xmm0
movups xmmword ptr [rsp + 376], xmm0
movups xmmword ptr [rsp + 360], xmm0
movups xmmword ptr [rsp + 344], xmm0
movups xmmword ptr [rsp + 328], xmm0
movups xmmword ptr [rsp + 312], xmm0
movups xmmword ptr [rsp + 296], xmm0
movups xmmword ptr [rsp + 280], xmm0
mov qword ptr [rsp + 392], 0
mov rdi, rbx
call void print_arr<int, 10ul, 10ul>(int (&) [10ul][10ul])
; ...
完整代码:godbolt Compiler Explorer
msvc
微软 cl 19.20 (/O2
):
; ...
call void print_arr<int,10,10>(int (&)[10][10])
xorps xmm0, xmm0
lea rcx, QWORD PTR arr$2[rsp]
xor eax, eax
movups XMMWORD PTR arr$2[rsp+280], xmm0
mov QWORD PTR arr$2[rsp+392], rax
movups XMMWORD PTR arr$2[rsp+296], xmm0
movups XMMWORD PTR arr$2[rsp+312], xmm0
movups XMMWORD PTR arr$2[rsp+328], xmm0
movups XMMWORD PTR arr$2[rsp+344], xmm0
movups XMMWORD PTR arr$2[rsp+360], xmm0
movups XMMWORD PTR arr$2[rsp+376], xmm0
call void print_arr<int,10,10>(int (&)[10][10]) ;
; ...
call void print_arr<int,10,10>(int (&)[10][10])
xorps xmm0, xmm0
lea rcx, QWORD PTR arr$1[rsp]
xor eax, eax
movups XMMWORD PTR arr$1[rsp+280], xmm0
mov QWORD PTR arr$1[rsp+392], rax
movups XMMWORD PTR arr$1[rsp+296], xmm0
movups XMMWORD PTR arr$1[rsp+312], xmm0
movups XMMWORD PTR arr$1[rsp+328], xmm0
movups XMMWORD PTR arr$1[rsp+344], xmm0
movups XMMWORD PTR arr$1[rsp+360], xmm0
movups XMMWORD PTR arr$1[rsp+376], xmm0
call void print_arr<int,10,10>(int (&)[10][10])
; ...
完整代码:godbolt Compiler Explorer
我认为实验可以到此结束。
【讨论】:
以上是关于二维数组中的 Memset的主要内容,如果未能解决你的问题,请参考以下文章