1

Тема: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

Привіт. Потрібна допомога в наступній задачі
Дано двовимірний масив, розміри якого задає користувач. В даний масив потрібно вставити стовбчик у вказану користувачем позицію. Використовувати можна лише вказівники. Жодних індексів!!!
У мене зациклює функція  copy_arr. Що з нею не так?
Дякую

void init_arr(int *arr, int x_size)
{
    for (int *arr_ptr = arr; arr_ptr < arr + x_size; ++arr_ptr)
    {
        *arr_ptr = rand() % 50 - 25;
    }
}

void arr_initialization(int **arr, int lines, int columns) // ініціалізація масиву
{
    for (int **arr_ptr_ptr = arr; arr_ptr_ptr < arr + lines; ++arr_ptr_ptr)
    {
        *arr_ptr_ptr = new int[columns];
        init_arr(*arr_ptr_ptr, columns);
    }
}


void print_arr(int **arr, int lines, int columns) // друк масиву
{
    for (int **arr_ptr_ptr = arr; arr_ptr_ptr < arr + lines; ++arr_ptr_ptr)
    {
        for (int *arr_ptr = *arr_ptr_ptr; arr_ptr < *arr_ptr_ptr + columns; ++arr_ptr)
        {
            cout << setw(4) << *arr_ptr;
        }
        cout << endl;
    }
}

void copy_arr(int **arr_scr, int lines, int columns, int **arr_dst) //копіювання масиву
{
    for (int **arr_scr_ptr_ptr = arr_scr; arr_scr_ptr_ptr < arr_scr + lines; ++arr_scr_ptr_ptr)
    {
        for (int *arr_scr_ptr = *arr_scr_ptr_ptr; arr_scr_ptr < *arr_scr_ptr_ptr + columns; ++arr_scr_ptr, ++arr_dst)
        {
            *arr_dst = arr_scr_ptr;
            arr_scr_ptr = nullptr;
        }
    }
}

int **add_column_to_arr(int **arr, int lines, int *columns, int index)
{
    int new_size = *columns + 1;
    int **arr_ptr_ptr = new int*[lines];
    for (int y = 0; y < lines; ++y)
    {
        arr_ptr_ptr[y] = new int[new_size];
    }

    copy_arr(arr, lines, index, arr_ptr_ptr);

    copy_arr(arr + index, lines, *columns - index, arr_ptr_ptr + index + 1);

    for (int y = 0; y < lines; ++y)
    {
        arr_ptr_ptr[y][index] = 1;
    }

    *columns = new_size;
    return arr_ptr_ptr;
    for (int y = 0; y < lines; ++y)
        arr_ptr_ptr[y] = new int[*columns];
    clear_arr(arr_ptr_ptr, lines);
}

void task1()
{
    int lines = 0;
    int columns = 0;
    cout << "Array initialization." << endl;
    cout << "Enter number of lines : ";
    cin >> lines;
    cout << "Enter number of columns : ";
    cin >> columns;
    int **dimm2_arr;
    dimm2_arr = new int *[lines];
    for (int i = 0; i < lines; i++)
    {
        dimm2_arr[i] = new int[columns];
    }
    arr_initialization(dimm2_arr, lines, columns);
    print_arr(dimm2_arr, lines, columns);

    int index;
    do
    {
        cout << "Enter the index of column to add : ";
        cin >> index;
    } while (index < 0 || index > columns);

    dimm2_arr = add_column_to_arr(dimm2_arr, lines, &columns, index);
    print_arr(dimm2_arr, lines, columns);

    clear_arr(dimm2_arr, lines);
}

2

Re: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

Вітаю на форумі.

Пара загальних порад:

0. Операції з вказівниками взагалі небезпечні, тому їхню кількість слід мінімізувати, а самі операції обгортати у відповідні функції та структури. Тобто дуже бажано, щоб в main не було жодних вказівників, а тільки виклики відповідних функцій. Ну, то лише побажання.
1. Не працюйте в одній функції з двома рівнями масиву одночасно, надто легко заплутатися.
2. Стежте за відповідністю назв тому, що функції роблять. copy_arr має копіювати масив, а натомість, здається, його переміщує.

Що ж до функції copy_arr, то ви надто заглибилися, дивіться пораду 1. Конкретно проблема в тому, що після arr_scr_ptr = nullptr; умова arr_scr_ptr < *arr_scr_ptr_ptr + columns буде гарантовано виконуватися, і цикл не завершиться.

3 Востаннє редагувалося ntkrnlpa.exe (14.11.2016 08:03:55)

Re: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

