Общее программирование C
Прежде всего, я начинающий программист (еще многому научиться).
В одном из моих небольших школьных проектов я написал стек для структуры. Теперь у меня немного другая структура, и мне нужен стек для этого тоже.
Должен ли я написать другую структуру данных [stack] (очень похожую на начальную) или попытаться выполнить некоторое общее программирование...
Знаете ли вы какие-либо хорошие общие стратегии программирования (онлайн-ресурсы в конечном итоге) в C? Я использовал google, но я не нашел ничего удовлетворительного, так как большинство результатов касаются стратегий на С++.
Спасибо!
ПОЗЖЕ ИЗМЕНЕНИЯ:
После некоторого чтения и переживания, в конце концов, я нашел два решения для своей проблемы.
Я документировал их здесь: http://andreinc.net/2010/09/30/generic-data-structures-in-c/. Эта статья может содержать ошибки или неточности, но она суммирует то, что я знаю до сих пор.
Ответы
Ответ 1
Нахождение общих черт и создание абстракций - одно из самых ценных навыков для программиста. Поскольку вы все еще учитесь, я предлагаю вам сделать следующее:
(1) Реализовать стек для этой другой структуры. Да, это двойная работа, но на вашем этапе рассчитывается каждая рабочая программа. Создает опыт.
(2) Сравните программы. Каковы их части? Каковы части, которые отличаются? Ваша цель - отделить части, которые являются общими для разных частей. Каковы средства, которыми эти две группы используют для общения? Части, которые у них есть, входят в одну часть вашей системы (stack.h/stack.c), разные части, которые входят в свои собственные файлы (account.h/c, person.h/c и т.д.), А часть, в которой вы их объединяете, должна включать в себя stack.h и параметрирующий объект.
(3) Попытайтесь найти все возможные способы, которыми вы знаете, что язык предлагает, что вы можете использовать для реализации абстрактных структурных функций. Поначалу всегда кажется, что существует только один способ, но для каждой нетривиальной проблемы существует несколько подходов. В случае стека, например, с использованием стандартного C, zou может использовать указатели void, вы можете использовать макросы препроцессора, вы должны изучить вставку маркеров, вы можете использовать указатели на функции плюс указатели на структуру и т.д.
(4) Реализовать как можно больше из них. Опять же, это для обучения. C имеет так много ловушек, и чем раньше вы сталкиваетесь с ними, тем лучше.
(5) После того, как вы перечислили и внедрили все эти разные подходы, вы должны их оценить: какой из них проще всего использовать? Какой из них проще всего реализовать? Какой из них самый быстрый? Какой из них проще всего отлаживать?
Ответ 2
Я не делаю много взлома C, но я думаю, что путь к этому - void*
.
Итак, просто перепишите свой стек push/pop void*
вместо some_struct*
. Это становится вашей проблемой, чтобы сохранить правильные типы, но это только цена, которую вы платите за использование такого низкоуровневого языка программирования.
* Не означает, что это плохо.
Ответ 3
Я считаю, что абстракция в основном в глазах программиста. Хороший программист может видеть шаблон в простых утверждениях, даже на низкоуровневом языке, таком как C. Языки и их синтаксис, безусловно, могут помочь, но то, как в конечном итоге пишутся операторы и выражения, несколько отличает хороших программистов от плохих. Тем не менее, как это поможет вам? Ну, моя цель - познакомиться с конструкциями в C, чтобы вы знали их, когда видите их, и void*
как упоминает Кевин Монтроуз, является обычной. Стратегии, которые я считаю хорошими, это думать о stdlib
, как там все решается? и отражать в отличном коде, когда вы видите некоторые. Т.е. в stdlib
используется ноль (0) для представления ОК. Или подумайте, насколько хорошо файловый дескриптор работает со всеми функциями read
, write
т.д. Независимо от их происхождения (сокет, файл, канал и т.д.). Этот ТАК вопрос (ссылка) имеет несколько хороших ссылок на отличный код для чтения.
(источник: skitch.com)
Изображение из Thinking Forth, великой старой книги по программированию независимо от языка.
Ответ 4
Для производственного кода я обычно предпочитаю С++. Даже если вы не планируете изо всех сил использовать OO, дженерики и метапрограммирование, вы можете использовать С++ как лучший C (в данном случае просто получить std:: stack бесплатно).
Если вам нужно использовать C, попробуйте сделать его простым и сделать прагматичный выбор на основе ваших конкретных обстоятельств. Например, если вы точно знаете, что ваш стек ограничен каким-то небольшим лимитом, а данные, которые вы держите, просты, то ваш код стека может быть таким же простым, как stack[tos++] = x;
и return stack[--tos]
без необходимости использования библиотеки многократного использования. Ответы, предлагающие библиотеку на основе void*
, также подходят при разных обстоятельствах. С++ std:: stack в значительной степени решает эту проблему раз и навсегда; C не совсем дает вам эту роскошь.
Ответ 5
Я приведу здесь пример общего программирования в C. Хотя его не алгоритм структуры данных, но, тем не менее, он может дать вам некоторые подсказки по этому вопросу. Надеюсь, это поможет вам.
Ниже приведен алгоритм общей реализации алгоритма сортировки пузырьков. Я использую указатели void и передаю данные для работы на уровне байтов. Также я использую внешнюю функцию сравнения, которая является единственным кодом, который знает тип сортируемых данных. Эта функция сравнения передается в качестве параметра алгоритму сортировки.
Еще один важный момент заключается в том, что главный алгоритм должен знать размер в байтах обрабатываемых данных, чтобы знать, сколько байтов необходимо для перемещения этих данных в новую позицию в массиве. Он также создает буфер для хранения одного экземпляра этих данных (переменная k в функции bubleSort).
static int compare(void *menor, void *maior)
{
int *pMenor = (int *)menor;
int *pMaior = (int *)maior;
return ( *pMenor > *pMaior );
}
void bubleSort(void *base, int bWidth, int len, int (*func)(void *key, void *data))
{
int i, j;
char *k=0;
char *bPtr = (char *)base; //Points to the beginning of the array
char *pi, *pj;
k = (char *)malloc(bWidth); //Creates a new var with the size of the data type
if(!k)
return;
for(i = 0; i < len; i++)
{
pi = (bPtr + (i*bWidth));
for(j=i+1; j < len; j++)
{
pj = (bPtr+(j*bWidth));
if( func((void *)pi, (void *)pj) )
{
memcpy(k, pi, bWidth);
memcpy(pi,pj, bWidth);
memcpy(pj, k, bWidth);
}
}
}
free(k);
}
int main()
{
int vet[5] = {4, 1, 3, 5, 2};
bubleSort((void *)vet, sizeof(int), 5, compare);
return 0;
}
Ответ 6
C - довольно низкоуровневый язык программирования, дающий вам лишь небольшую абстракцию над машиной, на которой работает код, но почти ничего не абстрагируется от языковой точки зрения. С++ имеет шаблоны для общего программирования, но в C у вас нет чего-то подобного.
Лучшее, что вы могли бы сделать, это написать ваши структуры данных, чтобы всегда использовать void*
и оставлять каждое выделение/освобождение/кастинг вызывающему. Это беспорядочно и подвержено ошибкам. Тем не менее.
Ответ 7
В то время как большая часть рекомендаций здесь является звездной (я призываю вас попробовать множество разных методов для получения опыта), я рекомендую использовать С++. Используйте шаблон стека, чтобы создать реализацию, а затем используйте extern "C", чтобы создать набор наборов функций C api для использования.
вам понадобится
o constructor to create the object
o a destructor to destroy the object
o a push function
o a pop function
o an "is_empty" function.
Последние 3 функции будут иметь указатель на объект в качестве первого (void *) параметра.
Внутри функции, которая указала бы указатель на тип объекта стека, затем используется в
нормальный режим С++.
Попытка этой реализации поставит еще одну стрелу в ваш колчан, чтобы вы могли охотиться на большую игру.