Подтвердить что ты не робот

Каковы важные понятия в C, которые вы не учили у своих учителей?

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

Этот год станет их вторым годом обучения C (они должны знать, что такое указатель и как его использовать, но, конечно, это понятие еще не ассимилировано)

В дополнение к классическим материалам (структурам данных, классическим алгоритмам,...), я, вероятно, сосредоточусь на некоторых своих лекциях:

  • Создайте алгоритм (и напишите его в псевдокоде) перед его кодированием в C (подумайте перед кодированием)
  • Сделайте свой код доступным для чтения (комментарии, имена переменных,...) и
  • Указатели, указатели, указатели! (что это такое, как и когда использовать его, распределение памяти и т.д.).

По вашему опыту, каковы самые важные понятия в C, которые ваши учителя никогда вас не учили? На какой конкретный момент я должен сосредоточиться?

Например, следует ли представить их некоторым инструментам (lint,...)?

4b9b3361

Ответ 1

Использование ключевого слова const в контексте указателей:

Разница между следующими объявлениями:

 A)   const char* pChar  // pointer to a CONSTANT char
 B)   char* const pChar  // CONSTANT pointer to a char
 C)   const char* const pChar  // Both

Итак, с A:

const char* pChar = 'M';
*pChar = 'S'; // error: you can't modify value pointed by pChar

И с B:

char OneChar = 'M';
char AnotherChar = 'S';
char* const pChar = &OneChar;
pChar = &AnotherChar; // error: you can't modify address of pChar

Ответ 2

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

Например, понятие арифметики указателя было чуждо мне, пока я уже не использовал С++ в течение нескольких лет:

Примеры:

  • c [0] эквивалентно * c
  • c [1] эквивалентно * (c + 1)
  • Итерация цикла: для (char * c = str; * c!= '\ 0'; С++)
  • и т.д.

Вместо того, чтобы заставить учеников бояться использовать указатели, научите их правильно использовать их.

РЕДАКТИРОВАТЬ: Как мне было доведено до конца комментариями, которые я только что прочитал на другом ответе, я думаю, что есть и некоторая ценность в обсуждении тонких различий между указателями и массивами (и как поставить два вместе, чтобы облегчить некоторые довольно сложные структуры), а также как правильно использовать ключевое слово const в отношении объявлений указателей.

Ответ 3

Они действительно должны научиться использовать вспомогательные инструменты (то есть ничего, кроме компилятора).

1) Valgrind - отличный инструмент. Он феноменально прост в использовании и отлично отслеживает утечки памяти и повреждение памяти.

Это поможет им понять модель памяти C: что это такое, что вы можете делать, и что вы не должны делать.

2) GDB + Emacs с gdb-many-windows. Или любой другой интегрированный отладчик, действительно.

Это поможет тем, кто будет лениться, чтобы выполнить код с помощью карандаша и бумаги.


На самом деле не ограничивается C; здесь я думаю, что они должны учиться:

1) Как правильно написать код: Как написать неподдерживаемый код. Читая это, я обнаружил по меньшей мере три преступления, за которые я был виноват.

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

Вы говорите, что ваши ученики на самом деле не программисты (они инженеры). Итак, , они не должны делать сложные вещи, они должны сосредоточиться на четком кодировании.

2) STFW. Когда я начал программировать (я начал в Паскале, чем переехал на C), я сделал это, читая книги. Я провел бесчисленные часы, пытаясь понять, как это сделать.

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

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


В целом, C довольно простой язык для изучения. У них будет гораздо больше проблем с записью чего-либо дольше, чем нескольких строк, чем у них будут обучаемые независимые понятия.

Ответ 4

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

Также напоминая им, что C не является С++, Java, С# и т.д., это хорошая идея. Это происходит чаще всего, когда вы видите, что кто-то рассматривает char * как строку в С++.

Ответ 5

unsigned vs signed.

Операторы сдвига бит

Маскировка бит

Настройка бит

целочисленные размеры (8-разрядные, 16-разрядные, 32-разрядные)

Ответ 6

Ориентация объекта:

struct Class {
    size_t size;
    void * (* ctor) (void * self, va_list * app); // constructor method
    void * (* dtor) (void * self);                // destructor method
    void (* draw) (const void * self);            // draw method
};

(Источник кода)

Ответ 7

Переносимость - редко учится или упоминается в школе, но в реальном мире очень много.

Ответ 8

(опасные) побочные эффекты макросов.

Ответ 9

Используйте valgrind

Ответ 10

Инструменты важны, поэтому я рекомендую хотя бы упомянуть что-то о

  • Makefiles и как работает процесс сборки.
  • GDB
  • ворса
  • полезность предупреждений компилятора

Что касается C, я считаю важным подчеркнуть, что программист должен знать, что означает "поведение undefined", то есть знать, что может возникнуть будущая проблема, даже если она, похоже, работает с текущей комбинацией компилятора/платформы.

Изменить: я забыл: научите их, как искать и задавать правильные вопросы на SO!

Ответ 11

Используйте согласованный и читаемый стиль кодирования.

(Это также поможет вам в анализе их кода.)

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

Ответ 12

