LINUX.ORG.RU

Контейнеры в C


2

3

Будучи сиплюсплюсником, меня давно интересует как те же задачи выполняются в C. Знаю, на ЛОРе есть множество приверженцев C, надеюсь они ответят на пару простых вопросов.

Я являюсь активным сторонником идеи «Алгоритм должен работать настолько быстро, насколько позволяет железо». С этой точки зрения C++ даёт потрясающие возможности из-за своей системы кодогенерации. Да, я имею в виду шаблоны.

Не будем зарываться в дебри boost'а, возьмём простую задачу - контейнеры.

Для примера я взял односвязные списки в GTK GSList и Qt QList. QList позволяет хранить как простые, так и сложные типы с конструкторами, деструкторами, типы с общими данными. При этом накладных расходов на выделение в куче не происходит, а при реаллокации выбирается нужный алгоритм в зависимости от QTypeInfo<T>, к примеру для int будет вызван memcpy(), а для QString оператор копирования. Кроме того блок данных заранее резервируется и для сложных типов вызывается placement constructor. Для удаления данных по указателям используется алгоритм qDeleteAll(from,to), который сам дёрнет нужные конструкторы. Беглый просмотр GSList показал, что в нём можно хранить только указатели, со всеми вытекающими накладными расходами на аллокацию/уничтожение памяти, ручное кастирование, слежение за утечками, фрагментацию памяти.

Аналогичная ситуация со связными списками GList и QLinkedList.

Это даже не затрагивая вопрос о типизации, в C++ компилятор сразу даст по рукам при попытке записать в контейнер неверный тип или с помощью неявного преобразования с explicit-конструктором.

Далее, счётчики ссылок и деструкторы.

К примеру, в C++ список строк будет выглядеть как QList<QString>, при этом можно забыть про внутреннюю структуру строки - конструктор, деструктор и операторы копирования сами занимаются подсчётом ссылок, разделением и уничтожением данных. Вернуть строку из функции проще простого:

QString foo()
{
    return listOfStrings.at( 5 );
}

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

В GTK я сходу нашёл GString:

struct GString 
{
  gchar *str;
  gint len;
};

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

Собственно, мне интересно каков принцип работы сишников в таких простых задачах. Наплевать на производительность, выделять всё в куче, делать глубокое копирование сложных структур, всегда следить за кастированием к нужному типу, писать для любой структуры деструктор и следить за его вызовом?

Или может GTK неудачный пример, тогда дайте ссылку на правильную C-библиотеку с контейнерами.

★★★★★
Ответ на: комментарий от Love5an

Судя по названию, LISP должен быть утилитой типа sed.

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

> Ну это потому что они тупые и с узким кругозором, скорее всего.

ну да - проще обозвать хэш-табличкой, хотя там хэш никаким боком не используется

ahonimous
()
Ответ на: комментарий от Love5an

> во всем цивилизованном мире называние ассоциативного массива именем всем известной функции

У меня такое ощущение, что когда в stl появился класс map; всем извесная функция с таким названием была еще нихрена ни всем известная.

shelA
()
Ответ на: комментарий от Love5an

> У тебя как у Степанова, да? Или просто шизофрения?

А что ни так со Степановым? На мой взгляд у него всё в шоколаде.

shelA
()
Ответ на: комментарий от ahonimous

Ну видишь, я на то хочу указать, что ублюдское именование «[hash_]map» используют только в C++, да Жабе(ну может и в каком подобном говне - Go, вроде бы, еще).
Map, в контексте CS и SE, это всем известная функция/операция, которая за сто лет до STL так называлась. А словари назывались словарями.

Reset тут как всегда пытается перевести разговор с ублюдочности C++ и тупости его создателей на стороннюю тему. Понятно, что ублюдочность любимого языка и тупость его создателей осознавать неприятно, но тем не менее.

Love5an
()
Ответ на: комментарий от shelA

Это тебе она была неизвестна. Да и сейчас, походу, тоже. Потому что ты книжек, и, наверное, даже статей, о программировании не читаешь, и вообще, тупой.

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

>ублюдочности C++

Ты принимаешь за аксиому утверждение, которое вообще то надо доказывать. Потрудись дать определение слова «ублюдочность», и доказать, почему оно применимо к C++.

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

Если лисп такой замечательный, то почему на нем никто не пишет?

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

>на нем никто не пишет

А мужики то и не знают! Вон оно как, оказывается. Пойду всем расскажу.

Love5an
()
Ответ на: комментарий от Reset

> В С++ эта функция называется transform, что более логично.

эээ... в С++ map называется transform? что же тут логичного?

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

2 Reset, shelA, ahonimous, namezys

Мне, как человеку воспитанному, не по себе наблюдать, как вы пытаетесь общаться с тем, у кого атрофировано элементарное уважение к людям. Большая просьба - не кормите. Пользы от этого «собеседника» ровно никакой, одно хамство.

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

> Это тебе она была неизвестна. Да и сейчас, походу, тоже. Потому что ты книжек, и, наверное, даже статей, о программировании не читаешь, и вообще, тупой.

О, ийлита облажалась и расстроилась?)

Ну канешно сокровенные знания воплощенные в понимание того что из себя представляет «всем известной функции» доступны исключительно только лисперам )))

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

> Ну канешно сокровенные знания воплощенные в понимание того что из себя представляет «всем известной функции» доступны исключительно только лисперам )))

чего же тут «сокровенного»?

http://en.wikipedia.org/wiki/Map_%28higher-order_function%29

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

