Memset в 2D массивах - PullRequest
       20

Memset в 2D массивах

0 голосов
/ 06 мая 2019

У меня проблема со статическим 2D-массивом.Я хочу сбросить элементы после 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-й строки.

edit: n = сколько строк в 2d-массивах m = сколько столбцов в 2d-массивах

edit: у меня большая проблема, и мне нужно сбрасывать k + 1-ю строку каждый раз, когда я что-то делаю в своей проблеме.

Ответы [ 2 ]

4 голосов
/ 06 мая 2019

Мы не делаем 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

Версия Pseudo-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::fill() слишком медленный для ваших нужд по сравнению с std::memset()

@ Sochuu:

сзаполнить, работает, но слишком медленно.Я хочу с 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 Explorer Compiler Explorer


clang

То же самое для 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 Explorer Compiler Explorer


msvc

Microsoft 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 Explorer Compiler Explorer

Я думаю, что эксперимент может закончиться вэтот момент.

4 голосов
/ 06 мая 2019

Индексы массивов начинаются с 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] = {};
...