Знайте, что когда вы увеличиваете указатель, новый адрес зависит от размера данных, на которые указывает этот указатель... (IE, какая разница между char * и приращением без знака long *)...

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

Зная, как использовать GDB, отлично. Знать, как использовать valgrind, отлично.

Разработайте стиль программирования C... Например, я предпочитаю писать довольно объектно-ориентированный код при написании больших программ на C (как правило, все функции в конкретном файле .C принимают некоторую (1) конкретную структуру * и работают на нем... Я имею тенденцию иметь foo * foo_create() и foo_destroy (foo *) ctor и dtors...)...

Ответ 13

Понимание компоновщика. Любой, кто использует C, должен понимать, почему "static int x;" в области файлов не создается глобальная переменная. Упражнение написания простой программы, в которой каждая функция находится в своей собственной единице перевода и составляется отдельно, не выполняется достаточно часто на ранних этапах обучения C.

Ответ 14

Всегда активные предупреждения. В GCC используйте не менее -Wall -Wextra -Wstrict-prototypes -Wwrite-strings.

I/O сложно. scanf() - зло. gets() никогда не следует использовать.

Когда вы печатаете что-то, что не является '\n' -terminated, вам нужно сбросить stdout, если вы хотите его немедленно распечатать, например

printf("Type something: ");
fflush(stdout);
getchar();

Используйте указатели const, когда это возможно. Например. void foo(const char* p);.

Используйте size_t для хранения размеров.

Литеральные строки обычно не могут быть изменены, поэтому сделайте их const. Например. const char* p = "whatever";.

Ответ 15

  • Хранящаяся память может вызывать всевозможные странные ошибки.
  • Отладчики могут лгать вам.

Ответ 16

Я думаю, что общая идея кажется действительно хорошей. Это дополнительные материалы.

  • Отладчик - хороший друг.
  • Проверьте границы.
  • Убедитесь, что указатель фактически указывая на что-то до его использования.
  • Управление памятью.

Ответ 17

Надеюсь, что это не было опубликовано раньше (просто прочитайте очень быстро), но я думаю, что очень важно, когда вам нужно работать с C, это знать о представлении данных в машине. Например: числа с плавающей запятой IEEE 754, большие или малоконечные, выравнивание структур (здесь: Windows vs Linux)... Чтобы практиковать это, очень полезно сделать несколько головоломок (решение некоторых проблем без использования какой-либо функции, а затем printf для печати результата, ограниченного числа переменных и некоторых логических операторов). Также часто бывает полезно иметь базовые знания о том, как работает компоновщик, как работает весь процесс компиляции и т.д. Но особенно понимание компоновщика (без этого так сложно найти какие-то ошибки...)

Книга, которая помогла мне улучшить свои навыки на C и С++, была: http://www.amazon.com/Computer-Systems-Programmers- Рэндал-Bryant/дп/013034074X

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

Ответ 18

Учите их модульное тестирование.

Ответ 19

Как насчет общих рекомендаций?

  • Всегда предположить, что кто-то еще уже написал ваш код и что он оба свободно доступный в Интернете и лучше написанный и проверенный, чем все, что вы производить до истечения срока.
  • Возвращение раннего/Избегайте других предложений
  • Инициализировать все переменные
  • Одна страница для каждой функции в качестве ориентира (т.е. используйте более мелкие фрагменты кода)
  • Когда использовать переключатель, if-else if или хеш-таблицу
  • Избегайте глобальных переменных
  • Всегда проверяйте свои входы и выходы (я не доверяю своему собственному коду.)
  • Большинство функций должны возвращать статус

    [Для других: не стесняйтесь редактировать это и добавлять в список]

Что касается проверочных входов:

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

Подумайте о сторонах Guard как предупреждения и ошибки компилятора во время выполнения.

Ответ 20

Я не думаю, что вы должны преподавать инструменты. Это следует оставить преподавателям Java. Они полезны и широко используются, но не имеют никакого отношения к C. Отладчик - это столько, сколько они должны надеяться получить доступ. Много раз все, что вы получаете, это printf и/или мигающий светодиод.

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

Попросите их написать программы, которые наверняка будут переполнены и segfault, и после этого убедитесь, что они понимают, почему это произошло.

Стандартная библиотека также является C, если она их использует, и их программы умирают болезненно в ваших личных тестах из-за использования get() и strcpy() или двойного освобождения.

Заставить их иметь дело с переменными различного типа, endianness (ваши тесты могут выполняться в другой архитектуре), преобразование float to int. Заставьте их использовать маски и побитовые операторы.

то есть. научите их C.

Вместо этого я получил некоторую пакетную обработку в C, которая также могла быть выполнена в GW-BASIC.

Ответ 21

Это ключевое слово в C: volatile ََََ

Ответ 22

  • Проверьте границы
  • Проверьте границы,

    и, конечно,

  • Проверьте границы.

И если вы забыли одно из этих правил, используйте Valgrind. Это относится к массивам, строкам и указателям, но очень легко забыть о том, что вы действительно делаете при выполнении распределений и арифметики памяти.