Еще раз - если ты не знал, что такое map, и что это сто лет как всем известная операция, то это ты такой тупой, а не map никому не нужная и никому не известная.

Love5an
()
Ответ на: комментарий от korvin_

> чего же тут «сокровенного»?

Ты что всерьез думаешь, что я не в курсе (в вики тыкаешь)?))) Просто не хотел упоминать, что я еще и питонщик со стажем. Этот клоун просто на гавно изойдет, как прочитает это!)

shelA
()
Ответ на: комментарий от Love5an

What happens if I have bugs? If I have a pointer to an object, can it be invalid (be a random bit pattern, point to a dead object)? It can? The program will crash or worse? What about arrays of objects and out-of-bounds indexes? Crash or a modification of some other random object? You call that encapsulation? Bad.

What happens if I change/add/remove a private value, without changing the interface? All code using the class has to be recompiled? I bet you call that encapsulation, too. Bad.

I don't like C++ classes.

ahonimous
()
Ответ на: комментарий от korvin_

> питонщик со стажем — это звучит круто

лиспер со стажем конечно звучало бы в разы круче )

shelA
()
Ответ на: комментарий от korvin_

>> Кстати да, пусть дохнет с голоду и сваливает с топика.

ты его плохо знаешь...

Да не, нормально знаю. Это не первый логин на ЛОРе))) Клоун свою функцию выполнил, заполнил паузу перед ответным словом Мужика. Кстати чего-то он не торопится с выходом )))

shelA
()
Ответ на: комментарий от Reset

>>ой, если мы и free поставим, то вообще жопа будет ...

Тогда и в QList надо указателями делать для полноты ощущений.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от namezys

Ну у тебя получается примерно одинаково. Хз почему у меня есть разница, может быть из-за того, что у меня все либы GLib самосборные.

MuZHiK-2 ★★★★
()

кароче сдаеться мне что все тут тролли - такчто я замолкаю
жаль потраченого времени

ae1234 ★★
()
Ответ на: комментарий от MuZHiK-2

> Тогда и в QList надо указателями делать для полноты ощущений.

А лучше двойными указателями. Или тройными. Пока по скорости не сравняются.

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

еще в shared_ptr завернуть

и еще в какую нить каку

namezys ★★★★
()
Ответ на: комментарий от MuZHiK-2

А что у вас за сборка?

Может стоит как-то настроить компиляцию Qt

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

подброшу ещё кода :) чтобы QT не слишком сильно сливал, пришлось в С-шный код вернуть копирование строк и добавить обход полученного списка:)

#include <list.h>
#include <stdlib.h>
struct person_list {
	struct list_head lh;
	char *nick;
	int id;
};

int main() {
	int t;
	struct person_list pl;
	struct person_list *node;
	INIT_LIST_HEAD(&pl.lh);
	for(t=0;t<5000000;t++) {
		node=(struct person_list *)malloc(sizeof(struct person_list));
		if (node==NULL)
			exit(0);
		node->nick=strdup("Dick");
		node->id=t;
		list_add(&(node->lh),&(pl.lh));
	}
	list_for_each_entry(node,&pl.lh,lh) {
	    node->id+=2;
	}
	return 0;
}

описалово list.h см. http://isis.poly.edu/kulesh/stuff/src/klist/

P.S. там-же хеш таблицы

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

нам бы понять, что сделать с С++, чтоб glib его смог догнать

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

>Везде идет выделение на куче

В qt-примере память под person выделяется не на куче, а на стеке, но это не важно, потому что если хранить в QLinkedList'e указатели на person, программа работает еще быстрее.

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

> В qt-примере память под person выделяется не на куче, а на стеке

Вы хоть понимаете что такое стек? Везде память выделяется в куче.

если хранить в QLinkedList'e указатели на person, программа работает еще быстрее


Глупость.

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

Глупость.

Что глупость?

#include <QString>
#include <QList>

const QString dendy = "Dendy";

struct person
{
    person(const QString& a_nick, int a_id) :
        nick(a_nick), id(a_id) {}

    QString nick;
    int id;
};

int main(int argc, char *argv[])
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);
    QList<person> list;

    for (int i = 0; i < 1000000; ++i) {
        list.append(person(dendy, i));
    }
}
$ time ./qt 
real    0m0.242s
user    0m0.203s
sys     0m0.037s
#include <QString>
#include <QList>

const QString dendy = "Dendy";

struct person
{
    person(const QString& a_nick, int a_id) :
        nick(a_nick), id(a_id) {}

    QString nick;
    int id;
};

int main(int argc, char *argv[])
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);
    QList<person*> list;

    for (int i = 0; i < 1000000; ++i) {
        person* per = new person(dendy, i);
        list.append(per);
    }


}
$ time ./qt 

real    0m0.140s
user    0m0.087s
sys     0m0.050s

Кстати выше по треду было некорректное сравнение QList vs GList. QList это же не связный список.

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

Забыл очистку элементов, с ней «real 0m0.197s»

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

>Вы хоть понимаете что такое стек? Везде память выделяется в куче. [code=cpp] person pers(dendy, i); list.append(pers); [/code]

Где выделяется память под pers? На куче?

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

на стеке живет только временный объект

но вообще поведение - как на стеке

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

Глупость.

Что глупость?

Вот это вот:

если хранить в QLinkedList'e указатели на person, программа работает еще быстрее

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

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

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

если вызывать append, а не resize

ahonimous
()
Ответ на: комментарий от Dendy

QList это не связный список, а скорее аналог deque в stl.

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