Задать вопрос
Ответы пользователя по тегу C
  • Как UTF-8 помещается в char?

    @abcd0x00
    Есть Unicode - это огромная таблица всех символов в мире. У них там свои номера, которые никак не меняются (все символы пронумерованы). И вот для этого юникода есть кодировки, одна из которыз UTF-8 (а другие - UTF-16, UTF-32). Что такое кодировка - это таблица из последовательностей байт, отображаемых на символы. Одна последовательность байт ставится в соответствие одному символу. Соответственно, одна последовательно байт с помощью UTF-8 ставится в соответствие одному символу в Unicode (по его номеру). И наоборот, одному символу в Unicode (его номеру) ставится в соответствие одна последовательность байт в кодировке UTF-8. То есть туда-сюда можно переводить.

    Дальше ты читаешь последовательность байт и её можно превратить в одно число по определённому алгоритму и потом это число уже берётся в юникоде и там смотрится символ.

    Сама кодировка UTF-8 (закон преобразования) очень просто устроена: она берёт первый байт, в нём записано, сколько там ещё байт нужно взять. Потом эти байты берутся и целиком рассматриваются в виде непрерывной последовательности бит, из которой можно составить число. И потом это число ищется в юникоде уже.

    Дальше ты хочешь понять, а что же такое cp1251. Это тоже кодировка, но она никак не связана с Unicode. Вместо Unicode там используется другая таблица (очень маленькая табличка на 256 символов), поэтому там хватает одного байта, чтобы получить код любого символа в этой таблице. И в ней как раз есть своя кириллица, поэтому для неё кириллица помещается в один байт.
    Ответ написан
    Комментировать
  • Что означает char **s на языке Си?

    @abcd0x00
    Что означает char **s?

    Есть, например, char x - это область в памяти размером один байт, у которой есть имя x. Область в памяти имеет свой адрес в памяти. И этот адрес - это такое целое число (обычно четырёхбайтовое, но может быть и восьмибайтовое).
    x = 'a';
    Есть, например, char *px - это область в памяти размером четыре байта, у которой есть имя px. В эту область можно сохранить адрес байта в памяти.
    px = &x;
    Есть, например, char **ppx - это область в памяти размером четыре байта, у которой есть имя ppx. В эту область можно сохранить адрес области в памяти, в которой хранится адрес байта в памяти.
    ppx = &px;
    Теперь ты можешь применить операцию разыменования указателя (или операцию ссылки по адресу) и получить значение из x через указатель ppx. Выражение **ppx даст значение 'a'.
    Ответ написан
    Комментировать
  • Как парсить текстовый документ на С(Не С++)?

    @abcd0x00
    Подскажите какие технологии,или что почитать о парсинге текстового файла?

    wiki. автоматное программирование

    На C ты просто открываешь файл (fopen() + fclose()), читаешь его построчно (fgets()) или посимвольно (getc()) и подаёшь данные в конечный автомат для анализа. Когда же нужно работать со строками, то ты используешь разные функции из string.h.
    Это общий подход, но есть и готовые библиотеки для работы с конретными форматами. Но вот когда библиотек нет никаких или их невозможно поставить, тогда ты должен сам всё написать, а сам ты будешь писать через конечный автомат, потому что библиотеки обычно через него и пишутся.
    Ответ написан
    Комментировать
  • Что написано в строке указатель или ссылка?

    @abcd0x00

    не совсем понимаю как интерпретировать этот кусок
    (struct sockaddr_in *)&serv_addr
    то ли указатель на сруктуру, то ли ссылка, то ли ещё что...

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

    В лине адреса делаются таким образом: берётся общая структура, которая подходит под все адреса, потом к ней в конце привязывается (дописывается в памяти) адрес в определённом виде. А так как адреса бывают разных размеров, то и размер конечной структуры может варьироваться. Сам язык по стандарту допускает дописывание в конец структуры каких-то данных, поэтому эта возможность языка и используется в POSIX-системах для составления адресов перед их использованием.
    Ответ написан
    Комментировать
  • Стоит ли учить программировать по старинке?

    @abcd0x00
    И ещё актуален ли Си как язык программирования в 21 веке, или даже ОС пишут на чем-нибудь повыше?

    На нескольких языках пишется проект одновременно. Если пишешь что-то, то надо сами файлы проекта обслуживать.
    Например:
    • удалить пробелы на концах строк во всех исходниках
    • генерировать новый файл с исходным кодом определённого вида
    • записывать комментарий с именем файла и датой в определённые исходные файлы
    • копировать файлы в специальный каталог, если в них есть строка с именем пользователя
    • найти все непрокоментированные функции в коде и добавить их в файл с задачами
    • вычислить покрытие кода тестами в процентах и вывести это в html-страницу

    Да там много всяких действий надо делать. Основной принцип: если ты что-то сделал второй раз, напиши скрипт, который будет делать это в следующий раз. Так вот, для таких дейсвий C не подойдёт, потому что он под это не заточен, нужен специальный язык. В итоге у тебя сам проект на C, куски кода для проекта генерирует скрипт на shell'е, тестирование проекта выполняется скриптом на питоне. А потом тебе, например, надо сгенерировать набор подобных скриптов для любого нового проекта - и ты на shell'е пишешь скрипт, который создаёт shell-скрипты, python-скрипты и базовый исходник на C. Написать программу на C, которая создаёт исходники на C для написания другой программы, - милое дело, а может и несколько программ написать.

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

    У меня вообще первые программы были на QBasic'е, и они делали своё дело - добывали мне текстовые данные из бинарников, которые я брал из программы одной. Но это было давно, но я до сих пор помню, сколько я данных ими добыл. Вот для чего нужны программы. У тебя их никто не будет покупать, ты их делаешь для того, чтобы они работали и давали результат, который вручную не получишь, потому что это долго или невозможно вообще (100 часов надо).
    Ответ написан
    Комментировать
  • Есть ли разница между char* и char[число] при передаче аргументов функции?

    @abcd0x00
    Разницы нет. Вообще, принято скобки писать просто пустыми, без числа. Но в идеале, чисто стилистически для повышения читаемости, когда передаётся указатель, пишется указатель, а когда передаётся массив, пишется массив.

    Вот пример:
    Обычно пишут так
    int main(int argc, char *argv[])
    Но это можно записать и так
    int main(int argc, char **argv)
    Так как разницы нет.
    Но пишут первым способом, чтобы подчеркнуть для читателя исходника, что там массив указателей, а не просто указатель на указатель какой-то непонятный (можно его двигать, или нельзя его двигать - неясно). А в первом варианте читателю сразу понятно, что там можно применять argv++, так как движение по массиву происходит.
    Ответ написан
    Комментировать
  • Продолжать обучение?

    @abcd0x00
    Пройдя этот курс в голове по-любому останется хоть какая-то база по С++, но будут ли базовые знания Си? Или всё же Си надо изучать отдельно?

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

    Для примера тебе:
    В C++, если есть приведение типа, оно делается явно, причём с выбором правильной операции приведения типа (кастования).
    В C то же самое пишется просто без каких-либо операций, потому что существует неявное приведение типа (по стандарту), про которое ты знаешь, а потому и писать ничего не надо.
    То есть уже минус одна заморочка, которая в C++ жрёт текст исходника, а в C она вообще не используется, оставляя исходник чистым и кратким.
    Ответ написан
    Комментировать
  • Массив uint8_t [8] на переменную uint64_t?

    @abcd0x00
    Собственно, имеется динамический массив uint8_t[8], который нужно заменять на переменную uint64_t, введенную пользователем.

    Нужно написать функцию упаковщик. Там есть проблемы с эндианством, ты можешь просто получить неявную логическую ошибку, если не будешь учитывать эндианство. В идеале, нужно приводить всегда к одному эндианству, а для этого надо его сначала определить на данной системе. Вот для этого всего и нужна функция, которая всё правильно определит и запакует. Так просто через union нельзя делать.
    Ответ написан
  • Есть ли хорошие задачники по СИ?

    @abcd0x00
    Вот тут неплохие задачи
    acm.timus.ru/problemset.aspx

    И проверка есть для многих языков
    acm.timus.ru/submit.aspx
    Ответ написан
    Комментировать
  • Какой хороший курс или ресурс по СИ для подготовки к собеседованию?

    @abcd0x00
    Тут писал (это не освежитель, а центральная документация)
    Откуда узнавать о библиотеках?
    Ответ написан
    Комментировать
  • Как присвоить строку перемененной типа char[]?

    @abcd0x00
    char s1[7];
    
    sprintf(s1, "hello");
    strcpy(s1, "hello");


    #include <stdio.h>
    
    int main(void)
    {
        char s1[7];
    
        sprintf(s1, "hello");
        printf("%s\n", s1);
        return 0;
    }
    Ответ написан
    Комментировать
  • Для чего в Си ввели синтаксис указателя на массив?

    @abcd0x00
    Представь, например, что у тебя есть точка, которая описывается массивом её координат:
    typedef int Point2D[2];
    И дальше ты делаешь массив таких точек:
    Point2D array[5];
    А потом ты выводишь их по очереди:
    Point2D *p, *q;
    
    for (p = array, q = p + 5; p < q; p++) {
        print_point(*p);
    }


    А вот немного по-другому записано - и у тебя уже прямая получается:
    typedef struct {
        Point2D *pt1;
        Point2D *pt2;
    } Line2D;

    Почему здесь указатели? Да потому что они жрут меньше памяти, чем массивы чисел. Так одна прямая жрёт 8 байт, а без указателей она жрала бы 16 байт. А была бы прямая в трёхмерном пространстве, то это так же занимало бы 8 байт, а без указателей - 24 байта.

    Дальше код пошёл:
    код
    #include <stdio.h>
    
    typedef int Point2D[2];
    
    typedef struct {
        Point2D *pt1;
        Point2D *pt2;
    } Line2D;
    
    void print_point(Point2D p)
    {
        printf("(%d,%d)\n", p[0], p[1]);
    }
    
    void print_line(Line2D l)
    {
        printf("Line[\n");
        print_point(*l.pt1);
        print_point(*l.pt2);
        printf("Line]\n");
    }
    
    int main(void)
    {
        Point2D array[5] = {{1, 2}, {3, 4}, {5, 6}};
        Point2D *p, *q;
        Line2D l;
    
        for (p = array, q = p + 5; p < q; p++) {
            print_point(*p);
        }
    
        l.pt1 = array;
        l.pt2 = array + 2;
        print_line(l);
    
        return 0;
    }


    Вывод
    [guest@localhost c]$ .ansi t.c -o t
    [guest@localhost c]$ ./t
    (1,2)
    (3,4)
    (5,6)
    (0,0)
    (0,0)
    Line[
    (1,2)
    (5,6)
    Line]
    [guest@localhost c]$



    Как видишь, без этой возможности язык был бы гораздо ограниченней и сдох бы ещё в 80-е годы.
    Ответ написан
    Комментировать
  • Как дефрагментируется память в языках без сборщика мусора?

    @abcd0x00
    В C функция free() ищет, какие блоки можно слить вместе, но это без перемещений. Например, один большой блок был занят данными A и B, при освобождении этих подблоков они сливаются обратно в один большой блок.
    Ответ написан
    Комментировать
  • Как пропустить весь поток для того, чтобы можно было воспользоваться getchar()?

    @abcd0x00
    Запомни одно
    fflush(stdin);
    это полное фуфло от незнающих. В стандарте оно не определено, поэтому на разных компиляторах оно будет по-разному себя вести в итоге. В лучшем случае оно ничего не будет делать, в худшем - выдаст какой-нибудь мусор.

    Твоя проблема в том, что после ввода числа там сохраняется перевод строки (нажатый Enter) и вот его-то и надо прочитать.

    scanf("%d%*c", &a);

    А вообще, не надо использовать scanf() таким образом. Вместо этого надо сделать getline() на базе fgets() + sscanf() - и тогда всё будет работать точно.
    Ответ написан
    Комментировать
  • Почему кракозябры при выводе строки задом наперед?

    @abcd0x00
    Для русских букв используй широкие символы wchar_t и широкосимвольные функции wcslen() и wprintf().
    Ответ написан
    Комментировать
  • Точность рекуррентной формулы?

    @abcd0x00
    Там только соотношение выведено правильно. Дальше нужно выбрать правильные начальные значения и правильную проверку для остановки.
    Ответ написан
    Комментировать
  • Как на си разобрать бинарные данные?

    @abcd0x00
    Вопрос вот в чем, на стороне клиента на javascript я формирую бинарные данные и отправляю их по веб сокету

    Ты должен их сериализовать в какой-нибудь определённый (или стандартизованный) формат типа json или xml и потом разбирать такие данные на принимающей стороне.

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

    @abcd0x00
    А теперь немного погуглив, нашёл язык Go, что, мол он тоже почти похож как Си, но только легкий для освоения.

    Нет, он сделан гораздо хуже, чем C. В нём многие недоделки выдаются за такие якобы гениальные возможности. Например, необходимый копипаст или if'ы на три страницы выдаются за очень ясный код, поэтому его типа так много. А дело только в том, что они придумали, что фигурные скобки должны стоять всегда и на отдельных строках. В C функция занимает одну или две строки, в Go та же самая функция занимает шесть строк и это всё выдаётся за гениальность. В итоге код из трёх функций в C занимает 25 строк, а в Go - три страницы. Также если брать длину строки в коде, то в Go вернулись к длинным строкам в стиле PHP, когда это всё просто невозможно читать, потому что для прочтения одной строки нужно три раза ходить чай пить. Когда в Питоне, например, вводят правило 80 символов на строку, которого даже в C не было, хотя оно хорошее и снимает много проблем, то в Go делают наоборот и на все вопросы заявляют, что они гении, поэтому знают, как лучше. Поэтому никакого желания читать Go'шный код нет, так как каждый раз ты знаешь, что это неповоротливый код в три строки, размещённый на трёх страницах.
    Ответ написан
  • Какую книгу можно использовать по языку C, чтобы получить базовые познания в CS?

    @abcd0x00
    Язык программирования C (K&R2)
    www.ozon.ru/context/detail/id/2480925

    Язык программирования C. Лекции и упражнения. (Стивен Прата)
    www.ozon.ru/context/detail/id/31649671
    Ответ написан
    Комментировать