LINUX.ORG.RU

Практика работы с массивом структур на СИ


0

2

Доброго времени суток! Задался вопросом как в программе лучше и правильней будет осуществлять доступ к данным в массиве. Массив исключительно динамический.

1. Через массив указателей.

struct T **t = malloc(size *sizeof(struct T*));
while (size) {
 t[i] = malloc(sizeof(struct T));
}

t[N]->...
2. Доступ по ссылке со смешением(не знаю как правильно назвать).
struct T *t = malloc(size * sizeof(struct T));
bzero( t, size * sizeof(struct T));

(t+N)->...

Может еще есть какие варианты?

Спасибо!


struct T *t = malloc(size * sizeof(struct T));

t[N] = ...;
Harald ★★★★★
()

Очевидно второе.

AIv ★★★★★
()

Оба варианта имеют право на жизнь. Второй быстрее инициализируется и доступ чуть быстрее (пара тактов, совершенно незаметно), но он, в отличии от первого, требует большой непрерывный кусок памяти. Так что всё зависит только от размера твоего массива. Если больше 10-20 Мб, советую всё-же первый вариант.

Dragon59 ★★
()

Зависит о размера структуры, от количества элементов, от характера доступа (последовательный/случайный, чтение/запись/удаление, запись куда, удаление откуда и сколько, необходимость перетасовывания элементов массива, realloc'и, ожидаемая наполненность).

В целом, второй вариант компактней (в плане отсутствия дополнительного массива с указателями, а также оверхеда в куче при аллокации структур по одной), быстрее для доступа как в плане отсутствия лишнего обращения по указателю (это действительно копейки), так и в плане дружественности к кэшу (это уже cерьёзнее, хотя ускорение будет заметно только при последовательном переборе элементов массива), проще в реализации (не нужно аллоцировать и освобождать отдельные элементы).

Из минусов - последовательному массиву потребуется непрерывный кусок памяти, которого может не оказаться на 32битной системе (но массив должен быть размером в сотни мегабайт), нельзя хранить указатели на элементы массива (при реаллокации они инвалидируются).

Вариант же с массивом указателей быстрее перетасовывать (например, сортировать) и реаллоцировать, а также (возможно) будет экономнее к памяти при удалении элементов (тогда как в массиве структур либо будут оставаться дырки, либо его придётся сжимать, а это копирование всего массива и поломанные индексы). Но может ты собираешься использовать массив как стэк (т.е. только добавлять в конец или удалять последний элемент) или кольцевой буффер, и эта проблема уже будет неактуальна.

А может у тебя в этом массиве будет полтора элемента, и имеет смысл использовать массив структур только потому что это проще.

В общем, вопрос ты не задал и ответа на него нет.

Может еще есть какие варианты?

Сотни их. Все структуры данных, фактически, потому что ты ни разу не сказал что тебе нужно. Список (если не нужен случайный доступ), список на массиве, массив указателей на пулы структур (экономия памяти), куча, деревья, хэш таблицы - что угодно. Можешь также рассмотреть транспонированное хранилище - не массив структур, а структуру массивов - это будет быстрее всего в случае обработки одного поля во всех элементах.

slovazap ★★★★★
()

2 — обычный одномерный массив. А вариант 1 какой-то кривой: ты по сути двумерный массив инициализируешь.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Dragon59

он, в отличии от первого, требует большой непрерывный кусок памяти

С чего ты взял? Ведро выделит N страниц памяти, а как они расположены — пофиг, программе будет казаться, что непрерывно, а на деле — хоть в разрядку.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Та не о том речь, страницы пусть как хотят лежат. Частое изменение блока большого размера на куче — не самая быстрая операция.

anonymous
()
Ответ на: комментарий от Eddy_Em

С чего ты взял? Ведро выделит N страниц памяти, а как они расположены — пофиг, программе будет казаться, что непрерывно, а на деле — хоть в разрядку.

man адресное пространство.

slovazap ★★★★★
()

Слышал про такой финт ушами в С. Последним элементом структуры делали массив указателей из 0 элементов на эту же структуру. Тогда если ты делаешь malloc(size * sizeof(struct T)), то у тебя будет что-то типа linked_list, потому-что первый элемент того самого массива будет указателем на следующую структуру в списке из-за того, что она сразу дальше в памяти лежит.

Такой вот грязный хак и я не уверен, что современный стандарт определяет это как-то иначе чем ud. Надо почитать, но идея прикольная, из 80х.

nanoolinux ★★★★
()
Ответ на: комментарий от Dragon59

Если больше 10-20 Мб, советую всё-же первый вариант.

О_О... Пора вылезти из криокамеры, все с точностью до наоборот. Ничего, что в кэш один хрен загрузка постранично идет? А вот за счет ХЗ какой локальности во втором варианте кэшируемость будет ни к черту, и производительность (при самом обычном обходе массива) просядет порядка так на два.

ВТорой вариант имеет право на жизнь только для очень-очень специфических задач. Даже в нормально реализованных списках память выделяется не поэлементно а крупными блоками. Не надо пытаться делать работу ОС и менеджера памяти, если не знаешь что точно делаешь и как вот это вот все работает (если задаешь такие вопросы/даешь такие ответы - точно не знаешь;-)).

