1 Востаннє редагувалося 0xDADA11C7 (11.02.2022 06: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 03: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

Подякували: Chemist-i, 0xDADA11C7, leofun013

7 Востаннє редагувалося wander (09.02.2022 14: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 16: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.

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

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

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

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

0xDADA11C7 написав:

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

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

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

14

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

0xDADA11C7 написав:

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

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

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

15

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

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

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

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

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