Ответ 23

  • Когда язык заканчивается и начинается реализация: например, stdio.h является частью стандартной библиотеки, conio.h не так уж и похож,
  • Разница между undefined и реализацией, определяемой поведением, и почему такие вещи, как x = x ++, undefined;
  • Просто потому, что он компилируется, это не значит, что он прав;
  • Разница между приоритетом и порядком оценки и почему a * b + c не гарантирует, что a будет оцениваться до b или c;
  • "Он работает на моей машине" не приводит к поведению, указанному в стандарте языка: например, только потому, что void main() или x = x ++ дает вам результаты, ожидаемые для конкретной платформы, а компилятор не означает нормально использовать;
  • Притвориться, что вы никогда не слышали о получении();

Ответ 24

Учитывая их предысторию, возможно, хороший акцент на C для встроенных систем, включая:

  • Инструменты статического анализа (например, PC-Lint)
  • MISRA-C.
  • Воздействие нескольких процессоров (например, PIC, STM32) и компиляторов
  • Как отлаживать.
  • Проблемы в реальном времени, включая прерывания, сигналы отладки, простое планирование /RTOS.
  • Разработка программного обеспечения.

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

Ответ 25

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

Ответ 26

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

  • Отладчики - gdb, totalview,...
  • Статические/динамические анализаторы - splint, valgrind,...
  • Структуры модульного тестирования - CUnit, cmockery, Check,...
  • Управление документацией - Doxygen,...
  • Управление конструкцией - Make, memoize, fabricate,...
  • Метрики кода - CCCC, Understand, SLOCCount,...
  • Покрытие кода - gcov, LCOV,...

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

Ответ 27

Слишком много, чтобы назвать их всех. Некоторые из них специфичны для C; некоторые из них являются общими примерами лучших практик.

  • Научитесь использовать доступные инструменты
    • Система контроля версий. Каждый раз, когда он работает, проверьте его.
    • Инструменты Diff: diff, rdiff, meld, kdiff3 и т.д. Особенно в сочетании с RCS.
    • Параметры компилятора. -Wextra -Wall __attribute __ ((aligned (8))), как упаковать структуры.
    • make: создавать версии отладки и выпуска
    • отладчик: как получить и интерпретировать трассировку стека. Как установить контрольные точки. Как пройти через/над кодом.
    • Редактор: Скомпилируйте в редакторе. Откройте несколько окон, M-x tags-query-replace (показаны ли мои корни emacs?) И т.д.
    • теги cscope, kscope, [ce] или другие инструменты просмотра источников.
  • Защитите оборону. assert (foo!= NULL) в -DDEBUG; скраб пользовательских входов.
  • Остановить и выхватить огонь при обнаружении ошибки. Отладка проще, когда вы даете две строки после обнаружения проблемы.
  • Поддержание 0-предупреждения с помощью -Wextra и -Wall включен.
  • Не помещайте все в один огромный файл .cc файла.
  • Test. Контрольная работа. И еще тест. И проверьте эти тесты рядом с вашим источником. Потому что инструктор может вернуться и изменить требования после того, как он был включен один раз.

Ответ 28

Перейдите весь жизненный цикл программирования, включая то, что произойдет с вашим кодом после того, как вы закончите с ним.

  • Предварительные стадии планирования и немного о том, как искать существующий проект/существующий код, который вы можете использовать для уменьшения количества исходного кода.
  • Небольшой (базовый) обзор лицензий и как этот внешний код влияет на то, какие лицензии вы можете использовать и не можете использовать (и другие соображения, которые входят в лицензирование).
  • Параллельное управление версиями и управление версиями. Я бы сделал SVN/ Git, но каждому свое. Вы сохраните их ТАК БОЛЬШЕ времени, если вы представите их им сейчас, а не учитесь на работе.
  • Покажите им, какие возможности существуют для кода с открытым кодом (Google Code, Github и т.д.) и когда/как определить, подходит ли это или нет.

Ничто из этого не является C-специфическим, но я добавляю его, потому что лично я просто прошел "C для инженеров-электриков" в своем университете, и это все, что мне пришлось выяснить самостоятельно.

Ответ 29

Важное понятие в C, которое я не узнал из своего учителя:

Оператор * не означает "указатель на" (слева боковая сторона). Вместо этого оператор разыменования - точно так же, как это справа (да, я знаю, что это беспокоит для некоторых).

Таким образом:

int *pInt

означает, что когда pInt разыменовывается, вы получаете int. таким образом pInt - указатель на int. Или иначе: * pInt - это int - разыменованный pInt - int; Затем pInt должен быть указатель на int (иначе мы бы не получили int, когда он разыменовываются).

Это означает, что нет необходимости изучать более сложные наименования наименований:

const char *pChar

* pChar имеет тип const char. Таким образом, pChar является указателем к const char.


char *const pChar

* const pChar имеет тип char. Таким образом, const pChar является указателем до char (сам pChar является постоянным).


const char * const pChar

* const pChar имеет тип const char. Таким образом, const pChar является указатель на const char (сам pChar является постоянным).

Ответ 30

Отступы. Все учителя говорили, что код должен быть отступом, но никто не дал указания относительно отступов. Я помню, что код всех учеников был действительно путаным.