AIv ★★★★★
()
Последнее исправление: AIv (всего исправлений: 1)
Ответ на: комментарий от anonymous

Частое изменение блока большого размера на куче — не самая быстрая операция.

А где ТС сказал что ему нужен realloc много-много раз?

AIv ★★★★★
()
Ответ на: комментарий от AIv

Тьфу ты, не второй вариант а первый (имеет право на жизнь...) Короче, не нажо выпендриваться, работайте с обычными массивами и будет Вам щастье;-)

AIv ★★★★★
()

1) это массив указателей. используется для того, что-бы сделать массив массивов, однако эти подмассивы можно изменять, и они могут быть разных размеров. Например это могут быть строчки текста разной длинны.

2) это вообще-то массив массивов называется. Например int a[4][4] это массив из четырёх массивов, каждый из которых в 4 целых. А так как ты не пишут, это забота компилятора(в x86 есть специальные команды, для прямого доступа к таким вещам. А ты НЕ даёшь компилятору их использовать).

других вариантов массивов нет. И быть не может.

drBatty ★★
()
Ответ на: комментарий от Eddy_Em

А, ну так realloc — понятное дело, долгий процесс (особенно если до хрена памяти гонять).

alloc тоже не быстро. Причём malloc(100500×9000) куда как медленнее, чем 100500×malloc(9000). Т.е. непрерывный кусок памяти выделять _дольше_, чем множество маленьких кусков. И страницы тебе не помогут. Подумай - почему. Если лень думать - попробуй, и потом подумай (только не забудь заюзать память, а то виртуальной можно хоть 100500гигов навыделять, току-то?).

drBatty ★★
()
Ответ на: комментарий от nanoolinux

Такой вот грязный хак и я не уверен, что современный стандарт определяет это как-то иначе чем ud. Надо почитать, но идея прикольная, из 80х.

насколько я знаю, компилятор считает, что всё имеет размер. Потому «массив из 0 эл-тов» у тебя будет иметь размер. Скорее всего - один указатель. Т.е. твой вариант тупо не сработает.

И конечно же это UB, ибо массив из 0 эл-тов невозможно использовать без выхода за границу. А это UB уже 30 лет, ЕМНИП.

drBatty ★★
()
Ответ на: комментарий от drBatty

Подумай - почему.

можно и Кнута почитать. Он доходчиво объяснил, почему тупили компы в 50х - фрагментация памяти. Много мелких дырок, а всунуть некуда, если х большой.