якшо я правильно зрозумів завдання, то ось вам правильна C версія. :D
ваш код виявився занадто прогресивним для мене, шоб намагатися його зрозуміти. Я думаю, шо він неправильний. Ні, він точно неправильний. В дуже багатьох місцях, шоб це пояснювати.
Якшо цікаво, подивіться на код нижче. Він працює. Єдине, я не впевнений шо зрозумів завдання точно. Тож, цей код робить те, шо я зрозумів як завдання. Плюс - нашвидкоруч вигадав інтерфейс введення. Ви можете змінити його якшо так бажається.
Програма приймає як правильне введення послідовність з 3-х чисел як аргументи командного рядка, де перше число задає кількість рядків, друге - кількість стовбчиків, третє - позицію в масиві яку треба поміняти, нумерація з нуля! Далі, якшо все правильно введено, програма ініціалізує масив так як ви робили, за допомогою, rand(), лише по модулю 256, просто шоб легше сприймалось на екрані. І просе ввести стовбчик, підказуючи як це зробити. Потім вставляє його і виводить змінений масив.
Для змінних Array і L вибраний тип void *, шоб підкреслити шо це не вказіники на int, насправді Array є вказівником який вказує на наш масив цілих, але логічно це ж двовимірний масив. Тож наш вказівник це просто вказівник на якийсь складний тип. Використання типу int * тут, могло б заплутати.  L - це вказівник власне на рядок цілих. На підмасив тобто. Знову довільний складний об'єкт. Шоб підкреслити це, ми вибираємо генеричний тип вказівника, а потім робимо потрібні приведення. У нас довжини масиву невідомі до часу виконання. Теж треба завважити.
Коментарі написав мовою, схожою на англійську, за інерцією.

Прихований текст
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char **argv){
    int    *C, lines, columns, position, length, i, j, x;
    void    *Array, *L;

    /* input check and usage instructions */
    if(argc != 4){
        printf("Usage: %s <lines> <columns> <position>\n", *argv);
        return 0xcacacaca;
    }

    lines        = atoi(*(argv + 1));
    columns        = atoi(*(argv + 2));
    position    = atoi(*(argv + 3));
    length        = lines * columns;

    if(length <= 0 || position >= columns || position < 0){
        printf("Invalid input.\n");
        return 0xcacacaca;
    }

    Array = (void *)malloc(length * sizeof(int)); 
    if(!Array){
        printf("malloc failed.\n");
        return 0xcacacaca;
    }

/* initialize and show the array */
/* L points to the "line item" in the array of lines */
    L = Array;    // first line address, we don't touch Array pointer for the obvious reasons, so we have a copy to work on
    for(i = 0; i < lines; i++){
// here, we initialize C pointer which points to the integer element inside the chosen line
        C = (int *)L;    // first column item in the i-th line
        for(j = 0; j < columns; j++){
            *C = rand() % 0x100;    // asign the integer element some number modulo 256 for readabilty
            if(j == position)
// show the element, we dereference C, this is equivalent to *((int *)Array + i * columns + j)
                printf("\t>%d<", *C);
            else
                printf("\t%d ", *C);
            C++;    // walk through the entire line
        }
/* now we adjust our line pointer to the next line address
 * we cast the pointer to the int pointer, because this way pointer arithmetics is easy. In fact, we just need
 * to adjust the line pointer to point to line length bytes forward, thus columns * sizeof(int) bytes forward.
 */
        L = (int *)L + columns;
        putc(0xa, stdout);
    }

    /* prompt the user to input their column to insert into the array */
    printf("enter your column values (%d numbers, type <Enter> after each to proceed).\n", lines);

    L = Array;
    for(i = 0; i < lines; i++){
        C = (int *)L + position;
        scanf("%d", &x);
        *C = x;
        L = (int *)L + columns;
    }

    printf("modified array:\n");
    L = Array;
    for(i = 0; i < lines; i++){
        C = (int *)L;
        for(j = 0; j < columns; j++){
            if(j == position)
                printf("\t>%d<", *C);
            else
                printf("\t%d ", *C);
            C++;
        }
        L = (int *)L + columns;
        putc(0xa, stdout);
    }
    free(Array);
    return 0;
}

А це скорочена версія, яка робить те саме, просто можливо буде наочніше. Правда не знаю, чи потрактується вона як "без індексів", бо хто зна шо саме мали на увазі даючи таке обмеження.

