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

    @res2001
    Developer, ex-admin
    Если количество цифр фиксировано, то просто перечислите в формате scanf сразу их все.
    Если количество цифр может меняться, то ...
    Вводить как строку и потом парсить ее с помощью strtol, кода будет, конечно, больше, чем сейчас.
    Строку вводить в какой-то достаточно большой буфер длинной в 1 или 4 кб.
    При превышении буфера можно выдавать ошибку.
    Или ввод оформить в цикл, на случай, если буфер заполнится, а Enter пользователь еще не нажал. Выход из цикла по нахождению символа '/n' в буфере. В таком случае буфер можно делать меньше. Но тут возникает дополнительная проблема с неоконченным вводом, т.е. пользователь вводит число 123456 и допустим 123 прилетело в конец прошлого буфера, а 456 в начало следующего. Это надо будет решать в коде.

    Есть проблема с фиксированным размером массива.
    Если нужен произвольный размер входных данных, то можно, предварительно выделять массив какого-нибудь начального размера, если в ходе парсинга размер массива превысится, то сделать realloc увеличив размер массива в 2 раза. Ну и надо отдельно хранить количество реально заполненных элементов массива.
    А вообще, массив может оказаться не нужен, если взглянуть на задачу целиком.
    Ответ написан
    1 комментарий
  • Лучше ли использовать enum для цвета нежели struct?

    @res2001
    Developer, ex-admin
    Как я понимаю через выше aligment тем лучше?

    Нет. Это бред. Тут не применимы термины лучше/хуже.

    Alignment (выравнивание) - это просто свойство типа данных.
    enum по умолчанию имеет тип int, соответственно и все его свойства наследуются. В С++ (с версии 17, если не ошибоаюсь) можно самому выбрать тип enum. Для выравнивания обычно действует простое правило - тип должен быть выровнен в памяти на границу кратную его размеру. Например у int размер 4 байта (обычно), и выравнивание должно быть по границе 4 байт. у char/uint8_t - размер 1 байт, соответственно ограничений по выравниванию нет, т.е. выравнивание по границе байта. Тип SDL_Color - это, видимо, структура, состоящая из 4 uint8_t (или что там в SDL используют для unsigned char), выравнивание для структуры берется из типа ее первого поля (если аттрибутами не задано другое).
    Используй типизированный в uint8_t enum и будет тебе счастье.
    https://en.cppreference.com/w/cpp/language/enum

    Кстати, выравнивание, это не ограничение С++ - это ограничение процессора на аппаратном уровне. Правда почти все современные процессоры уже не имеют этого ограничения и нормально жуют не выровненные типы, но при этом операции выполняют медленнее, чем с выровненными. Фактически процы просто научились "маскировать" это ограничение. Поэтому ограничение в языке осталось.
    К тому же все еще есть процессоры, где требование выравнивания действует, но это в основном что-то из разряда микроконтроллеров. На таких процессорах использование не выровненных типов приводит к ошибке шины и вылету программы.
    Обычно тебе не нужно думать про выравнивание, т.к. компилятор об этом позаботится за тебя. Но бывают ситуации, когда программист должен это учитывать. Например когда ты читаешь поток бинарных данных из сети или из файла, и данные в потоке - это что-то сложнее, чем байты и символы.
    Ответ написан
    1 комментарий
  • Почему GCC не видит встроенную атомарную операцию?

    @res2001
    Developer, ex-admin
    Почему не использовать функции из стандартной библиотеки?
    https://en.cppreference.com/w/c/atomic/atomic_comp...
    Ответ написан
  • Как конвертировать из DEC в HEX ASCII?

    @res2001
    Developer, ex-admin
    Раз уж вы используете для преобразования в число strtol, то для обратного преобразования вполне можно использовать snprintf(asciiHex, sizeof(asciiHex), "%X", decVal).
    Хотя эти операции достаточно простые, можно было бы легко реализовать самому оба варианта.

    strtol ждет в первом параметре строку с завершающим нулем. У вас завершающий ноль в asciiHex отсутствует.
    Kolchislo как объявлена? Это строка с завершающим нулем? Возможно стоит использовать strcpy, а не memcpy.
    Зачем вам промежуточный массив asciiHex? Почему в strtol не использовать сразу kolchislo?
    Ответ написан
  • Как устранить ошибку конверсии при использовании битовых полей?

    @res2001
    Developer, ex-admin
    У вас ошибка появляется из-за опции -Werror=conversion компилятора. Она заставляет компилятор генерировать ошибки при любом неявном преобразовании, в котором есть вероятность потери данных или изменении значения. А в этой операции такая вероятность есть по формальным признакам.
    Тут можно либо убрать опцию, либо, применить явное преобразование к unsigned int:20

    Согласен с jcmvbkbc по поводу отказа от битовых полей.
    Хотя опция -Werror=conversion добавит проблем и без битовых полей, но, с другой стороны, она заставляет глубже понимать, то что вы написали.
    Ответ написан
    Комментировать
  • Как именно гарантируется выделения n байт памяти библиотекой stdint.h?

    @res2001
    Developer, ex-admin
    В Си, да и в С++, стандартные целочисленные типы, поддерживаемые компилятором - это char, short, int, long long и их беззнаковые братья. Стандарт действительно не фиксирует размеры стандартных целочисленных типов. Это сделано потому что стандарт описывает абстрактный язык, который должен компилироваться для разных платформ.

    Компилятор же компилирует программу под конкретную платформу с конкретными соглашениями по типам. Ему заведомо известны размеры стандартных типов на данной конкретной платформе. Стандартная библиотека так же обычно пишется под конкретный компилятор и конкретную платформу.
    Так что (u)intX_t - это всегда define над стандартными типами. И ничего странного в этом нет.
    Ответ написан
    Комментировать
  • Почему в Си после main() не ставят ;?

    @res2001
    Developer, ex-admin
    В Си различают определение функции и предварительное объявление функции (он же прототип).
    Точка с запятой ставится только в случае предварительного объявления функции.
    Например:
    int func(int a, inb);     // предварительное объявление
    int func(int a, inb) { return a+b; }       // определение функции

    В вашем примере main - это определение функции - точка с запятой не ставится.
    Ответ написан
    Комментировать
  • Как настроить CMake в Clion для stm32 для подключения библиотек?

    @res2001
    Developer, ex-admin
    Попробуйте в include_directories() указывать полные пути. Стандартная переменная cmake ${CMAKE_CURRENT_SOURCE_DIR} содержит путь где лежит текущий файл cmakelists.txt, ее можно использовать для генерации полного пути.
    Обычно, если в проекте используется иерархия каталогов, то в каждом каталоге с исходниками лежит свой собственный cmakelists, а подключается он к нижележащему через add_subdirectory(). Собирайте в каждом каталоге свою цель (статическую библиотеку), потом эти цели указывайте в зависимостях для основной цели.
    Ответ написан
    Комментировать
  • В чем разница записи массива через указатели?

    @res2001
    Developer, ex-admin
    Приоритет операции * выше, чем у -
    Ответ написан
    Комментировать
  • Что и почему лучше подключить в C++? math.h или cmath? stdio.h или cstdio?

    @res2001
    Developer, ex-admin
    В С++ используют вариант где не указывают расширение .h в include. С расширением это для исходников на Си.
    Впрочем, как вы верно заметили, в плюсах это то же работает. Обычно внутри <cxxxx> просто делается #include <xxxx.h>
    Ответ написан
    Комментировать
  • Как подключить файл с помощью макроса к другому файлу?

    @res2001
    Developer, ex-admin
    У вас в вопросе 2 варианта main.
    В первом будет ошибка, во втором нет при сборке командой:
    gcc main.c function1.c

    Чтоб в первом случае не было ошибки нужно собирать командой:
    gcc main.c
    т.к. function1.c включается в main.c с помощью include и его не нужно повторно давать компилятору.
    Эта команда для сборки второго варианта main не подойдет.
    Ответ написан
  • Не могу понять почему не работает USER тред?

    @res2001
    Developer, ex-admin
    Видимо потому что main заканчивается и программа завершается вместе со всеми потоками.

    Вообще не понятно, что ты этим хотел сказать. Где вызов swapcontext? Без него стеки псевдопотоков не используются и переключение контекстов не происходит. Зачем они тогда тут нужны?
    В прошлом варианте с таймером, по крайней мере, была идея и было понятно, чего ты хотел добиться. Хоть оно и не работало.
    Ответ написан
  • Как решить проблему с Segmentation fault?

    @res2001
    Developer, ex-admin
    Ошибка выполнения stat для файла: No such file or directory

    Потому что:
    strcpy(filename, "mntEntry->mnt_dir");
    Вы записываете в filename строку "mntEntry->mnt_dir", а не каталог.
    Добавить затем слэш в конец можно так: strcat(filename, "/");
    Или вручную:
    size_t len = strlen(filename);
    filename[len] = '/';
    filename[len + 1] = '\0';

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

    Есть еще момент. В линукс есть константа PATH_MAX она задает максимальный размер пути к файлу. Рекомендую ваши буфера для хранения путей делать размером PATH_MAX.
    Ответ написан
  • Почему после ассемблера учить Си легче?

    @res2001
    Developer, ex-admin
    Утверждение пожалуй верно.

    Лично на мой взгляд это из-за того, что и в ассемблере и в Си есть указатели. Причем в ассемблере ничего кроме указателей для работы с памятью нет (имею ввиду именно оперативную память, а не регистровую или ПЗУ) и вам волей-неволей на ассемблере придется понять, что это такое и как реально работает память в компе. И с этими знаниями очень легко уже понять указатели в Си, т.к. это суть одно и то же, только синтаксис разный.

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

    Если вам для чего-то нужен ассемблер, то видимо, стоит с ним разобраться. Но разбираться с ассемблером для изучения Си - это какой-то садомазохизм. Си довольно простой ЯП. Ассемблер сам по себе то же не сложен, но что бы на нем что-то писать нужно хорошо знать и понимать архитектуру конкретного процессора.

    У издательства ДМК пресс есть книжки по ассемблеру (в т.ч. по АРМ) и по архитектуре компьютеров, можете посмотреть у них на сайте, там же можно и купить. Что плохо, они не делают скидку на электронные книги, как многие другие.
    Изучать нужно ассемблер под ту платформу, на которой вы сможете писать тестовые программы. Обычно это х86, но при наличии, например, малинки или апельсинки можно и АРМ попробовать.
    Конечно можно и эмулятором воспользоваться, но это лишние проблемы. А может и не лишние - если уж вы хотите ассемблер, то это может пригодиться.
    Ответ написан
    Комментировать
  • Нужно ли писать суффиксы литералов?

    @res2001
    Developer, ex-admin
    Однако с точки зрения оптимизации, эстетики и/или стандартов, как писать все-таки лучше

    Как хотите.

    На самом деле опциями компилятора можно задать выдавать предупреждения (или даже ошибку) на подобное не явное приведение типов. Тогда это будет "не красиво" или даже вообще ошибка компиляции, и тут вам придется исправлять ситуацию добавив суффикс.
    Но подобные опции используют не часто. На сколько помню для gcc это опции -Wsign-conversion и/или -Wconversion но могу ошибаться. И, по моему, они даже не включаются через -Wall -Wextra -pedantic - обычно используют этот набор опций, когда хотят заставить компилятор выполнять больше проверок кода.
    Немного модифицировав опции можно добиться не предупреждения, а ошибки.

    Если вы пишите исполняемый файл, то вы контролируете опции сборки в makefile (или чем вы там пользуетесь) и вас этот вопрос может не парить вообще.
    Если вы пишете библиотеку, которую распространяете в исходниках, то есть вероятность, что пользователь при сборке захочет добавить своих опций и тогда ошибки/предупреждения могут проявится. Можете расставить суффиксы для обхода потенциальных проблем, а можете забить и оставить на усмотрение конечного пользователя библиотеки.
    Ответ написан
    Комментировать
  • Почему программа "Конвентер Валют" выдаёт 0.000000 при любом значении?

    @res2001
    Developer, ex-admin
    Ответ в комментариях
    Ответ написан
    Комментировать
  • Как присвоить динамическому массиву типа void* значение в Си?

    @res2001
    Developer, ex-admin
    Нельзя выделить память для произвольного типа, т.к. размер произвольного типа - произвольный. Память всегда выделяется конкретного размера.

    В вашем примере вы выделяете память для двух указателей (void*). На всех распространенных платформах указатель, не важно на какой тип он ссылается, имеет один и тот же размер.
    Нельзя сделать разъименование void*, т.к. это указатель с неопределенным типом - компилятор не знает какого типа данные лежат по адресу в указателе, а следовательно не может с ними корректно работать. Для нормальной работы нужно привести указатель к какому-нибудь типу и потом уже можно делать разъименование (ваш 3 пример).

    Ваш пример не корректен для х64 платформы, т.к. sizeof(void*) там 8 байт, а sizeof(int) - 4 байта.
    Вы mallocом выделяете 16 байт памяти (2 указателя), а в 3 присваивании (которое работает) присваиваете значение 10 старшей половине первого указателя. В общем выглядит как бред.
    Для х32 - корректен, т.к. тут sizeof(void*) == sizeof(int)

    Для копирования двух массивов произвольного типа и размера, нужно знать размер массива в байтах (не в элементах). Можно не знать тип, но знать размер необходимо - иначе ничего не поучится. Выделяете память заданного размера, приводите указатель к char* и побайтово копируете (memcpy).
    Приводить к int* и копировать intы в этом случае нельзя, т.к. массив может быть, например 3 байта или 33, тогда при копировании через приведение к int* вы неминуемо выйдете за границу массива.
    Ответ написан
    Комментировать
  • Как написать функцию sin из библиотеки math.h в Си?

    @res2001
    Developer, ex-admin
    У вас sum2 не инициализируется в начале, а в цикле вы туда уже чего-то прибавляете.
    Как думаете какой результат будет возвращен функцией? Зависит от того что лежало на стеке где теперь лежит sum2, а лежать там может все что угодно.
    Ответ написан
    Комментировать
  • Почему fopen устанавливает ошибку Invalid argument в errno при fopen в режиме r+ после w и fopen в режиме w+ после r?

    @res2001
    Developer, ex-admin
    Интересный вопрос. Ответа у меня нет, что-то не приходилось использовать режимы "+".
    Попробуйте обнулять errno перед вызовом fopen. Возможно значение, осталось от предыдущего вызова. Вообще читать errno надо, только если fopen вернул NULL. При нормальном завершении errno не меняется.
    И если pFile == NULL, очевидно, что не надо вызывать fclose.
    Ответ написан