Да, у тебя «страницы», ну и что? Тебе легче от того, что физическая память фрагментированна не так, как виртуальная? У тебя есть метод перебросить Over9000 страниц, дабы закрыть дырки, и сделать одну большую, причём быстро и без переноса? Теоретически, ты можешь пофиксить TLB, а на практике? Тебя туда пустят? Код покажешь?

drBatty ★★
()

Действительно выбор наверно зависит от ситуации. В моем случае все проще. Массив создается 1 раз в момент запуска программы и он не меняется до завершения работы программы. Выбрал 3 вариант от Harald.

friniz
() автор топика
Ответ на: комментарий от drBatty

Причём malloc(100500×9000) куда как медленнее, чем 100500×malloc(9000). Т.е. непрерывный кусок памяти выделять _дольше_, чем множество маленьких кусков.

А у меня быстрее. ЧЯДНТ?

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define ELEMENTS	30000
#define BLOCK_SIZE	4096

char *malloc1() {
	char *p;
	for (int i = 0; i < ELEMENTS; i++) {
		p = malloc(BLOCK_SIZE);
		*p = i;
	}

	return p;
}

char *malloc2() {
	char *p = malloc(BLOCK_SIZE * ELEMENTS);
	for (int i = 0; i < ELEMENTS; i++) {
		*p = i;
		p += BLOCK_SIZE;
	}

	return p;
}