Прихований текст
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char **argv){
    int    lines, columns, position, i, j;
    void    *Array;

    if(argc != 4){
        printf("Usage: %s <lines> <columns> <position>\n", *argv);
        return 0xcacacaca;
    }

    lines        = atoi(*(argv + 1));
    columns        = atoi(*(argv + 2));
    position    = atoi(*(argv + 3));

    if(lines * columns <= 0 || position >= columns || position < 0){
        printf("Invalid input.\n");
        return 0xcacacaca;
    }

    Array = (void *)malloc(lines * columns * sizeof(int));
    if(!Array){
        printf("malloc failed.\n");
        return 0xcacacaca;
    }

    for(i = 0; i < lines; i++){
        for(j = 0; j < columns; j++){
            *((int *)Array + i * columns + j) = rand() % 0x100;
            if(j == position)
                printf("\t>%d<", *((int *)Array + i * columns + j));
            else
                printf("\t%d ", *((int *)Array + i * columns + j));
        }
        putc(0xa, stdout);
    }

    printf("enter your column values (%d numbers, type <Enter> after each to proceed).\n", lines);
    for(i = 0; i < lines; i++){
        scanf("%d", &j);
        *((int *)Array + i * columns + position) = j;
    }

    printf("modified array:\n");
    for(i = 0; i < lines; i++){
        for(j = 0; j < columns; j++){
            if(j == position)
                printf("\t>%d<", *((int *)Array + i * columns + j));
            else
                printf("\t%d ", *((int *)Array + i * columns + j));
        }
        putc(0xa, stdout);
    }
    free(Array);
    return 0;
}

4

Re: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

Привіт. Забув вказати, що мова програмування С++.
ntkrnlpa.exe, у вас в коді використовуються індекси
for(i = 0; i < lines; i++); таким способом я цю задачу зробив без особливих проблем.
На жаль, індекси використовувати не дозволяється, тому потрібно робити все через вказівники
for(int **arr_ptr = arr; arr_ptr = arr + lines; ++arr_ptr)
де **arr_ptr - вказівник на масив вказівників
arr - вказівкник на двовимірний масив

Думаю, що я майже зрозумів в чому проблема і сьогодні на свіжу голову спробую поправити код.

5 Востаннє редагувалося boshik1983 (14.11.2016 23:29:44)

Re: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

Зробив. Все працює. Але є питання: чи можна оптимізувати даний код?

#include<iostream>
#include<iomanip>
#include<time.h>

using namespace std;


void init_arr(int *arr, int x_size) //ініціалізація змінних в рядку
{
    for (int *arr_ptr = arr; arr_ptr < arr + x_size; ++arr_ptr)
    {
        *arr_ptr = rand() % 50 - 25;
    }
}

void arr_initialization(int **arr, int lines, int columns) //ініціалізація масиву
{
    for (int **arr_ptr_ptr = arr; arr_ptr_ptr < arr + lines; ++arr_ptr_ptr)
    {
        *arr_ptr_ptr = new int[columns];
        init_arr(*arr_ptr_ptr, columns);
    }
}


void print_arr(int **arr, int lines, int columns) //вивід масиву на екран
{
    for (int **arr_ptr_ptr = arr; arr_ptr_ptr < arr + lines; ++arr_ptr_ptr)
    {
        for (int *arr_ptr = *arr_ptr_ptr; arr_ptr < *arr_ptr_ptr + columns; ++arr_ptr)
        {
            cout << setw(4) << *arr_ptr;
        }
        cout << endl << endl;
    }
}


void clear_arr(int **arr, int lines) //видалення масиву
{
    for (int **arr_ptr_ptr = arr; arr_ptr_ptr < arr + lines; ++arr_ptr_ptr)
    {
        delete[] * arr_ptr_ptr;
    }
    delete[] arr;
}


//Написать функцию, добавляющую столбец двухмерному массиву в указанную позицию.
void add_column(int **scr, const int lines, const int columns, int **dst, const int index)
{
    for (int **scr_ptr = scr, **dst_ptr = dst; scr_ptr < scr + lines; ++scr_ptr, ++dst_ptr) //проходимося по ігрикам
    {
        for (int *scr_ptr_2 = *scr_ptr, *dst_ptr_2 = *dst_ptr; scr_ptr_2 < *scr_ptr + columns;) //проходимося по іксам
        {
            for (; scr_ptr_2 < *scr_ptr + index; ++scr_ptr_2, ++dst_ptr_2) //копіюємо значення по іксу від початку до індекса
            {
                *dst_ptr_2 = *scr_ptr_2;
            }
            *dst_ptr_2 = 1; // ініціалізація змінної індекса (можна і написати, щоб користувач вводив, або рандомно)
            ++dst_ptr_2; // переходимо до наступного ікса, щоб не скопіювати значення в індекс повторно
            for (; scr_ptr_2 < *scr_ptr + columns; ++scr_ptr_2, ++dst_ptr_2) //копіюємо значення по іксу від індекса до кінця
            {
                *dst_ptr_2 = *scr_ptr_2;
            }
        }
    }
}

