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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Звучит в точности как симптомы Undefined Behavior. На самом деле программа вылетает из-за, например, доступа к неправильным указателям. Но во время отладки звезды выстраиваются так, что это некорректное действие не приводит к падению программы.

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

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

    Возможно, там где-то выход за границы массива, Попробуйте еще, например, увеличить все выделенные строчки и массивы в коде раз в 10. Если после этого программа отработает, ищите, где у вас ошибка в логике - почему массивов не хватает.
    Ответ написан
    32 комментария
  • Взгляд опытных программмистов, что улучшить и исправить?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    1. #define SPACE ' ' - смысла не несет такая замена. Или какое-то более осмысленное имя, вроде EMPTY_FIELD, или просто в коде используйте ' '. Хотя лучше хорошее имя.

    2. check_field - ужас. Во-первых, проверки на крестики и на нолики идентичны - только сравниваете вы или с 'X' или с 'O'. Ну напишите функцию, которая принимает char и ищет тройку из этих символов. Далее, используйте циклы. 3 строки можно проверить одним и тем же кодом, только индексы сдвигаются на 3 - вот и запустите цикл на 3 итерации. Так же со столбцами. В итоге вместо 8 условий будет только 4.
    Вообще, можно до одного условия в трех вложенных циклах ужать:
    const int dx[4] = {0, 1, 1, 1};
    const int dy[4] = {1, 0, 1, -1};
    for (dir = 0; dir < 4; ++dir) {
      for (int start = 0; start < 9; ++start) {
        int step;
        int x = start % 3;
        int y = start / 3;
        for (step = 0; step < 3; ++step) {
          if (x < 0 || x > 2 || y < 0 || y > 3 || field[x+3*y] != player) break;
          x += dx[dir];
          y += dy[dir];
        }
        if (step == 3) {
          return 1;
        }
      }
    }
    return 0;


    3. print_field тоже делается циклами. Хотя бы по трем строкам.

    4. основной игровой цикл можно делать while (1), ибо по любому из значений check происходит break;
    Ответ написан
    Комментировать
  • VS Code творит лютую дичь при компиляции и отладке?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Это называется "оптимизируюший компилятор". Он может выкинуть какие-то строчки, часть кода переписать и перетасовать. Соптимизировать по своему усмотрению. Поэтому для отладки используют специальные debug сборки. Установите компилятору флаг -O0, чтобы отлкючить оптимизацию.

    В закоменченную фунцию он может заходить, если вы не перекомпилировали перед запуском.
    Ответ написан
    7 комментариев
  • Почему возникает ошибка Fatal error. System.Runtime.InteropServices.SEHException?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что библиотека бросила исключение. В ней какая-то ошибка. Это не проблема сборки или компиляции - это проблема в логике. Что-то вы не то библиотеке передаете. И там, допустим, деление на ноль. Или обращение по нулевому указателю.

    Просите у пользователей все данные, чтобы воспроизвести проблему. Или может оно минидамп создает, тогда можно будет его в отладчике открыть.

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

    Судя по сигнатурам функции, возможно вы пустой массив туда передаете вместо чего то осмысленного.
    Ответ написан
    7 комментариев
  • Как сделать ввод через стандартный поток (stdin) и через файл?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Есть такая функция freopen. Можно всегда читать через scanf, read из stdin, но если указано имя файла, то переоткрыть stdin на файл:
    freopen("input.txt", "rb", stdin);
    Ответ написан
  • Как убрать дублирование чисел в бинарном файле на си?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    С чего вы взяли, что туда записывается два числа 7?
    Туда записывается одно число размером в 4 байта, которое, если его интерпретировать как строку, выглядит как "7 7 ".

    Вы числа читаете и пишите в бинарном формате. Соответственно, файл input.bin у вас должен в hex редакторе выглядеть "0x00 0x00 0x00 0x01 0x00 0x00 0x00 0x07...", а не то, что у вас.
    Ответ написан
  • В чем разница записи массива через указатели?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что * выполняется перед вычетанием. *(current-1) - это предыдущий элемент. (*current-1) в плохой версии - это текущее значение минус 1.
    Ответ написан
    Комментировать
  • Как решить данную задачу на двумерные массивы c++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    int sum = 0;
    int cnt = 0;
    for (int dx = -1; dx <= 1; ++dx) {
      for (int dy = -1; dy <= 1; ++dy) {
        if (dx == 0 && dy == 0) continue;
        int nx = i + dx;
        int ny = j + dy;
        if (nx < 0 || nx >= N || ny < 0 || ny >= M) continue;
        sum += A[nx][ny];
        ++cnt;
      }
    }
    B[i][j] = cnt == 0 ? 0 : sum / cnt;
    Ответ написан
    Комментировать
  • Почему не работает ввод?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Попробуйте ваши строки на экран выводить. Нет ли там всяких лишних пробелов или переводов строк? Завершается ли программа, если ввести только конечную фразу? А если вы вводите что-то вроде "abc. Dragon flew away!", то строка будет " Dragon flew away!".
    Ответ написан
    Комментировать
  • Где ошибка в коде?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Проблема в том, что strtok нельзя вот так использовать вложенно. Вы когда разбиваете строку по пробелам, вы ломаете состояние доя разбития по строкам. Выделите сначала все строки.

    Но вообще, тут не надо никаких strtok. Нельзя строки сразу читать через scanf, например? А вообще, лучше читать число и символ. И там в зависимостм от символа или начинать новую строку, или нет.
    Ответ написан
    Комментировать
  • Как исправить ошибку Е0028 Выражение должно иметь константное значение на С?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    С вообще говоря, не умеет выделять массивы произвольного размера на стеке (вот так как у вас x локальная переменная). Ему надо знать размер массива во время компиляции. Вот про это он и ругается, n - не константа.

    Есть расширение VLA, которое есть в стандарте C99. С ним вот такие вот массивы можно заводить. Оно включено не во всех компиляторах из коробки. В некоторых его вообще, наверное, нет.
    Попробуйте передать компилятору в ключах флаг -std=c99.

    Или, выделяйте массив через malloc (не забудьте free сделать потом).

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

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Не совсем. Действительно, вы получаете в x код символа '4'. Но вы не преобразуете его в символьный. Это все еще int (скорее всего, 4-х байтный). В данном конкретном случае, это работает. Но тут вам повезло, что printf может вместо char принять int и правильно его синтерпретировать. Правильнее было бы преобразовать к char перед передачей в printf:
    printf("%c", (char)x);

    Вот тут уже действительно происходит преобразование к символьному типу char.

    Edit:
    Немного не прав был. printf надо передавать int, даже если формат "%с". Но какой-нибудь другой функции может понадобится именно char.
    Ответ написан
    5 комментариев
  • Как решить эту небольшую задачу?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Не самая подходящая задача для ДП. Но если задание такое - то можно вывести рекуррентную формулу:
    F(n) = F(n-1) + (F(n-1)-F(n-2))*2*n*(2*n-1)/(n-1)
    F(0) = 0
    F(1) = 2
    Ответ написан
    Комментировать
  • Получение значения указателя в структуре которая является указателем?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    В чем проблема? Если бы у вас в структуре было поле типа int, как бы вы получали значение этого поля? вот точно так же, только у вас тип не int, а char*

    struct Foo {
    char* data;
    };
    
    Foo* bar;
    char* pointer = bar->data;
    Ответ написан
    Комментировать
  • В чём ошибка вычисления бесконечно убывающей прогрессии с точностью до эпсилон?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Целочисленное переполнение при умножении i на i+1. Замените на, скажем prog += 1/((double)i*(i+1));, должно сработать.

    При i порядка 50000 результат перемножения не влезает в int. А у вас там 6697830 операций. В результате используются отрицательные или слишком маленькие неправильные значения i для вычисления слагаемых после 50000, и результат вообще не правильный.

    Ну и, кстати, логика решения у вас неправильная. Надо не с конечным значением сравнивать, а останавливаться, когда следующее слагаемое становится слишком маленьким.
    Ответ написан
    5 комментариев
  • C выдаёт ошибку при попытке сравнить 2 int?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Отключити оптимизацию и соберите проект в режиме Debug.

    У вас там Undefined Behavior и, очевидно, проблемы работы с памятью. Ошибку вы замечаете не там, где она, собственно, происходит.

    Сразу вижу проблему вот тут:
    strcat(result, alph[mod]);

    Тут вы приписываете строку с адресом alph[mod] к строке по адресу result. Обратите внимание, не строку, начинающуюся с позиции mod в массиве alph, а строку с адресом вроде кода символа 'F'.

    Ну и, вряд ли вы хотите часть строки alph скопировать в result. Плюс у вас в alph[] нет места под терминирущий 0, поэтому strcat вызовет переполнение буфера. Вам там надо дописывать один символ в конец result. Библиотечных функций именно для этого нет. Заведите счетчик количества символов в ответе и тупо переписывайте значение result[cnt].
    Ответ написан
    2 комментария
  • Почему после ассемблера учить Си легче?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Изучать ассемблер стоит, потому что это вправляет мозги. У программиста появляется какое-то понимание того, как все на самом деле устроено, как вообще работает компьютер. Тут в первую очередь не про сам ассемблер, а про устройство ЭВМ, которое почти всегда дается вместе с ассемблером вместе.

    Это понимание особенно важно тем, кто работает на относительно низкоуровневых языках (коим Си и является). Потому что абстракции не слишком далекие от ассемблера.

    Те же указатели, конечно, можно объяснять и как номер коробочки с данными, но если знать, что это адрес в памяти, то как-то понятнее и почему индексация работает, и что происходит при выделении памяти. Понимание стека и как процессор вызывает функции объясняет, что происходит с локальными переменными и в чем разница между ними и данными, выделенными через malloc.

    Еще один фактор: Учить язык более высокого уровня всегда легче после освоения языка более низкого уровня. Ну как после подготовки к марафону вам будет сильно проще ходить на 3-ий этаж пешком. Потому что там за счет абстрагирования и отдаления от предыдущего уровня, становится легче писать программы. Поэтому после ассемблера будет легче учить и питон и си++ и хаскель. А после си будет проще учить си++, чем если бы вы сразу взялись за си++. При этом учтите, что общее потраченное время и силы на изучение ассемблера + си может быть больше чем просто обучение си.

    Но вообще, я бы советовал не учить сначала ассемблер. а потом си. Ибо досконально знать ассемблер вам не надо будет. Да и кучу времени потратите на него. Стоит учить их параллельно, не особо углубляясь в ассембрер. Сможете функцию вызвать, вывести на экран строку, сложить 2 введенных с экрана числа - уже хорошо.
    Ответ написан
    Комментировать
  • Нужно ли писать суффиксы литералов?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    При инициализации переменных это обычно не используют. Ну, происходит тут преобразование типа - ошибки быть не может. Это лишь немного снижает читабельность кода и все.

    Вот когда вы константы передаете в какие-то перегруженные функции, где имеет значение unsigned ли ей передан или signed, или есть разница между float и double, то тогда - да, надо писать.
    Ответ написан
    4 комментария
  • Как сделать побитовое умножение и сложение большого числа decimal?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Вот эти побитовые сдвиги работли бы, если бы вы хранили число в двоичной системе счисления. Допустим, по 16 бит в каждом int. В таком виде вычисления побыстрее будут немного, но при выводе надо переводить число в 10-ую систему счисления. Судя по примеру ответа, вы храните по 3 десятичных знака в каждой ячейке. Т.е. у вас основание системы счисления 1000. И тут нужны сдвиги не битовые, а тысячные (которых не существует).

    Вообще, основной цикл умножения должен быть примерно такой:

    for (i = 0; i < len1; ++i)
      for (j = 0; j < len2; ++j) 
        c[i+j] += a[i]*b[j];


    И потом надо сделать все переносы. Вот тут и происходят сдвиг на i позиций (в системе счисления 1000) - это то самое +i в индексе у массива результата. И вместо выполнения прибавления, когда бит равен 1, тут происходит умножение на a[i]. В битовом случае оно как раз вырождается в умножение на 1 или 0 - прибавление или пропуск прибавления.

    Это работает даже если вы в массивах a,b храните по несколько бит в каждой ячейке, или даже десятичных знаков. Это просто умножение в столбик в произвольной системе счисления.

    Правда, тут проблема, что вот такое вот умножение, если вы храните по 32 бита, оно переполнится, и ответ надо в 64-битном типе получать. И даже если вы храните по 16 бит, то сумма может переполниться из-за большого количества слагаемых. Ну это решается выполнением переноса сразу же:
    const int BASE = 1000; // База системы счисления. Оно же максимальное число в ячейке +1.
    for (i = 0; i < len1; ++i) {
      int carry = 0;
      for (j = 0; j < len2; ++j) {
        carry += c[i+j] + a[i]*b[j];
        c[i+j] = carry % BASE; 
        carry /= BASE;
      }
      int i = len1+len2-1;
      while (carry > 0) {
        carry += c[i];
        c[i] = carry % BASE;
        carry /= BASE;
        ++i;
      }
    }
    Ответ написан
  • Как присвоить динамическому массиву типа void* значение в Си?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Если это произвольный тип, то какой у него размер? Какими значениями вы его заполнять-то пытаетесь?

    А так, скорее всего вам подойдет функция memset. Какими-нибудь нулями все заполнить - отлично можно. Хоть там int, хоть char, хоть float.
    Ответ написан