int main() {
	struct timeval tv1, tv2;
	
	gettimeofday(&tv1, NULL);
	malloc1();
	gettimeofday(&tv2, NULL);
	printf("malloc1: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));

	gettimeofday(&tv1, NULL);
	malloc2();
	gettimeofday(&tv2, NULL);
	printf("malloc2: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
}
$ ./test
malloc1: 324642
malloc2: 292944
$ ./test
malloc1: 322630
malloc2: 294004
$ ./test
malloc1: 329206
malloc2: 292570
AptGet ★★★
()
Ответ на: комментарий от AptGet

А у меня быстрее.

у тебя примерно одинаково.

ЧЯДНТ?

ты не учитываешь реальной ситуации - у тебя в тесте выделяется либо один большой блок, либо 30000 маленьких блоков ПОДРЯД, БЕЗ ПЕРЕРЫВОВ. Это тоже самое, на самом деле, и ни о какой фрагментации речи быть не может. IRL память выделяется не подряд, и не по порядку. Отчего и получается дырчатая карта памяти, в которую ничего не воткнуть, ибо много мелких дырок. Даже для выделения мелкого объекта придётся искать нужную дырку, что долго. И чем умнее алгоритм, тем дольше поиск. Ну а тупой алгоритм либо рухнет, либо назанимает 100500Мб реальной памяти, и тоже рухнет.

drBatty ★★
()
Ответ на: комментарий от drBatty

А и правда: увеличил в 10 раз размер блока:

./a.out 
malloc1: 36123
malloc2: 138425

./a.out 
malloc1: 37254
malloc2: 141251
почти в 4 раза медленнее получается выделять одним псевдокуском.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от drBatty
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define WASTE_ELEMENTS  100000
#define ELEMENTS                300000
#define BLOCK_SIZE              4096

char *waste_heap() {
        char *p;
        for (int i = 0; i < WASTE_ELEMENTS; i++) {
                p = malloc(BLOCK_SIZE);
                *p = i;
                if (i % 2)
                        free(p);
        }

        return p;
}

char *malloc1() {
        char *p;
        for (int i = 0; i < ELEMENTS; i++) {
                p = malloc(BLOCK_SIZE);
                *p = i;
        }

        return p;
}

char *malloc2() {
        char *p = malloc(BLOCK_SIZE * ELEMENTS);
        for (int i = 0; i < ELEMENTS; i++) {
                *p = i;
                p += BLOCK_SIZE;
        }

        return p;
}

int main() {
        struct timeval tv1, tv2;

        waste_heap();

        gettimeofday(&tv1, NULL);
        malloc1();
        gettimeofday(&tv2, NULL);
        printf("malloc1: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));

        gettimeofday(&tv1, NULL);
        malloc2();
        gettimeofday(&tv2, NULL);
        printf("malloc2: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
}

версия с фрагментированной кучей.

аллокатор glibc вполне справился:

[~]$ ./test
malloc1: 589051
malloc2: 554143
[~]$ ./test
malloc1: 599575
malloc2: 574536
[~]$ ./test
malloc1: 580707
malloc2: 551839

AptGet ★★★
()
Ответ на: комментарий от Eddy_Em

Не, там пример неудачный: аллокация сразу после освобождения

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#define WASTE_ELEMENTS  100000
#define ELEMENTS                300000
#define BLOCK_SIZE              4096

char *waste_heap() {
        int i;
        char *p[WASTE_ELEMENTS];

        for (i = 0; i < WASTE_ELEMENTS; i++) {
                p[i] = malloc(BLOCK_SIZE);
                *p[i] = i;
        }

        for (i = 0; i < WASTE_ELEMENTS; i+=2) {
                free(p[i]);
        }

        return p[1];
}

char *malloc1() {
        char *p;
        for (int i = 0; i < ELEMENTS; i++) {
                p = malloc(BLOCK_SIZE);
                *p = i;
        }

        return p;
}

char *malloc2() {
        char *p = malloc(BLOCK_SIZE * ELEMENTS);
        for (int i = 0; i < ELEMENTS; i++) {
                *p = i;
                p += BLOCK_SIZE;
        }

        return p;
}

int main() {
        struct timeval tv1, tv2;

        waste_heap();

        gettimeofday(&tv1, NULL);
        malloc1();
        gettimeofday(&tv2, NULL);
        printf("malloc1: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));

        gettimeofday(&tv1, NULL);
        malloc2();
        gettimeofday(&tv2, NULL);
        printf("malloc2: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
}
[~]$ ./test
malloc1: 492572
malloc2: 575672
[~]$ ./test
malloc1: 492178
malloc2: 572503
[~]$ ./test
malloc1: 489821
malloc2: 579386

таки да, поиск пустого куска тормозит

AptGet ★★★
()
Ответ на: комментарий от AptGet
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/mman.h>

#define WASTE_ELEMENTS  400000
#define ELEMENTS                100000
#define BLOCK_SIZE              4096

char *waste_heap() {
        int i;
        char *p[WASTE_ELEMENTS];

        for (i = 0; i < WASTE_ELEMENTS; i++) {
                p[i] = malloc(BLOCK_SIZE);
                *p[i] = i;
        }

        for (i = 0; i < WASTE_ELEMENTS; i+=2) {
                free(p[i]);
        }

        return p[1];
}

char *malloc1() {
        char *p;
        for (int i = 0; i < ELEMENTS; i++) {
                p = malloc(BLOCK_SIZE);
                *p = i;
        }

        return p;
}

char *malloc2() {
        char *p = malloc(BLOCK_SIZE * ELEMENTS);
        for (int i = 0; i < ELEMENTS; i++) {
                *p = i;
                p += BLOCK_SIZE;
        }

        return p;
}

char *malloc3() {
        char *p;
        for (int i = 0; i < ELEMENTS; i++) {
                p = mmap(NULL, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
                *p = i;
        }

        return p;
}

char *malloc4() {
        char *p = mmap(NULL, BLOCK_SIZE * ELEMENTS, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        for (int i = 0; i < ELEMENTS; i++) {
                *p = i;
                p += BLOCK_SIZE;
        }

        return p;
}

int main() {
        struct timeval tv1, tv2;

        waste_heap();

        gettimeofday(&tv1, NULL);
        malloc1();
        gettimeofday(&tv2, NULL);
        printf("malloc2: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));

        gettimeofday(&tv1, NULL);
        malloc2();
        gettimeofday(&tv2, NULL);
        printf("malloc1: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));


        gettimeofday(&tv1, NULL);
        malloc3();
        gettimeofday(&tv2, NULL);
        printf("malloc3: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));

        gettimeofday(&tv1, NULL);
        malloc4();
        gettimeofday(&tv2, NULL);
        printf("malloc4: %ld\n", (tv2.tv_sec - tv1.tv_sec) * 1000000 + (tv2.tv_usec - tv1.tv_usec));
}

WASTE_ELEMENTS = 400000

[~]$ ./test
malloc1: 21367
malloc2: 193148
malloc3: 206509
malloc4: 156608
[~]$ ./test
malloc1: 21464
malloc2: 193017
malloc3: 210229
malloc4: 147694

WASTE_ELEMENTS = 0

[~]$ ./test
malloc1: 192950
malloc2: 182022
malloc3: 202683
malloc4: 192483
[~]$ ./test
malloc1: 193976
malloc2: 183883
malloc3: 205844
malloc4: 193393

на самом деле, фрагментация тут не особо мешает поиску большого куска, а ускоряет поиск мелких. Плюс пейджфолты уже обработаны

AptGet ★★★
()
Ответ на: комментарий от drBatty

Абсолютно согласен. Я так не делал если что, читал когда-то о программистах 80х.

насколько я знаю, компилятор считает, что всё имеет размер.

А может 30 лет назад это было не так? Но у меня нет данных на этот счёт, спорить не стану.

nanoolinux ★★★★
()
Ответ на: комментарий от Eddy_Em

А теперь попробуй обойти это хозяйство, если как у ТС лапша из относительно маленьких структур;-)

AIv ★★★★★
()
Ответ на: комментарий от AptGet

на самом деле, фрагментация тут не особо мешает поиску большого куска, а ускоряет поиск мелких. Плюс пейджфолты уже обработаны

угу. В итоге мы всё равно возвращаемся к случаю из 60х, который Кнут рассматривал. Только аллокатор у нас не такой тупой, как 50 лет назад.

Но всё равно - страницы никак НЕ влияют на этот процесс выделения и освобождения. Ну разве что размер блока хорошо делать кратным размеру страницы, а не намного меньше (впрочем, для мелких там ЕМНИП свой аллокатор). Т.е. для процесса абсолютно наплевать, страничная организация, или непрерывная физическая.

drBatty ★★
()
Ответ на: комментарий от nanoolinux

А может 30 лет назад это было не так?

ЕМНИП да, этот твой грязный хак работал(на x86 во всяком случае). Хотя массив в 0 эл-тов это деление на ноль.

drBatty ★★
()
Ответ на: комментарий от drBatty

Т.е. для процесса абсолютно наплевать, страничная организация, или непрерывная физическая.

В системах без MMU трудно будет запустить новый процесс, если память уже фрагментирована старыми завершенными. Разве что какие-нибудь наркоманские техники перемещения блоков памяти уровня Win16

AptGet ★★★
()
Ответ на: комментарий от AptGet

В системах без MMU трудно будет запустить новый процесс, если память уже фрагментирована старыми завершенными.

ну я про один процесс. Со _своей_ личной памятью. Без MMU это будет DOS какая-то.

drBatty ★★
()
Ответ на: комментарий от drBatty

это идеома - без выделения памяти иметь возможность без приведения типов иметь дополнительный вариант работы с памятью начиная с определёного адреса как вектором элементов.

т.е масив нулевого размера идеома со смыслом чуть иным чем у обычного(больше нуля элементов) массива.

забавно , что в проприентарном языке имено этот баг - неожиданое поведение после создания масива нулевого размера как то находил.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

это идеома

не. Это грязный хак, а не идиома.

забавно , что в проприентарном языке имено этот баг - неожиданое поведение после создания масива нулевого размера как то находил.

задроты - они такие.

drBatty ★★
()
Ответ на: комментарий от drBatty

идИома -оборот речи, употребляющийся как некоторое целое, не подлежащий дальнейшему разложению и обычно не допускающий внутри себя перестановки.

http://ru.wikipedia.org/wiki/Идиома_(программирование) :

... Программная идиома — выражение, обозначающее элементарную конструкцию, типичную для одного или нескольких языков программирования. Идиомы могут быть зафиксированы в принятом стиле программирования или документации конкретного языка.

Знание идиом языка и правил их применения является одним из показателей свободного владения языком программирования.

...

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

Программная идиома — выражение, обозначающее элементарную конструкцию, типичную для одного или нескольких языков программирования. Идиомы могут быть зафиксированы в принятом стиле программирования или документации конкретного языка. Знание идиом языка и правил их применения является одним из показателей свободного владения языком программирования.

да я в школе учился, и в вике меня ещё не забанили. Не утруждайся. Лучше попытайся понять, что «ЛОЛ ЩИТО» это не идиома в русском языке, как и массив в 0 элементов в pure C. Это признак безграмотности и тупости.

drBatty ★★
()
Ответ на: комментарий от drBatty

Это признак безграмотности и тупости.

какая идиома(сочетания их покрывающая(если сочетание то тогда с указанием разбиения и где какая идиома наилучшая) область применения нулевого массива) заведомо(т.е на всей области применения) лучше(т.б всегда) нулевого массива?

речь про pure C на дистанции от начала 80ыx по сей денью

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

какая идиома(сочетания их покрывающая(если сочетание то тогда с указанием разбиения и где какая идиома наилучшая) область применения нулевого массива) заведомо(т.е на всей области применения) лучше(т.б всегда) нулевого массива?

указатель естественно.

речь про pure C на дистанции от начала 80ыx по сей денью

дык в том-то и беда, что там по сей день нормальные массивы не сделали и не сделают. Раньше надо было думать. А сейчас всё - поезд ушёл. Массивы вроде как есть, но на самом деле, их и нет вовсе. Из-за этой врождённой кривизны и стали возможны такие вещи, как массивы без эл-тов. Это баг ЯП.

drBatty ★★
()
Ответ на: комментарий от drBatty

указатель в отличии от массива(указатель на момент компиляции) требует под себя место в размере достаточном адрессации элементов в адрессуемой области(короткие длиные кошмар x86)

массив есть заявка на последовательность.

указатель есть заявка на хранение индекса некоторого хэша.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

указатель в отличии от массива(указатель на момент компиляции) требует под себя место в размере достаточном адрессации элементов в адрессуемой области(короткие длиные кошмар x86)

я в курсе. жи/ши пешы с И. Потому ты «ЩИТО» пишешь? И да, если ты в слове «адрес» пишешь две с, пиши и два д. Вот так: «address».

указатель есть заявка на хранение индекса некоторого хэша.

а массив в 0 эл-тов это заявка на то, что ты kr00toj ksaper?

drBatty ★★
()
Ответ на: комментарий от drBatty

если в альтернативе выбора дискуСии ты предпочитаеш казуистику орфографии ветке показа замены одного негодного инструмента набором годных то спасибо.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

если в альтернативе выбора дискуСии ты предпочитаеш казуистику орфографии ветке показа замены одного негодного инструмента набором годных то спасибо.

мы говорили за «идиому», про которую, как ТЫ сам сказал «Знание идиом языка и правил их применения является одним из показателей свободного владения языком программирования.». Ну согласись, что идиома «массив» в твоём примере применяется ИЗНАЧАЛЬНО НЕПРАВИЛЬНО (против правил и всякой логики). Массив элементов без элементов - типичная НЁХ. То, что оно когда-то где-то работало - ничего не доказывает. Как то, что кто-то где-то написал «ЩИТО», и ему за это ничего не было, мало того, читатели поняли, что хотел сказать написавший. Это не делает «ЩИТО» идиомой русского языка.

drBatty ★★
()
Ответ на: комментарий от drBatty

идиома не обязательно следует правилам.

имиома это клише имеющее для стороны употребившей и стороны при которой употребили устойчивое значение.

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

жаргон.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

идиома не обязательно следует правилам.

тогда она никак не может служить критерием «знания ЯП». Разве что ты знаешь, что это такое, понимаешь как работает, но не употребляешь IRL.

drBatty ★★
()
Ответ на: комментарий от drBatty

правила и правила.

первые то чем руководствуется компиляторописатель

вторые те которым учат при освоение языка.

идиомы(в прог) очевидно следуют первым( ибо иначе не попадут в маш код) и ортогональны правилам вторым (стиль и т.п)

из знания идиом косвенно следует (т.е кореляция положительна но причиность не обязательна) опыт(разнообразие )|(сознательное выведение всех(как можно более разнообразных) следствий из правил) работы с языком.

qulinxao ★★☆
()
Ответ на: комментарий от qulinxao

первые то чем руководствуется компиляторописатель вторые те которым учат при освоение языка. идиомы(в прог) очевидно следуют первым( ибо иначе не попадут в маш код) и ортогональны правилам вторым (стиль и т.п)

мне это далеко не очевидно. И как раз наоборот. Правила компиляторописателей тесно связаны с правилами «при освоении». Например правило стиля, ставить отступы вот в таком коде:

if(x == y)
    z++;
зачем тут нужен отступ перед z? Для того, что видеть, что вторая строчка прямо _зависит_ от первой. Для компилятора это очевидно, ибо первая строка для него не завершена, она не кончается ;. Но для читателя (и писателя) это видно не очень хорошо. Потому-то и ставят отступ.

В нашем случае мы имеем массив, т.е. непустое множество элементов. Пустой массив - это особый случай, который имеет смысл начального особого состояния в других ЯП (вроде php), но в pure C смысла не имеет, ибо массивы в нём НИКОГДА не меняются. Т.е. массив в N элементов остаётся таким НАВСЕГДА.

из знания идиом косвенно следует (т.е кореляция положительна но причиность не обязательна) опыт(разнообразие )|(сознательное выведение всех(как можно более разнообразных) следствий из правил) работы с языком.

делить на ноль не имеет смысла. Это логика. И даже если какой-то калькулятор умеет делить на ноль - то это просто кривой и глючный калькулятор. Его чинить надо, а не радоваться.

drBatty ★★
()
Ответ на: комментарий от drBatty

Массив элементов без элементов - типичная НЁХ

Таки это вполне обычная «идиома», такая же как и «пустой список» (nil в лиспах).

Поэтому в С++ у std::array есть метод empty:

#include <array>
#include <iostream>

int main()
{
    std::array<int, 0> xs {{}};

    std::cout << "begin = " << xs.begin() << std::endl
              << "end   = " << xs.end() << std::endl
              << "empty = " << xs.empty() << std::endl
              << "size  = " << xs.size() << std::endl;
}

Аналогично в Scala:

scala> Array()
res1: Array[Nothing] = Array()

scala> Array().length
res2: Int = 0

или в Agda:

open import Data.Nat

data Vec {a} (A : Set a) : ℕ → Set a where
  []  : Vec A 0 -- <- вот это
  _∷_ : ∀ {n} (x : A) (xs : Vec A n) → Vec A (1 + n)

open import Data.Empty

empty : Vec ⊥ 0 -- <- аналогично Array[Nothing] скалы
empty = []
quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от quasimoto

Аналогично в Scala:

а ещё в PHP. тоже empty. И что теперь? При чём тут Pure C, в котором невозможно изменить размер массива? И даже более того - эта константа, которую компилятор должен знать ДО компиляции. А в твоих пэхапэ много других, чудных и удивительных идиом.

drBatty ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.