1

Тема: Незрозумілий запис масиву

Ось така функції дана в одному із завдань на Codewars:

int **SolvePuzzle (int *clues) {
    return 0;
}

Як із таким працювати, я маю на увазі int* i int**? На звичайні clues.at(0) або clues.size() воно лається.
Розумію, що вказівники, але як їх до звичного виду привести, до того ж вектора?

2

Re: Незрозумілий запис масиву

Teg Miles написав:

Як із таким працювати, я маю на увазі int* i int**?

Це типи вказівників. Під результат треба виділити память

  • C: ptr = malloc(size),

  • C++: ptr = new <type> | arr = new <type>[count].

Teg Miles написав:

На звичайні clues.at(0) або clues.size() воно лається.
Розумію, що вказівники, але як їх до звичного виду привести, до того ж вектора?

Чоловіче, зупинись, задумайся шо ти робиш. Чому ти взагалі згадав про вектор ? В задачі є щось про вектор ? якщо ні, то не варто його тягнути за вуха.

Див.new[]

Подякували: Teg Miles1

3

Re: Незрозумілий запис масиву

leofun01 написав:
Teg Miles написав:

Як із таким працювати, я маю на увазі int* i int**?

Це типи вказівників. Під результат треба виділити память

  • C: ptr = malloc(size),

  • C++: ptr = new <type> | arr = new <type>[count].

Teg Miles написав:

На звичайні clues.at(0) або clues.size() воно лається.
Розумію, що вказівники, але як їх до звичного виду привести, до того ж вектора?

Чоловіче, зупинись, задумайся шо ти робиш. Чому ти взагалі згадав про вектор ? В задачі є щось про вектор ? якщо ні, то не варто його тягнути за вуха.

Намагаюся зробити ось так:

int** answer = new int[4][4];

Отримую помилку:
помилка: не можна перетворити «int (*)[4]» на «int**» при ініціалізації
   13 |     int** answer = new int[4][4];
      |                                ^
      |                                |
      |                                int (*)[4]

Подякували: leofun011

4

Re: Незрозумілий запис масиву

Дивно, я не знайшов нормальних прикладів в cppreference. Добре що є stackoverflow.

Подякували: Teg Miles1

5

Re: Незрозумілий запис масиву

В нас на проджекті колись робили так :

int8_t const w = 4;
int8_t const h = 4;
int32_t *data = new int32_t[w * h];
int32_t **arr = new int32_t *[h];
if(data && arr)
    for(int8_t i = 0; i < h; ++i)
        arr[i] = data + w * i;
else throw "Memory allocation failed.";
// do something with arr[y][x]
delete[] arr;
delete[] data;

А могли би й так :

if(data && arr) // ...
else throw std::bad_array_new_length();

Ще 1 мій косяк, який вилазить через роки.

6

Re: Незрозумілий запис масиву

leofun01 написав:

В нас на проджекті колись робили так :

int8_t const w = 4;
int8_t const h = 4;
int32_t *data = new int32_t[w * h];
int32_t **arr = new int32_t *[h];
if(data && arr)
    for(int8_t i = 0; i < h; ++i)
        arr[i] = data + w * i;
else throw "Memory allocation failed.";
// do something with arr[y][x]
delete[] arr;
delete[] data;

А могли би й так :

if(data && arr) // ...
else throw std::bad_array_new_length();

Ще 1 мій косяк, який вилазить через роки.

Такий запис масивів — це просто legacy code чи від нього є якась користь?

7

Re: Незрозумілий запис масиву

Teg Miles написав:

Такий запис масивів — це просто legacy code чи від нього є якась користь?

Є користь. Типи-шаблони (std::array, std::vector) містять вказівники на динамічні масиви.
Користати чи не користати динамічні масиви, це залежить від типу проджекта. На деяких embedded проджектах не можна користати std::vector і інші std обгортки, можна тільки вказівники + масиви.
На деяких проджектах не можна навіть динамічні масиви, дозволено тільки звичайні масиви з фіксованою довжиною.
І ще існують дурнуваті проджекти, в яких вказівники взагалі не можна, і адресу брати теж не можна, тільки тому що хтось не дописав систему тестуваня софта для апаратів, які летять дуже далеко.

Подякували: Teg Miles1

8

Re: Незрозумілий запис масиву

Це більш-менш норма на C. Покажчик на масив - це покажчик на його нульовий елемент, довжину треба передавати або окремо, або записуючи "охоронця" (на кшталт нуля) в останній елемент. int ** - це покажчик на масив покажчиків на масиви, тобто треба виділяти пам'ять спершу для масиву int *, а потім окремо - для кожного підмасиву.

