1 Востаннє редагувалося 0xDADA11C7 (11.02.2022 05:38:21)

Тема: Вказівниковий сказ, функційна пошесть тощо

Я написав:

Може краще все ж якось інакше. Якусь інкапсуляцію зробити, фабрику класів, сінгтон вкінці-кінців.

Я пишу мовою С, а не С++ принципово, тому ООП можливо застовувати, але кишками назовні. Щодо шарів абстракцій, то з ними все в порядку.
Як я дійшов до char ***?
Спочатку я послуговувався char *:

char *p = strdup("перша стрічка");

Потім мені довелося створити список стрічок, це вже char **:

char *list[] = { strdup("перша стрічка"), strdup("перша стрічка"), strdup("перша стрічка") }

А тоді мені довелося створити функцію, яка приймає як аргумента вказівник на список стрічок. щоби функція могла працювати зі самим вказівником на список стрічок.
Тобто код має бути десь такий:

DestroyList(char ***ppList, unsigned n);

Але насправді він такий, щоб можна було вивільнювати будь-які вказівники у списку, а не лише ті, що посилаються на стрічки:

DestroyList(void *ppList, unsigned n);

Таким чином, якщо я забуду дописати амперсанд перед змінню, то її функція з'їсть - бо всі типи жере, але в роботі крешнеться:
Правильне застосування функції:

DestroyList(&list, 3);

Неправильне застосування функції:

DestroyList(list, 3);

Вказівники сказилися! Поможіть!
Робе добре:

char *list[] = { strdup("перша стрічка"), strdup("перша стрічка"), strdup("перша стрічка") }

Але наступні 2 рядки працюють з однаковими наслідками:

void *p1 = list;
void *p2 = &list;

Тобто значення змінних p1 та p2 будуть однаковими. Як мені дізнатися адресу змінної list?

2

Re: Вказівниковий сказ, функційна пошесть тощо

Знайшов відповідь.

    char **p = (char **)Malloc(3);
    p[0] = StrdupA("XEROX");
    p[1] = StrdupA("COP");
    p[2] = StrdupA("MEMORY ERROR");
    void *p2 = p;
    void *p3 = &p;

3 Востаннє редагувалося 0xDADA11C7 (09.02.2022 02:20:14)

Re: Вказівниковий сказ, функційна пошесть тощо

Мене ця тема зачепила, тому продовжу оповіданнячко...
Можна і так робити:

char **p = (char *[]){ StrdupA("XEROX"), StrdupA("COP"), StrdupA("MEMORY ERROR") };

Анонімні масиви, що з'явилися в С99

4

Re: Вказівниковий сказ, функційна пошесть тощо

*list[] 

Мені здається, що це створює масив вказівників, а не вказівник на масив. Чи навпаки???
А що буде як там дужки поставити?

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

5

Re: Вказівниковий сказ, функційна пошесть тощо

Так і є, char *list[] - масив вказівників, а char (*list)[] - вказівник на масив, що, в принципі, є майже синонімом char *list.

6

Re: Вказівниковий сказ, функційна пошесть тощо

Знайшов ось такий сайтик, можна дивитися всі ці структури:
https://replace.org.ua/uploads/images/931/9ab5983445a66e73eedbf8dd00e57389.png

Подякували: rb.fedor, Chemist-i, 0xDADA11C7, leofun014

7 Востаннє редагувалося wander (09.02.2022 13:29:24)

Re: Вказівниковий сказ, функційна пошесть тощо

Овва, нова тема в С/C++ і тут навіть не просять безплатно щось зробити замість автора..

0xDADA11C7 написав:

Вказівники сказилися! Поможіть!
Робе добре:

char *list[] = {strdup("перша стрічка"), strdup("перша стрічка"), strdup("перша стрічка")}

Але наступні 2 рядки працюють з однаковими наслідками:

void *p1 = list;
void *p2 = &list;

Тобто значення змінних p1 та p2 будуть однаковими. Як мені дізнатися адресу змінної list?

Пане 0xDADA11C7, вітаю вас у світі С/C++.
Ваша проблема в тому, що ви намагаєтесь робити перетворення в void*, фактично два різних типи. І схоже не знаєте, які перетворення відбуваються під капотом. Що ж, грубо кажучи, єдина вимога, щоб зробити каст у void* -- це щоб справа був хоча б якийсь вказівник на об'єкт.
Хоча у першому випадку

void *p1 = list; // тип list'а є char*[3]

, а у другому

void *p2 = &list; // тип &list'а є char*(*)[3]

І так, це різні типи. Відповідно згідно з правилами мови і правилами перетворення у void*, отримуємо

void *p1 = list; // тут conversion char** -> void* (char** же відповідно получився через array-to-pointer conversion)
void *p2 = &list; // тут conversion char*(*)[3] -> void*

Якщо ви хотіли отримати адрес list'a, то ви його отримали у двох випадках, бо всі перетворення звелися до void*.
Якщо ж, ви хотіли більш наочно це отримати, можна було зробити так:

char **p2 = list;
char *(*p3)[3] = &list; // або так

https://rextester.com/RTS77253

Подякували: 0xDADA11C7, koala, lucas-kane, leofun014

8

Re: Вказівниковий сказ, функційна пошесть тощо

Я гадав що у всіх випадках list це вказівник на масив вказівників - дякую, що виправили мою помилку. Щодо властивостей void* я знаю, у роботі мені доводиться ним послуговуватися, щоби не плодити зайві сутності, розумію що відсутність типізації в цьому випадку призводить до певних складнощів в роботі, але маємо те що маємо.

9 Востаннє редагувалося wander (09.02.2022 15:19:20)

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:

Я гадав що у всіх випадках list це вказівник на масив вказівників

