Ответы пользователя по тегу C
  • Зачем определению(definition) объявлять(to declares), если есть определение(definition), которое определяет(to defines)?

    @res2001
    Developer, ex-admin
    int val;
    Является одновременно и определением и объявлением.
    В данном случае происходит реальное выделение памяти под переменную.

    Объявление это
    extern int val;
    Оно говорит компилятору, что где-то есть переменная val типа int. В этом случае компилятор уже знает какие операции допустимо использовать с этой переменной, но память под переменную не выделяет. Реальный адрес переменной подставит линковщик, когда будет собирать исполняемый файл из нескольких единиц трансляции.
    Если в одной единице трансляции вы используете объявление переменной, то где-то (в другой единице трансляции или в этой же) вы должны обязательно сделать определение. Иначе линковщик не найдя определения выдаст undefined reference.
    Если же определять переменную в каждой единице трансляции, то линковщик выругается на redefinition symbol, т.е. несколько символов с одним именем (переопределение существующего символа).
    Если вам все таки нужны символы с одним именем в разных единицах трансляции, то вы должны объявлять их static. В этом случае будет использоваться локальная для данной единицы трансляции переменная с данным именем.

    С функциями все аналогично.
    Ответ написан
  • Стоит ли изучать СИ?

    @res2001
    Developer, ex-admin
    Вопрос используется ли вообще язык СИ где то в проектах?

    Странный вопрос. Возьмите любой проект где требуется скорость/низкое потребление памяти там будет Си.
    Список популярных репозиториев на Си на гитхабе за последний месяц: https://github.com/trending/c?since=monthly
    Ответ написан
    Комментировать
  • Как добавить в массив строк новую строку в си?

    @res2001
    Developer, ex-admin
    1.Выделить новый массив размером большим на 1 элемент чем прежний;
    2.скопировать в начало старый массив;
    3.удалить старый массив;
    4.последнему элементу присвоить указатель на новую строку,
    Первые 3 пункта выполняет realloc().

    Если операция добавления нового элемента в массив регулярная, то стоит над этим поработать дополнительно. Обычно в таких случаях выделяют не на 1 элемент больше, а например в 2 раза больше. Отсюда возникают дополнительные потребности в хранении количества занятых элементов, количества всех элементов и т.п.
    Операция динамического выделения памяти - достаточно затратная, поэтому стоит свести ее к минимуму.
    Ответ написан
    Комментировать
  • Какой смысл несет слово "имитация" в словосочетании "имитация передачи аргументов по ссылке"?

    @res2001
    Developer, ex-admin
    В Си нету такого понятия как "ссылка" (это понятие из С++).
    В С++ ссылки используют для изменения переменных на которые ссылается ссылка.
    В Си для этого используют указатель.
    Что бы еще больше имитировать ссылку, можно аргумент функции объявлять как константный указатель:
    void func(char * const str);
    Ответ написан
    Комментировать
  • Чем опасно переполнение статического массива в C?

    @res2001
    Developer, ex-admin
    Посмотрите на код ниже:
    char a[5];
    int b = 0;
    a[5] = 'a';

    И ответьте на вопрос:
    1.Куда произойдет запись в последней строке?
    2.Программист действительно хотел именно этого?
    Учитывая, что память под стеком уже выделена ОС, то никакой ошибки при этом не происходит. Ошибка проявится где-то дальше по ходу выполнения программы, где будет использовано значение переменной b или производных от нее. Отловить подобные ошибки бывает достаточно трудно, т.к. причина ошибки и место проявления ошибки может быть сильно разнесено по коду.

    С динамической памятью все примерно то же самое, за исключением того, что теоретически выделяется только запрошенное количество памяти, поэтому выход за пределы сразу же вызовет аппаратное исключение.
    Но практически это обычно не так, т.к. обычно ОС выделяет память минимум в 4Кб (размер страницы памяти), а дальше выделенная память поступает в распоряжение менеджера памяти (malloc/free). Поэтому будет ошибка или нет зависит от реализации менеджера памяти, от количества выделяемойпамяти и т.п.
    Если не произойдет исключения, то возможно повредятся (перезапишутся) данные менеджера памяти и дальнейшие операции с памятью будут не правильными. К чему это может в итоге привести - трудно предугадать, но ясно, что ни к чему хорошему.
    Так же возможно, что никакие данные не повредятся, это вероятно еще хуже - вы думаете что программа работает нормально, но в дальнейшем вносите изменения в код и все разваливается хотя вроде бы изменения корректные. Убираете изменения - все опять хорошо. Вы думаете, что проблема в изменениях, а на самом деле нет.

    Для поиска подобных ошибок обычно используют инструменты типа valgrind memcheck
    Ответ написан
    Комментировать
  • Какую IDE посоветуете для C( просто си, не плюсы!)? И с какой книги начать изучать программирование на языке С?

    @res2001
    Developer, ex-admin
    IDE: MSVS, VS Code, qtcreator.
    Я лично предпочитаю qtcreator, но там вам нужно будет еще как минимум cmake изучить, чтоб можно было проект компилировать из него, так что лучше MSVS на первых порах (или возможно VS Code).

    Из минусов микрософтовского компилятора - он не поддерживает ни один стандарт Си полностью. Т.е. ANSI С и то что микрософт посчитал нужным поддержать для себя любимых. На первых порах это будет не важно, но дальше захочется большего. Так что, возможно, для обучения лучше использовать mingw (msys2+mingw) и к нему прикрутить VS Code.

    Книга: Дейтелов "Как программировать на С"
    В книге по Си только 50%, дальше начинаются плюсы, что наверное и не плохо. Сам по себе Си - язык очень компактный и легкий.
    Ответ написан
    Комментировать
  • Куда податься со знаниями языка Си?

    @res2001
    Developer, ex-admin
    Кроме драйверов и ядра линукса Си востребован там где нужно максимальное быстродействие и/или минимальное использование памяти.
    Ответ написан
    Комментировать
  • Как исправить "Выражение должно иметь константное значение"?

    @res2001
    Developer, ex-admin
    То как вы используете массив называется VLA (Variable Length Array). Появилось только в стандарте С99. В С++ его нет в принципе.
    Чтоб использовать VLA нужно компилятору явно задать используемый стандарт. Для gcc: -std=c99 или -std=c11. C11 - стандарт 2011 года.
    Если вы используете компилятор от микрософт (в составе MSVS например), то могу вас опечалить - микрософт никогда не стремилась поддерживать стандарты Си. Поддержки VLA там нет и вряд ли будет в ближайшее время.

    Но вообще не рекомендуется использовать VLA без четкого понимания что это, как работает и к каким последствиям может привести. Например, в свое время в ядре линукс была целая компания по выкорчевыванию кода с VLA. Так что лучше воспользуйтесь динамическими массивами или статическими с константными размерностями.
    Ответ написан
    2 комментария
  • Как объяснить записи оператора if?

    @res2001
    Developer, ex-admin
    Cicici,
    Можно ли сказать что в первом случае if(x) будет истиной, если x не равно 0?

    Именно так.
    Второе выражение математически верно, но в программировании так не пишется. Хотя компилятор это прожует (см. ответ wisgest ), но результат выражения будет не тот, какой ожидается от математического выражения.
    Правильная запись:
    if (0 < x && x < 10)
    Ответ написан
    1 комментарий
  • Сколько ячеек памяти будет занято при инициализации указателя адресом литерала?

    @res2001
    Developer, ex-admin
    Если оба объявления внутри функции, то:
    • В случае массива на стеке будет выделена память только под данные, т.е. 6 байт. Возможно, строка продублируется в сегменте данных и компилятор вставит операцию копирования данных из сегмента данных на стек при инициализации переменной. Пишу возможно, потому что я не проверял факт дублирования строки, но считаю, что скорее всего это именно так и работает. Таким образом памяти будет выделено 12 байт: 6 байт на стеке и 6 байт в сегменте данных.
    • В случае указателя, будет выделена память под сам указатель размером в sizeof(void*) байт на стеке, данные будут лежать в сегменте данных, указатель будет инициализирован адресом строки в сегменте данных.

    Для обоих вариантов память в сегменте данных выделяется при старте программы загрузчиком ОС и освобождается только после завершения программы.
    Ответ написан
    Комментировать
  • Как перевести int в unsigned int?

    @res2001
    Developer, ex-admin
    Преобразование типов в Си делается так:
    int a = -103;
    unsigned int b = (unsigned int)a;

    Но надо понимать, что unsigned типы - это беззнаковые, т.е. unsigned int переменная может принимать значение от 0 до 4294967295, т.е. значение -103 в вашем случае не входит в область допустимых значений типа unsigned int.
    Когда вы делаете преобразование знаковых типов в беззнаковые вы должны четко понимать к чему это приведет.
    В двоичном представлении переменная a из моего примера принимает значение: 0xFFFFFF99
    Переменная b в двоичном представлении примет это же значение (0xFFFFFF99), но интерпретироваться оно будет уже как 4294967193.
    Обычно когда нужно преобразование знаковых в беззнаковые числа вы перед этим должны убедиться, что знаковое число строго >=0 и только потом преобразовывать. Отрицательные значения должны обрабатываться по другому.
    Хотя, конечно, могут быть и другие задачи, где не требуется отсекать отрицательные числа, а имеет значение только двоичное представление числа (например такой подход применяется при преобразовании порядка байт из little endian в big endian и наоборот).
    Ответ написан
    Комментировать
  • Как и где лучше изучать C?

    @res2001
    Developer, ex-admin
    Язык не сложный. Берите любой предложенный учебник (K&R - не учебник), ставьте компилятор и IDE и делайте примеры из учебника.
    Ответ написан
  • Что такое Си Runtime Library?

    @res2001
    Developer, ex-admin
    CRT - это реализация стандартной библиотеки Си/С++ для данного компилятора.
    ОС обычно пишутся на Си и если в ОС нет реализации функций из стандарта Си, то они реализуются в стандартной библиотеке. Все классы из stdlib С++ реализованы в CRT.

    CRT в VS может линковаться как статически так и динамически, в зависимости от опций компилятора. Линкуете статически - у вас распухает ваш исполняемый файл, динамически - пользователь должен предварительно поставить соответствующий vcredist (установку можно встроить в собственный инсталлятор). С точки зрения производительности оба варианта примерно одинаковы.

    Вы можете не использовать стандартную библиотеку в принципе (отключив ее опциями компилятора) и работать на прямую с ОС, но это накладывает много ограничений и вы фактически остаетесь с кастрированным вариантом ЯП, т.к. например в С++ даже стандартный new/delete, на сколько я знаю, реализованы в стандартной библиотеке и прямого аналога в ОС нет. Обычно этим никто не заморачивается из-за возникающих проблем, решение которых заметно увеличит время реализации проекта, разве что вам необходимо сделать исполняемый файл минимального размера, максимально быстро стартующий и без зависимостей.
    Ответ написан
    4 комментария
  • Как отправить аудиофайл по POST HTTP с помощью PulseAudio?

    @res2001
    Developer, ex-admin
    На сколько я знаю pulseaudio сама не умеет читать звуковые файлы.
    Она может только писать в (play) или читать из (record) звукового устройства аудио данные.
    Из аудио файла данные нужно читать другими средствами. Например можно использовать libsndfile. Прочитанные данные уже можно подавать для воспроизведения в pulseaudio.
    Как-то так.

    Я сам еще не успел плотно познакомиться с pulseaudio, но скоро предстоит этим заняться, так что я пока изучаю тему. libsndfile - достаточно простая библиотека, поддерживает несколько широко распространенных форматов аудио файлов (mp3 - не поддерживает по лицензионным соображениям).
    Ответ написан
    Комментировать
  • Посимвольный i/o vs i/o в строку (буфер)?

    @res2001
    Developer, ex-admin
    Это верно!
    Особенно заметно, когда нужна обработка больших файлов.
    Избегать или нет - зависит от ситуации. Если скорость не важна, а файл не большой, то можно не заморачиваться.

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

    На больших файлах вместо собственной буферизации можно использовать механизм "файлов проецируемых в память", предоставляемый ОС.
    Ответ написан
    Комментировать
  • Ресурс для практики указателей в Си?

    @res2001
    Developer, ex-admin
    Сделайте, например, транспонирование произвольной матрицы на указателях.
    В этой связи, так же, интересно будет поэкспериментировать с указателями на массив переменной длины. Эта тема прямо вытекает из VLA, которые есть в C99, но до сих пор не поддерживаются некоторыми компиляторами (msvc). В С++ VLA нет.
    Ответ написан
    Комментировать
  • Как клонировать двумерный динамический массив строк?

    @res2001
    Developer, ex-admin
    for(int step = 0; mysql.array[step]; step++)//очищаю каждый элемент массива
            free(mysql.array[step]);

    1.Цикл завершается на первом встреченном пустом элементе.
    Судя по логике у вас могут быть пустые строки, под которые память не выделилась, а затем снова могут идти заполненные строки.
    2.Кроме того после выделения памяти для mysql.array рекомендую сразу ее обнулять. malloc не зануляет выделенную память - в ней лежит мусор, используйте calloc или memset. Из-аз этого проверка в цикле выше (mysql.array[step]) может срабатывать не корректно.
    Ответ написан
  • Чем принимать REST API HTTPS запросы?

    @res2001
    Developer, ex-admin
    Т.е. вам нужен HTTP сервер на Си.
    libonion - только Линукс, SSL есть.
    haywire - кросс-платформенно, по моему он не умеет SSL.
    Ответ написан
    Комментировать
  • Почему в VS программа на СИ работает неправильно, хотя в онлайн компиляторе все в порядке?

    @res2001
    Developer, ex-admin
    У вас куча операций с файлами, любая может вернуть ошибку. У вас в коде нет ни одной проверки ошибок.
    Были бы проверки ошибок, возможно это вам бы помогло.
    В reverseOrder() - используется рекурсия, по тексту заданий я вообще не понял, где там можно было бы применить рекурсию. Возможно ошибка где-то тут.
    В 1.3 вы странно вычисляете count - strlen() возвращает количество символов уже без учета нулевого символа. Зачем вы еще вычитаете 1?

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

    @res2001
    Developer, ex-admin
    Немного усложните программу, например написав парочку функций, которые определяют еще несколько переменных и что-нибудь в них пишут. В main() вызовите эти функции, а затем объявите test и выведите его значение. Вот тогда у вас гарантированно образуется мусор. При компиляции, нужно еще предотвратить оптимизацию компилятором (опция -O0), а то он может выкинуть вызовы функций, если посчитает, что они не влияют на дальнейшее выполнение программы.

    Что бы понять происходящее, почитайте как работает стек.
    Автоматические переменные определяются на стеке. Одно и то же место на стеке в разное время выполнения программы могут занимать разные переменные. Если вы не инициализируете автоматическую переменную, то она принимет то значение, которое уже лежит в области памяти, которую она занимает. Из-за того, что переменные имеют разный размер, то часто, например текущая 4 байтовая переменная (int) может занимать память, в которой до этого были 2 двух байтовые переменные, или любые другие вариации.
    Стек выделяется при загрузке программы (или при старте потока). В ходе выполнения программы стек не освобождается и не перевыделяется, поэтому мусор (данные от старых автоматических переменных) в нем образуется постоянно. При выходе автоматической переменной из области видимости фактического освобождения памяти не происходит - просто изменяется указатель на стек (регистр sp), т.е. при этом не происходит даже обращения к памяти. Поэтому все ранее сохраненные в стеке значения в памяти остаются. И когда вы определяете новую переменную без инициализации, она занимает ранее освобожденное место и принимает какое-то значение (мусор), которое ранее было сохранено в эту область памяти.
    Ответ написан
    Комментировать