int **add_column_to_arr(int **arr, int lines, int *columns, int index)
{
    int new_size = *columns + 1;
    int **arr_dst = new int*[lines];
    for (int **arr_ptr = arr_dst; arr_ptr < arr_dst + lines; ++arr_ptr)
    {
    *arr_ptr = new int[new_size];//створюємо масив на один ствпчик більший існуючого
    }

    add_column(arr, lines, *columns, arr_dst, index);
    *columns = new_size; //присвоюємо змінній нове значення, а масиву, відповідно, новий розмір
    return arr_dst;
    clear_arr(arr_dst, lines); //не забуваємо очистити пам'ять
}

void task1()
{
    int lines = 0;
    int columns = 0;
    cout << "Array initialization." << endl;
    cout << "Enter lines quantity : ";
    cin >> lines;
    cout << "Enter columns quantity : ";
    cin >> columns;
    int **dimm2_arr;
    dimm2_arr = new int *[lines];

    arr_initialization(dimm2_arr, lines, columns);
    print_arr(dimm2_arr, lines, columns);

    int index;
    do
    {
        cout << "Enter the index of column to add : ";
        cin >> index;
    } while (index < 0 || index > columns);

    dimm2_arr = add_column_to_arr(dimm2_arr, lines, &columns, index);
    print_arr(dimm2_arr, lines, columns);

    clear_arr(dimm2_arr, lines);
}

6 Востаннє редагувалося ntkrnlpa.exe (15.11.2016 13:42:12)

Re: Двовимірний динамічний масив (додати стовбчик у вказану позицію)

boshik1983 написав:

Привіт. Забув вказати, що мова програмування С++.
ntkrnlpa.exe, у вас в коді використовуються індекси
for(i = 0; i < lines; i++);
таким способом я цю задачу зробив без особливих проблем.
На жаль, індекси використовувати не дозволяється, тому потрібно робити все через вказівники

Думаю, що я майже зрозумів в чому проблема і сьогодні на свіжу голову спробую поправити код.

Ну який же це індекс, це лічильник цикла. :D

for(int **arr_ptr = arr; arr_ptr = arr + lines; ++arr_ptr)
де **arr_ptr - вказівник на масив вказівників
arr - вказівкник на двовимірний масив

Тут таке діло, якшо arr і справді був би "вказівником на двовимірний масив", тобто шось оголошене як
int arr[n][m]; ваш код не працював би. Навіть оцей шматок циклу вище був би глибоко неправильним. arr у вас такий же вказівник на вказівник на тип int як і arr_ptr, і ви його просто безглуздо копіюєте, хоча оскільки то аргумент переданий в функцію, яких ви наклепали невідомо нашо стільки, ви б могли його викорситовувати прямо. це вам оптимізація. а взагалі переробіть його - занадто багато непотрібних функцій.
чесно, розібратися в вашому коді майже неможливо.
Дивіться, а так - "без індексів"?

Прихований текст
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char **argv){
    int    *L, *C, lines, columns, position, Z;
    void    *Array;

    if(argc != 4){
        printf("Usage: %s <lines> <columns> <position>\n", *argv);
        return 0xcacacaca;
    }

    lines        = atoi(*(argv + 1));
    columns        = atoi(*(argv + 2));
    position    = atoi(*(argv + 3));

    if(lines * columns <= 0 || position >= columns || position < 0){
        printf("Invalid input.\n");
        return 0xcacacaca;
    }

    Array = (void *)malloc(lines * columns * sizeof(int));
    if(!Array){
        printf("malloc failed.\n");
        return 0xcacacaca;
    }

    for(L = (int *)Array; L < (int *)Array + columns * lines; L += columns){
        for(C = L; C < L + columns; C++){
            *C = rand() % 0x100;
            printf("\t%d", *C);
        }
        putc(0xa, stdout);
    }

    printf("enter your column values (%d numbers, type <Enter> after each to proceed).\n", lines);
    for(L = (int *)Array + position; L < (int *)Array + columns * lines; L += columns){
        scanf("%d", &Z);
        *L = Z;
    }

    printf("modified array:\n");
    for(L = (int *)Array; L < (int *)Array + columns * lines; L += columns){
        for(C = L; C < L + columns; C++)printf("\t%d", *C);
        putc(0xa, stdout);
    }
    free(Array);
    return 0;
}