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

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Пишу ОС (важно, не выйдет использовать stdarg)

    Остановись на этом месте и почитай про __builtin_va_list, __builtin_va_start, __builtin_va_arg и __builtin_va_end.
    Поскольку компилятор знает ABI функций с переменным числом аргументов, он так же знает, как с ними обращаться.

    А если вызывать такую функцию (с аргументами "test", 'q'):

    void test(char* format, ...)
    {
        char* ptr = format;
    
        for (int i = -10; i < 10; i++)
        {
            putc(*(ptr+i));
        }
    }


    Ожидания были таковы, что хотя бы где-то должна была появиться буква q, чего не произошло.


    Ты просто искал не в том месте: вместо того чтобы двигаться по стеку ты двигался по памяти вокруг первого параметра.
    Вот так нашёл бы...:
    void test(char* format, ...)
    {
        char *ptr = (char *)&format;
    
        for (int i = -10; i < 10; i++)
        {
            putc(*(ptr+i));
        }
    }

    ...если бы аргументы действительно всегда были на стеке. Но это не всегда так -- всё зависит от используемого ABI.
    Ответ написан
    Комментировать
  • Как работает while ( !feof ( cfPtr ) )?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Как работает while ( !feof ( cfPtr ) )?

    Как видишь, плохо работает. Потому что man feof говорит нам, что функция возвращает статус потока, а статус потока меняется только от вызова других функций.
    Поэтому по всем правилам было бы написать так:
    FILE * cfPtr = fopen( "test.txt", "rb" );
    char temp;
    
    for (;;) {
      int read = fread( &temp, sizeof( char ), 1, cfPtr );
      if (read == 1) {
        printf("%c\n", temp);
      } else {
        if (feof(cfPtr)) {
          /*случился конец файла*/
        } else if (ferror(cfPtr)) {
          /* случилась ошибка */
        }
        break;
      }
    }
    Ответ написан
    1 комментарий
  • Поведение указателей в Cи?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Это присваивание адреса первого байта массива {0, 0, 0, 0} указателю float?

    Нет. Так можно было бы инициализировать массив. Но если так инициализировать указатель, то это присваивание самому указателю значения 0.

    что будет в i, после данных действий?

    сегфолт при попытке разыменования указателя.

    Начиная с с99 можно сделать так:
    float* arr = (float []){0, 0, 0, 0};
    и это означает создание в текущем контексте (на стеке, если эта строчка внутри функции, или в глобальных данных, если вне) массива из 4 элементов типа флоат, инициализация их нулями и присваивание указателю arr адреса этого массива.
    В этом случае int i = *((int*)arr); проинтерпретирует память первого элемента массива как целое число и присвоит его значение i. По стандарту ieee754 нулевое значение типа float представляется в памяти как 4 нулевых байта, соответственно в i будет записан 0.
    Ответ написан
    6 комментариев
  • Библиотеки GMP, как сохранять результат вычислений?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    как при работе с функциями из библиотеки GMP, считывать и сохранять данные в файл (и читать из файла) например используя Массивы?

    Экспортировать в массив/импортировать из массива через mpz_export/mpz_import.
    Ответ написан
  • Как перенаправить поток в несколько файлов?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    как реализовать то что можно делать в командной строке:
    ls > file1 > file2

    Эта командная строка вовсе не перенаправляет поток в несколько файлов. Вывод попадает только в один из них. Второй файл создаётся, но остаётся пустым.
    Перенаправить вывод в несколько файлов можно командой tee:
    ls | tee file1 file2

    Единственно что мне приходит в голову...вручную записывать в каждый fd.

    Да.
    Ответ написан
    Комментировать
  • Как получить массив элементов на выходе если возвращаемый тип функции int?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    должна возвращать 0

    что написать в return для этого?

    return 0;

    Как получить массив элементов на выходе

    Использовать массив переданный на вход.
    Ответ написан
    Комментировать
  • Функция gets() в C?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Первые два работают нормально, но когда доходит до 3, он не выполняется и длина выводится 0?

    Потому что scanf("%d", &n); зачитывает только один int из стандартного ввода. Если ты вводишь число и жмёшь enter, то конец строки остаётся в потоке ввода и читается gets'ом.
    Поэтому нужно либо вводить строку предназначенную для gets следом за числом предназначенным для scanf, либо scanf сделать таким: scanf("%d ", &n);
    Ответ написан
    Комментировать
  • Как передать двумерный статический массив в функцию на Си?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Есть массив char mass[1024][1024] и функция int fun(char*)

    в такую функцию можно передать fun(&mass[0][0]).

    Есть ли возможность для указания статического массива как аргумента?

    В смысле массива статической размерности? Легко: int fun(char a[][1000]); ... fun(mass);
    Ответ написан
  • Как запустить приложение через forkpty?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    может быть так?

    #include <pty.h>
    #include <unistd.h>
    #include <thread>
    #include <future>
    #include <iostream>
    #include <string>
    
    ssize_t sz = 1;
    
    int main()
    {
      int mfd;
    
      pid_t pid_fork = forkpty(&mfd, NULL, NULL, NULL);
    
      if (!pid_fork) {
        // Дочерний процесс
        execl("/bin/sh", "-", NULL);
      } else {
        // Родительский процесс
        char buf[1024];
    
        // Async
        auto future = std::async(std::launch::async, [mfd]() {
          std::string line;
          while (sz) {
            std::getline(std::cin, line);
            line = line  + "\n";
            write(mfd, line.c_str(), line.size()); // Нужно направить в дочерний процесс как stdin
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
          }
        });
    
        while (sz = read(mfd, buf, sizeof(buf))) {
          write(STDOUT_FILENO, buf, sz); // Вывод из дочернего процесса stdout
          std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    
        exit(0);
      }
    }

    Поскольку это теперь терминал, то имеет смысл установить размеры pty такими же, как у терминала вызывающей программы, например так:

    ...
    #include <sys/ioctl.h>
    ...
      struct winsize ws, *pws = NULL;
    
      if (ioctl(1, TIOCGWINSZ, &ws) >= 0)
        pws = &ws;
      pid_t pid_fork = forkpty(&mfd, NULL, NULL, pws);


    Кроме того, sleep_for в циклах чтения и записи не нужен, поскольку операции чтения присутствующие в обоих циклах -- блокирующие. Но нужна проверка того, что записались все прочитанные данные.
    Ответ написан
    1 комментарий
  • Как изменить строку, инициализированную при объявлении массива символьных указателей, с помощью scanf?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    char * suit [4] = { "Hearts", "Diamonds", "Clubs", "Spades" };

    Как изменить строку, инициализированную при объявлении массива символьных указателей, с помощью scanf?

    В данном случае -- только выделив память для новой строки, сделав scanf туда и присвоив указатель на выделенную память элементу массива suit. Напрямую в первоначальный массив -- никак, потому что указатели указывают на константные строки (а возможность писать char * вместо const char * -- это устаревшее средство обеспечения совместимости с древними версиями стандарта).
    Ответ написан
    3 комментария
  • Си. Как добавить программу в автозагрузку?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Как добавить программу на Си в автозагрузку Windows?

    Так же как и на любом другом языке -- прописав её в соответствующий ключ реестра.

    И еще: как искать информацию на ответы по Си, если я пишу в гугле "Си как добавить программу в автозагрузку", но везде появляется только С++? Нереально найти ответ.

    Потому что ты задаёшь неправильный вопрос. Язык -- это способ делать действия, а не сами действия. Добавить программу в автозагрузку можно через реестр. Писать в реестр можно через winapi. Справка по winapi есть тут.
    Ответ написан
    Комментировать
  • Почему считывается не по порядку?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Почему результат вывода именно такой?

    Потому что если внимательно прочитать man dup, особенно вот эту часть:
    After a successful return from one of these system calls, the old
    and new file descriptors may be used interchangeably. They refer
    to the same open file description (see open(2)) and thus share file
    offset and file status flags; for example, if the file offset is modified
    by using lseek(2) on one of the descriptors, the offset is also
    changed for the other.

    то окажется, что это поведение предписанное стандартом.
    fhandle2 открыт независимо от fhandle1, поэтому у него все параметры состояния свои.
    Ответ написан
    Комментировать
  • Передача массива в функцию?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    printMAS (&MAS);//будет ошибкой! Почему?

    Будет ошибкой только с точки зрения соответствия типов, потому что тип выражения &MAS -- int (*)[5]. Значение же адреса будет одним и тем же.

    Вот такие инструкции же работают...

    Потому что значение адреса передаётся в них через ... и интерпретируется внутри как void *, т.е. тип исходного адреса больше не имеет значения.
    Ответ написан
    4 комментария
  • Нужно ли вообще очищать память в этом месте?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Нужно ли вообще очищать память в старом указателе?

    Зависит от логики этой функции: если string всегда выделена в куче и нигде не остаётся ссылок на неё, то можно её освобождать. В противном случае -- нет.

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

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Как не сложно заметить при большой точности, мой результат отличается от результата библиотечного printf.

    Как не сложно заметить, long double имеет 19 значащих цифр, т.е. 0,0000000000000012300000000000000679 -- это всё что имеет смысл печатать, и эта часть одинакова в выводе printf и ft_printf. Всё что идёт дальше -- это мусор, который не содержится в исходном числе.

    long double tt = 0.00000000000000123;

    Число справа от знака = имеет тип double. См.
    Ответ написан
    4 комментария
  • На каких правилах основана шестнадцатеричная нотация в printf для вещественных чисел?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Где можно почитать почему так происходит, почему такие разные вариации записи одно числа в шестнадцатеричной системе исчисления?

    Разве что в спецификации использованной библиотеки языка C. Потому что в стандарте явно сказано, что первая цифра в таком представлении -- unspecified:
    A double argument representing a floating-point number is converted in the
    style [−]0xh.hhhh p±d, where there is one hexadecimal digit (which is
    nonzero if the argument is a normalized floating-point number and is
    otherwise unspecified) before the decimal-point character...
    Ответ написан
    Комментировать
  • Почему fgets не работает второй раз?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    char str1[5];
      char str2[3];
    
      fgets(str1,5,stdin);
      fgets(str2,3,stdin);

    Я ожидаю, что при запуске введу слово из 5 букв, потом слово из 3

    Чтение man fgets легко отвечает на этот вопрос:
    fgets() reads in at most one less than size characters from stream
    and stores them into the buffer pointed to by s.
    Reading stops after an EOF or a newline.
    If a newline is read, it is stored into the buffer.

    Т.е. чтобы прочитать ровно 5 и ровно 3 символа, нужно вызывать fgets с аргументом размера 6 и 4 соответственно. Кроме того, когда первая строка заканчивается символом конца строки, этот символ тоже должен быть считан. Т.е. 7 и 5.
    Ответ написан
    1 комментарий
  • Почему не выводится матрица?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    void readm(int N, int* X, FILE * dat)
    ... 
      int *X = nullptr;
      readm(N, X, dat);
    ...
      writem(N, X, res);

    Потому что ни N ни X внутри main не меняются, потому что их новое значения не возвращаются из функции readm.
    Минимальное изменение с которым всё заработает:
    void readm(int& N, int*& X, FILE * dat)
    Ответ написан
    2 комментария
  • Как правильно прервать поток в Си?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Общее правило при работе с потоками, не специфичное для С или pthreads: состояние резделяемое между потоками должно быть или атомарным или должно быть защищено примитивом синхронизации.
    Код функции thread_process никак не гарантирует, что компилятор не выкинет проверку !quit, потому что quit не меняется в этой функции и в функциях вызываемых из неё.

    Учитывая, что атомарность и потоки вошли в стандарт С11 имеет смысл смотреть туда.
    Если С11 недоступен, смотреть в pthread_mutex_*, pthread_cond_*, ...

    Может стоит вынести флаг в параметры потока

    По-хорошему -- да, стоит. Это, однако, ортогонально к синхронизации доступа.

    while(!quit) {
            if(difftime(time(NULL), last_cycle) > 30) {
                last_cycle = time(NULL);
                // Тут мои грязные дела
            }
        }

    Вместо busy wait лучше использовать sleep или что-нибудь типа pthread_mutex_timedlock/pthread_cond_timedwait.

    Я бы оформил код этого примера так:
    #include <sys/time.h>
    #include <pthread.h>
    #include <stdbool.h>
    #include <string.h>
    #include <stdio.h>
    
    struct thread1 {
        pthread_mutex_t lock;
        pthread_cond_t cond;
        bool quit;
    };
    
    static void* thread_process(void *p) {
        struct thread1 *arg = p;
    
        for (;;) {
            bool quit;
    
            pthread_mutex_lock(&arg->lock);
            if (!arg->quit) {
                struct timeval now;
                struct timespec ts;
    
                gettimeofday(&now, NULL);
                ts.tv_sec = now.tv_sec + 30;
                ts.tv_nsec = now.tv_usec * 1000;
                pthread_cond_timedwait(&arg->cond, &arg->lock, &ts);
            }
            quit = arg->quit;
            pthread_mutex_unlock(&arg->lock);
            if (quit)
                return NULL;
    
            // Тут мои грязные дела
        }
        return NULL;
    }
    
    int main() {
        pthread_t th;
        struct thread1 arg = {
            .lock = PTHREAD_MUTEX_INITIALIZER,
            .cond = PTHREAD_COND_INITIALIZER,
            .quit = false,
        };
        pthread_create(&th, NULL, thread_process, &arg);
    
        char cmd[16];
        while(true) {
            scanf("%s", cmd);
            if(!strcmp(cmd, "quit")) {
                pthread_mutex_lock(&arg.lock);
                arg.quit = true;
                pthread_cond_broadcast(&arg.cond);
                pthread_mutex_unlock(&arg.lock);
                break;
            }
            // остальные команды
        }
    
        pthread_join(th, NULL);
        return 0;
    }
    Ответ написан
    1 комментарий
  • Если к int добавить в конце точку без буквы f, то к какому типу будет произведено приведение?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    x = float(a) / 4.;

    Этот код не на С а на С++. В С++ тип выражения справа -- double, потому что это деление float на double. См. eelis.net/c++draft/lex.fcon
    Ответ написан
    Комментировать