Ответы пользователя по тегу C
  • Как считать из файла двоичные числа и перевести их в десятичные?

    @res2001
    Developer, ex-admin
    В простейшем случае примерно так же как и с текстовым файлом.
    Кроме того что:
    1. Окрывать файл нужно в двоичном режиме (по умолчанию в текстовом)
    2. Читать нужно сразу в массив соответствующего типа и размерности (подразумевается, что вы знаете какой тип двоичных данных в файле, а иначе нет смысла его читать). Читать по одному элементу то же не возбраняется.

    Если файл записан на машине с одной и той же архитектурой, то при таком подходе проблем не будет.
    Если же архитектуры (процессоры) разные, то может вмешаться порядок байт (big/little endian). Это нужно учитывать.
    Так же, если вы читаете файл в котором записаны данные разных типов (например какая-то структура), то может возникнуть проблема с выравниванием полей в структуре. https://stackoverflow.com/questions/4306186/struct...
    Думаю, что к вашей текущей задаче эти нюансы не относятся. Но знать, что двоичная чтение/запись это несколько сложнее аналогичных операций с текстом стоит.
    Ответ написан
  • Как правильней сделать это на си?

    @res2001
    Developer, ex-admin
    Правильно пишут. Вас не смущает два void* в callback?
    У вас функция принимает 2 параметра на что угодно, хреновая идея. Тут и будут ошибаться.
    На сколько я могу судить data - это пользовательские данные, тут void* без вариантов, но можно обозвать например userdata и описать, что userdata в калбэке и в init - это одно и то же и библиотека больше никак не использует этот указатель.
    На мой взгляд event вполне можно описать какой-то заранее определенной структурой. Или хотя бы у этой структуры должен быть единый заголовок (включающий тип события и возможно что-то еще общее для всех событий). Посмотрите, например, на адресные структуры в сокетах: struct sockaddr, struct sockadd_in, struc sockaddr_in6. Это не самая удачная реализация, на мой взгляд, но достаточно распространенная. Гораздо лучше реализованы объекты в питоне (имею ввиду исходный код самого питона на Си), принцип тот же, но реализация более понятна.
    callback может принимать указатель на заголовок структуры, затем его можно будет преобразовать в структуру для конкретного типа события. Хорошо бы для этого предусмотреть в реализации набор макросов.
    Общий посыл таков: нужно как можно реже использовать void*, можно его использовать только тогда когда без этого ну вообще никак не обойтись (а если обойтись, то это будет ну очень "дорого"). К сожалению в Си время от времени приходится использовать void*. В т.ч. для решения этой проблемы в плюсах придумали шаблоны.

    Я так и не понял, что означает type.
    По моему идентифицировать издателей/подписчиков лучше всего по имени. И пусть имя они сами себе выбирают.

    Где набор функций для добавления/удаления подписчиков?
    Ответ написан
  • Как передать массив списков в функцию Си (С++)?

    @res2001
    Developer, ex-admin
    int searchByHash(Stack* hashTable, int element)
    Ответ написан
    Комментировать
  • Зачем определению(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]) может срабатывать не корректно.
    Ответ написан