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

    @res2001
    Developer, ex-admin
    1. В двумерных массивах в Си данные располагаются в памяти по строчно, т.е. сначала идет первая строка массива, затем вторая и т.д. У вас же индекс по строкам указан вторым. Вас тут спасает только то, что матрица квадратная и количество строк равно количеству столбцов.
    2. Внутри функции переданный массив - это не массив, это указатель float *nameMatrix. Соответственно вы не можете делать двойную индексацию nameMatrix[x][y]. Вам надо вычислять индекс вручную:
    *(nameMatrix + y * columnsMatrix + x) = a * (x + 1) * powf(sinf(y + b), 2);

    Для справки, индексация массива это синтаксический сахар для следующей операции: *(nameMatrix + i)
    Ответ написан
    Комментировать
  • Кроссплатформенное программирование на C?

    @res2001
    Developer, ex-admin
    Тут не важно GUI или нет (может быть любой интерфейс - сеть, файлы, потоки, процессы, и т.п. системные интерфейсы). Подход один и тот же. Если нужна кроссплатформенность, то ищите кроссплатформенные библиотеки, реализующие нужный интерфейс. Если таких нет (видимо плохо искал), то пишите свою. Это довольно сложно.
    Ответ написан
    Комментировать
  • Декларация и инициализация, в чем различия?

    @res2001
    Developer, ex-admin
    По моему, на русском чаще используют термин объявление, а не декларация. По крайней мере мне так привычнее.
    Объявление в одном из возможных переводов на английский звучит как declaration.
    https://en.cppreference.com/w/c/language/declarations
    https://en.cppreference.com/w/c/language/type

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

    Понять различие между объявлением и определением на простом встроенном типе (int) довольно сложно, потому что сам тип уже известен, его не нужно объявлять. Для примера буду использовать структуру.

    Кроме того важно где конкретно в коде программы определена переменная - глобально (по отношению к файлу исходного кода) или локально (в функции).
    struct s;                     // предварительное объявление
    struct s { int v; };       // объявление структуры
    strcut s s1;                // определение глобальной структуры
    struct s s2 = { 0 };     // определение глобальной инициализированной структуры
    int main()
    {
      struct s s3;             // определение локальной структуры
    }

    Предварительное объявление - говорит о том, что где-то есть полное объявление. Тип объявленный только предварительным объявлением - не завершенный (не полный). Нельзя определить переменную по неполному типа. НО можно определить указатель на неполный тип. Но обращаться по указателю на неполный тип нельзя. Но присвоить адрес можно :-) Это можно использовать в своих интересах.
    После полного объявления типа уже можно определять переменные этого типа.
    s1 - глобальная не инициализированная структура. Память под нее выделяется в секции bss исполняемого файла. В исполняемом файле обычно не выделяется память непосредственно, но сохраняется размер секции. Загрузчик ОС читает размер секции из файла и выделяет память нужного размера. Глобальные не инициализированные переменные на самом деле инициализируются неявно нулем.
    s2 - глобальная инициализированная структура. Память под нее выделяется в секции data исполняемого файла. Тут уже не достаточно сохранить информацию о секции, т.к. есть начальные значения. Поэтому такие переменные непосредственно присутствуют в исполняемом файле (точнее выделена память под них и присвоено начальное значение). Есть еще секция rodata - для константных данных.
    s3 - локальная не инициализированная переменная. Память под нее выделяется на стеке. Не зависимо от того инициализирована переменная или нет, память выделена, а значит в этой памяти уже что-то лежит - не бывает "пустой" памяти. В случае не инициализированной переменной на стеке, в памяти лежит мусор, который остался тут от прошлых действий.
    Ответ написан
    1 комментарий
  • Как создать указатель на вводимый текст?

    @res2001
    Developer, ex-admin
    Ну и покажите что вы пытались сделать.

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

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

    Когда вы выделите буфер (с помощью malloc например), вы получите указатель. Пока этот указатель указывает на "пустой" буфер. После чтения в буфер, указатель будет указывать на прочтенные данные. Функция чтения вернет вам еще и длину прочитанных данных. Этого достаточно для дальнейшей работы с буфером.
    Ответ написан
    8 комментариев
  • Возможно ли создание своего типа данных - массива бит?

    @res2001
    Developer, ex-admin
    В п.1 написана какая-то синтаксически не корректная хрень, смысла которой я понять не могу.

    Массив бит или битовое поле можно реализовать, конечно. Фактически это будет массив байт (или любого другого беззнакового целого), можно его спрятать в typedef и реализовать над ним несколько операций отдельными функциями.
    Например в С++ есть специализация std::vector<bool>, которая для экономии памяти использует внутри битовый массив, а не массив bool, как можно было бы подумать.

    Объявляете байт и работаете с отдельными битами с помощью битовых операций. Если есть необходимость в более широком битовом поле, то можно все унифицировать: при инициализации задаете размер битового поля, вычисляете по заданному размеру размер массива (можно использовать uint8_t, uint16_t, uint32_t или uint64_t - любой беззнаковый целочисленный тип оптимальной длинны), выделяете память для массива.
    Операции над битовым полем:
    1. создание/удаление
    2. установить/снять бит по номеру
    3. проверить установлен ли бит по номеру
    4. опционально вычисление количества установленных бит и/или проверка пустое ли битовое поле.

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

    Объявить переменную размером в один бит (или размером не кратным 8 бит) нельзя, потому что в современных компьютерах минимально адресуемая единица памяти - 1 байт. Была бы 1 бит - можно было бы объявлять переменные размером в 1(2,3, 11, ...) бит. Это архитектурное ограничение компьютеров, а не языка программирования.
    Ответ написан
    2 комментария
  • Как корректно отслеживать завершение потоков?

    @res2001
    Developer, ex-admin
    Предлагаемый POSIX вариант это join. Если не устраивает, то вы сами можете конструировать проверку завершения потоков теми средствами какие вам удобно и какие доступны.

    Самое простое: выставляйте в потоке атомарный флаг завершения, в основном потоке проверяйте.
    Если можно подождать какое-то время, то можно использовать условную переменную с таймером, а не флаг.
    Можно и очередь, но, по моему, это как из пушки по воробьям. Для этой задачи не совсем то. С тем же успехом можно использовать pipe. Но зачем, когда это можно сделать проще.

    Имейте ввиду, что join все равно надо вызвать, когда удостоверитесь, что поток завершен. Чтоб окончательно освободить все ресурсы потока. Или надо заранее делать поток отсоединенным (detach). Я бы использовал join, а не detach.
    Ответ написан
  • Нужно ли заключать все переменные макроса в скобки?

    @res2001
    Developer, ex-admin
    В данном случае скобки, скорее всего, действительно роли не играют. Я не смог придумать вариант, в котором бы подстановка без скобок дала бы не правильный результат.
    Но в других случаях скобки обычно нужны. Так что проще запомнить правило - параметры макросов при использовании заключай в скобки. И делать так всегда, чем каждый раз думать, нужны тут скобки или нет.
    Кроме того, если ни я ни вы не придумали подходящего варианта для взлома конструкции, это не значит, что его не может быть в принципе.
    Ответ написан
    1 комментарий
  • Библиотека Cron scheduler на windows для си?

    @res2001
    Developer, ex-admin
    В винде шедулер доступен в виде набора COM объектов.
    Смотрите тут: https://docs.microsoft.com/en-us/windows/win32/tas...
    Кросс-платформенной библиотеки не встречал.
    Ответ написан
    Комментировать
  • Как сделать чтобы программа ждала завершения bat-файла?

    @res2001
    Developer, ex-admin
    Как уже было сказано system() всегда выполняется синхронно, т.е. ждет окончания выполняемой программы.
    В батнике аналогично - когда стартуете команду/программу обычным способом батник приостанавливается до завершения программы.
    Так что описанного поведения по этим причинам быть не может.
    Скорее всего проблема в чем-то другом.
    В конце батника добавьте строку pause, чтоб он не закрывался сразу и посмотрите что выводится на консоль. Подозреваю, там будет какая-то ошибка. Может быть "file not found" или еще что-нибудь.
    Ответ написан
    Комментировать
  • C - как сделать замену подстроки в строке (руками)?

    @res2001
    Developer, ex-admin
    1. Не правильно вычисляете размер будущей строки в первом случае. В первом случае результирующая строка будет меньше оригинальной, у вас же получается больше.
    2. Не выделяете место под завершающий нулевой символ. Вообще завершающий ноль вы полностью игнорируете. Без него у вас в итоге получится не строка, а массив символов. Раз уж вы делаете все в ручную, то и завершающий ноль надо проставлять руками.
    3. Согласен с Wataru на счет startsof и трех циклов.
    4. Я бы сначала искал вхождение строки замены и только если оно найдено производил бы необходимые действия, в т.ч. и выделение памяти под новую строку. У вас же сейчас предполагается, что подстрока замены всегда найдется, что в общем случае не верно.
    5. Что будете возвращать, если замены не будет? Будете просто выделять память под новую строку и возвращать новую строку аналогичную оригинальной? Или будете возвращать указатель оригинальной строки? Но тогда будет мешать квалификатор const для str1. А кроме того, если возвращать указатель на оригинальную строку, то что должен делать вызывающий код со старой строкой? Т.е. если замена была, то вы возвращаете новую строку и в вызывающем коде старую строку можно удалить. А если строка не найдена, то старую строку удалить нельзя, т.к. вы вернули указатель на нее же. В общем надо еще продумать как возвращать результат. Я бы на самом деле возвращал признак была замена или нет (или количество замен), а результирующий указатель надо возвращать в дополнительном параметре. Если замен нет, то либо указатель не возвращается, либо возвращается указатель на str1.
    6. в качестве оптимизации:
    for (int i = 0; i < strlength(str1); i++)
    Вызываете на каждой итерации strlength.
    Ответ написан
    1 комментарий
  • Почему программа требует константу?

    @res2001
    Developer, ex-admin
    Наверное стоит обратится к учебнику по Си почему именно так.
    Изначально автоматические массивы в Си имели фиксированный размер, т.е. размер надо задавать константой.
    Отличие константы от обычной переменной - компилятор в процессе компиляции знает значение константы, а значение переменной нет. В процессе компиляции компилятор может сгенерировать команды для выделения памяти под массив.

    Начиная с С99 (это такой стандарт Си от 1999 года) появились VLA (Variable Length Array). Синтаксис такой же как и у обычных массивов, но они могут иметь изменяемый размер, т.е. размер можно задавать обычной переменной.
    Использование VLA влечет дополнительные расходы времени выполнения, поэтому многие принципиально не используют эту возможность (например ядро Linux). И это вполне оправданно еще и потому, что память под VLA массивы выделяется на стеке, пространство стека ограничено, а вы с дуру можете там выделить слишком большой массив, что вызовет переполнение стека.

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

    @res2001
    Developer, ex-admin
    Макросы это просто один из инструментов, который предоставляет язык. Использовать его или не использовать ваше дело, многопоточность тут не при делах.
    Один и тот же код может моментально стать не потокобезопасным, как только появляются не защищенные разделяемые между потоками данные, которые использует код. При этом сам код может не поменяться, просто вы решили добавить поток и использовать в нем те же данные. Данные сразу же становятся разделяемыми и нужно их использование защищать везде, где они раньше использовались "беззащитными", будь то обычный код или код в макросах.
    Ответ написан
    Комментировать
  • Почему нет данных в хэш-таблице?

    @res2001
    Developer, ex-admin
    malloc не зануляет выделенную память. После выделения mallocом в выделенной памяти содержится мусор.
    А у тебя в ht_hasItem определение занят ли элемент идет через сравнение с NULL. Когда выделил array надо обнулить массив memsetом или использовать calloc для выделения.
    Ответ написан
    2 комментария
  • Можно ли управлять всей программой с помощью скриптового языка?

    @res2001
    Developer, ex-admin
    Так же lua (как и другие скриптовые языки) можно встраивать в собственное приложение.
    Оба варианта имеют место быть, вопрос лишь в том кто главный и кто рулит тут всем.
    При использовании подхода из ответа shurshur главный - интерпретатор скриптового языка (и твое приложение, написанное на этом языке), а твоя С/С++ библиотека - один из модулей используемых скриптом.
    При встраивании главное твое приложение, которое может вызывать скрипты для каких-то своих целей. Скрипты могут использовать объекты твоей программы, для которых ты разрешил это действие.
    Выбор реализации во многом зависит от того, что хочется получить в итоге.
    Ответ написан
    Комментировать
  • Как грамотно это сделать? Сравнение файлов?

    @res2001
    Developer, ex-admin
    Вычислять offset

    Вычислять ничего не нужно. Сравнивайте по символьно, не используя strcmp (или memcmp) и offsetом у вас будет текущий индекс. Кроме того для текстового варианта вам не нужен "глобальный" offset, т.к. вы будете сравнивать построчно. Т.е. offset только внутри данной конкретной строки.
    Для бинарного сравнения - все то же самое, но offset будет глобальный, тут уже сиволы CR и LF роли не играют.
    Имейте ввиду, что в винде перевод строки это CRLF (0x0D 0x0A), а в никсах LF (0x0A)
    Ответ написан
    Комментировать
  • Утечка памяти sqlite 3? Откуда?

    @res2001
    Developer, ex-admin
    Не работал с sqlite.
    В table_exists не вызывается sqlite3_finalize. Похоже утечка тут.
    Но ошибка ссылается на 54 строку, что странно. Тут выглядит все Ок.
    Ответ написан
    Комментировать
  • Как задать размерность массива при помощи константы?

    @res2001
    Developer, ex-admin
    Задай с помощью defineа.
    Размерность статического массива должна быть известна на этапе компиляции. А значение константы в общем случае может быть не извесно при компиляции. К сожалению в Си незавезли из плюсов constexpr.
    Ответ написан
    8 комментариев
  • Имитация ООП в C, где ошибка?

    @res2001
    Developer, ex-admin
    Ну как минимум вы ничем не инициализируете test.tochar в "конструкторе", но используете этот указатель в printf, это вызывает вылет программы без вызова _point_tochar.

    Вычисление адреса объекта (self) вы так не сделаете. Я в принципе могу догадаться, что вы пытались сделать, но это выполнено явно не правильно. И это даже может дать правильный результат, если правильно реализовать. Но любое изменение в стеке приведет к тому, что "формулу" придется переделывать. А стек меняется при каждом вызове и возврате из функции, при каждом объявлении локальной переменной, т.е. очень часто.
    Так что адрес объекта передавайте явно в функцию.

    В "Экстремальный Си" описывается подход к ООП на Си.
    Ответ написан
    Комментировать
  • Как найти родителя заданного элемента в бинарном дереве?

    @res2001
    Developer, ex-admin
    Потому что в блоке else оба вызова идут с left_child.
    А так же после первого вызова сразу будет возврат, т.к. вызовы находятся в операторе return.
    И вам нужно сначала проверить, что указатели на потомков не nullptr.
    На самом деле вам нужно ещё одно условие в блоке else:
    If(root->data < x) return searchParent(root->left_child, x);
    Else return searchParent(root->right_child, x);
    Ответ написан
  • По какой книге изучать язык Си: Эффективный Си или k&r?

    @res2001
    Developer, ex-admin
    KR не учебник и не стандарт, написана давно. Хотя есть довольно свежее издание, но текст не изменился.
    Берите Эффективный Си, хотя я не читал эту книгу, но выглядит норм. К тому же написана на основе свежих стандартов, в отличии от KR.
    У вас не плохой бэкграунд, сам язык достаточно прост, разберетесь с тонкими местами (массивы, строки, динамическая память, указатели, адресная арифметика) и можно начинать что-то делать.
    По функциям стандартной библиотеки полно информации в интернет, например тут
    Вообще книг на русском именно по Си не много, возможно это связано с тем, что язык достаточно прост и одна хорошая книга закрывает большую часть потребностей.
    Кстати, "экстремальный Си" выглядит то же многообещающе, но это, похоже, уже следующий уровень.
    Ответ написан
    3 комментария