Подякували: Teg Miles, leofun012

9

Re: Незрозумілий запис масиву

Хочу заповнити нулями масив при ініціалізації, роблю так:

    int** answer = new int*[4];
    for (int i = 0; i < 4; ++i) {
        answer[i] = new int[4](0);
    }

Але показує помилку: Array 'new' cannot have initialization arguments.
Хоча масив після цього і заповнюється нулями, програма виконується.

10 Востаннє редагувалося leofun01 (24.08.2024 20:30:05)

Re: Незрозумілий запис масиву

Teg Miles написав:

Хочу заповнити нулями масив при ініціалізації..
Array 'new' cannot have initialization arguments.

Можна так :

int **answer = new int *[4];
for(int i = 0; i < 4; ++i)
    answer[i] = new int[4]{ 0 };
(тут я помилився)

або в 1 рядок :

int **answer = new int *[4]{ new int[4]{ 0 } };

upd: так робити не бажано, бо answer[1] і всі наступні будуть не ініціалізовані.

11

Re: Незрозумілий запис масиву

leofun01 написав:
Teg Miles написав:

Хочу заповнити нулями масив при ініціалізації..
Array 'new' cannot have initialization arguments.

Можна так :

int **answer = new int *[4];
for(int i = 0; i < 4; ++i)
    answer[i] = new int[4]{ 0 };

або в 1 рядок :

int **answer = new int *[4]{ new int[4]{ 0 } };

Ні, мабуть, доведеться окремий цикл робити для заповнення нулями.
Бо якщо нуль замінити на одиницю, він буде лише в першому стовпчику.
Це ж ініціалізація вказівника, а він лише на перший елемент.
Не знаю, чому з нулями всюди спрацьовує.

12

Re: Незрозумілий запис масиву

Загалом `type *` – це анотація вказівника на певний тип (зліва від *). Вказівник може вказувати як на один єдиний об'єкт так і на перший елемент з масиву об'єктів, як вже було сказано вище. Все залежить від контексту та даних.

int     a  [3] = {1, 2, 3};         // << це масив `int`ів
int  (*pa) [3] = &a;                // << це вказівник на масив `int`ів
int*    ap [3] = {a, a + 1, a + 2}; // << це масив з вказівників на `int`
int* (*pap)[3] = &ap;               // << це вказівник на масив з вказівників на `int`
int**  pp      = ap;                // << це вказівник на вказівник на `int`

Вказівники – це база, вони є всюди (якщо дозволяє середовище) :)

Подякували: Teg Miles, leofun012

13

Re: Незрозумілий запис масиву

wander написав:

Загалом `type *` – це анотація вказівника на певний тип (зліва від *). Вказівник може вказувати як на один єдиний об'єкт так і на перший елемент з масиву об'єктів, як вже було сказано вище. Все залежить від контексту та даних.

int     a  [3] = {1, 2, 3};         // << це масив `int`ів
int  (*pa) [3] = &a;                // << це вказівник на масив `int`ів
int*    ap [3] = {a, a + 1, a + 2}; // << це масив з вказівників на `int`
int* (*pap)[3] = &ap;               // << це вказівник на масив з вказівників на `int`
int**  pp      = ap;                // << це вказівник на вказівник на `int`

Вказівники – це база, вони є всюди (якщо дозволяє середовище) :)

Щоб заповнити весь масив треба було всі елементи вказувати.

answer[i] = new int[4] { 1, 1, 1, 1 };

Але все одно не розумію, чому з нулями спрацювало, хоча не повинно було.

Подякували: leofun011

14

Re: Незрозумілий запис масиву

Нарешті зрозумів.
Якщо ніяк не ініціалізувати масив (наприклад, просто new int[4]),
то всі змінні в масиві буде заповнено стандартною величиною -1094795586.
Якщо ж ви вказуєте лише частину елементів (наприклад, new int[4] {1} або new int[4] {1, 1}),
то незалежно від їхньої величини (буде це 0 чи 1 чи -122)
решта елементів заповнюється нулями.
Це якісь стандартні налаштувати мови С++.

Подякували: P.Y.1

15

Re: Незрозумілий запис масиву

Якщо ніяк не ініціалізувати, то нічого і не буде ініціалізовано. Там буде сміття, іноді компілятори або Address/Memory Sanitizers можуть пхати в неініціалізовану змінну певні маркери, як от -1094795586 значення. Для можливості відловлювання доступу до таких обʼєктів.
Щодо часткової ініціалізації, то так — це очікувана поведінка.

Подякували: leofun01, P.Y., koala3