list -- якраз таки у всіх випадках вказівник на масив вказівників. Проте, ви ж його перетворювали у void*, а там вже відбуваються неявні перетворення (згідно з правилами мови). Ну, і & -- теж змінює тип, як і value category виразу.

0xDADA11C7 написав:

Можна і так робити:

char **p = (char *[]){ StrdupA("XEROX"), StrdupA("COP"), StrdupA("MEMORY ERROR") };

Анонімні масиви, що з'явилися в С99

А, це якщо що, не анонімні масиви, а Compound Literals. Так, там йдеться про конструювання анонімного об'єкта, проте це не є, власне, особливістю лише масивів. Адже можна писати й так:

int i = ++(int){ 1 };
struct foo x = (struct foo){ 1, 'a', 'b' }; // чи так
Подякували: 0xDADA11C7, lucas-kane, leofun013

10

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:

Щодо шарів абстракцій, то з ними все в порядку.
Як я дійшов до char ***?
Спочатку я послуговувався char *:

char *p = strdup("перша стрічка");

Потім мені довелося створити список стрічок, це вже char **:

char *list[] = { strdup("перша стрічка"), strdup("перша стрічка"), strdup("перша стрічка") }

А тоді мені довелося створити функцію, яка приймає як аргумента вказівник на список стрічок. щоби функція могла працювати зі самим вказівником на список стрічок.
Тобто код має бути десь такий:

DestroyList(char ***ppList, unsigned n);

Тут вказівник на вказівник на масив вказівників. Логічніше використати вказівник на масив вказівників. Та із оголошених вами змінних випливає наступне:

DestroyList(char **pList, unsigned n);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void destroy(char **, size_t);
int main() {
    char *list[] = {strdup("str0"), strdup("str1"), strdup("str2")};
    destroy(list, 2); // free(list[2]);
    destroy(list, 1); // free(list[1]);
    destroy(list, 0); // free(list[0]);
    return 0;
}
void destroy(char **p_list, size_t id_list_el) {
    if (p_list[id_list_el]) {
        printf("Стрічку \"%s\" буде ЗНИЩЕНО! DESTROY\n", p_list[id_list_el]);
        free(p_list[id_list_el]);
    }
}

11

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:

Але насправді він такий, щоб можна було вивільнювати будь-які вказівники у списку, а не лише ті, що посилаються на стрічки:

DestroyList(void *ppList, unsigned n);

А які ще ділянки пам'яті можна вивільнити окрім тих, що є елементами масиву, - вказівників на ряди? Розташовані вони в КУПІ, створені за допомогою функції strdup. Ну видаляти можна тільки їх.

12

Re: Вказівниковий сказ, функційна пошесть тощо

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

lucas-kane написав:

А які ще ділянки пам'яті можна вивільнити окрім тих, що є елементами масиву

Про якісь додаткові ділянки пам'яті мова не йшла.

13 Востаннє редагувалося lucas-kane (10.02.2022 19:42:38)

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:

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

Та я зрозумів, і прочитав пост до кінця. Цікаво було. Дякую! Але постановка завдання така, що якщо правильно виконати покроково її, то і void * можна було б не робити. А на рахунок, що стрічки можуть бути не тільки utf8 не сказано нічого.

Подякували: 0xDADA11C7, leofun012

14

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:

.. void* .. щоби не плодити зайві сутності ..

Сутності існують поки ви працюєте з сурсами, а після компіляції сутностей не є. Юнікоду void* теж не поможе. Сміливо беріть котрийсь із символьних (або стрічкових) типів, тоді хоть якісь переваги типізації можна буде використати.

Подякували: 0xDADA11C71

15

Re: Вказівниковий сказ, функційна пошесть тощо

Бачу, спільноті подобаються технічні питання, хто б міг подумати, ліл. Тож буду продовжувати.

leofun01 написав:

Сутності існують поки ви працюєте з сурсами, а після компіляції сутностей не є

Сумнівне твердження, або ми один одного не розуміємо. Я наведу приклади зворотнього.
Наша зброя в цей момент вподобайка та комент, ліл.

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

16

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7 написав:
leofun01 написав:

Сутності існують поки ви працюєте з сурсами, а після компіляції сутностей не є

Сумнівне твердження, або ми один одного не розуміємо.

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

17 Востаннє редагувалося steamwater (03.03.2024 01:06:53)

Re: Вказівниковий сказ, функційна пошесть тощо

0xDADA11C7, додам до вже написаного наступне. Я не знаю iншого способу боротьби з довгими ланцюгами конструйованих типiв включаючи квалiфiкатори, анiж алiаси типiв. У С iснує лише typedef :

#include <iostream>

const unsigned arr_size_ = 4;

typedef char symbol_t;
typedef const symbol_t csymbol_t; // const бо будемо iнiцiалiзувати лiтералами
typedef csymbol_t *ptr_symbol_t;
typedef ptr_symbol_t (arr_symbol_t)[arr_size_];
typedef arr_symbol_t *ptr_arr_symbol_t;

void str_dup(ptr_arr_symbol_t array_list_name) {
    for(unsigned i = 0; i < arr_size_; ++i)
        std::cout << (*array_list_name)[i] << '\n';
}
// до речи, масив це константа i навряд ви зможете з ним щось зробити, а
// доступ до змiсту можна (i легше) одержати так:
void str_dup2(ptr_symbol_t *array_list_name) {
    for(unsigned i = 0; i < arr_size_; ++i)
        std::cout << array_list_name[i] << '\n';
}
int main() {
    arr_symbol_t array_list_name = { "papa", "mama", "sister", "brother" };
    str_dup(&array_list_name);
    cout << '\n';
    str_dup2(array_list_name);
    return 0;
}