Найдите решение квадратного уравнения

Я начал гораздо больше знакомиться с C (и кодированием в целом) и с тем, как использовать функции в своих интересах для создания более сложных программ. В качестве способа сделать что-то действительно полезное вместо моей книги «Начало программирования на C: для чайников» я решил сделать простую программу, которая поможет мне обмануть меня на уроках математики, программе, которая вычисляет квадратное уравнение.

Однако из-за того, что я никогда не видел фрагмента кода длиной более 60 строк за пределами видео на YouTube и моей книги для чайников, я точно не знаю, доступен ли способ форматирования для других людей.

/*El programa calcula la ecuación general de segundo grado,
no tiene mayor proposito más que practicar C y hacer trampa en los exámenes -w-,
trato de usar funciones claras para hacer más legible el código, tómese nota de que me gusta 
main en primer lugar main y luego el resto de funciones.*/
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int a,b,c,total,resultado; //total se usa para el resultado de diferentes opercaciones de la ecuación, resultado es el resultado final de la ecuación
void value_input();
int discriminante(int b, int a, int c);
void display();
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void main(){
        value_input();
        b=-b; //como todas las operaciones con "b" son su multiplicación *-1 y su cuadrado, decidí solo volver b negativo (o positivo dependiendo del caso)
        discriminante(b,a,c);
        
        display();
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
//value_input solo le pregunta al usuario los valores de a, b y c en la ecuación y los aloca a las variables correspondientes.
void value_input(){
        printf("Dame el valor de a: ");
        scanf("%d", &a);
        printf("Dame el valor de b: ");
        scanf("%d", &b);
        printf("Dame el valor de c: ");
        scanf("%d", &c);
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
//La función calcula la discriminante y devuelve su valor
int discriminante(int b, int a, int c){
        int result;
        b*=b;
        c=4*a*c;
        total=b-c;
        return total;
} 
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
//display calcula el divisor de la ecuación y determina como imprimir la ecuación basado en el discriminante
void display(){
        a*=2; //se calcula el divisor
        //se calcula valor del discriminante y dependiendo de éste se imprime el resultado
        if(total>0){    //es mayor que 0
                total=sqrt(total);
                printf("El resultado es: (%d±%d)/%dn",b,total,a);
                }
        else if(total<0){       //es menor que 0
                total=abs(total);
                total=sqrt(total);
                printf("El resultado es: (%d±%di)/%dn",b,total,a);
                }
        else if(total=0){       //es igual a 0
                printf("El resultado es: (%dn",b/a);
                }
}

Я думаю, что это в основном понятно для всех, кто хочет просмотреть код в целом, но я думаю, что в некоторых местах он может стать немного запутанным, особенно в функции display ().

Как я могу прояснить это?

2 ответа
2

Мне было бы легче понять, если бы комментарии были на английском языке, но я не тот, кто будет вносить изменения позже, поэтому напишите свои комментарии для этого человека (что будет наиболее полезно для вас в будущем).

Что я считаю активно вредным, так это очень длинные строки, такие как

//----------------------------------------------------------------------------------------------------------------------------------------------------------------------

а также

        b=-b; //como todas las operaciones con "b" son su multiplicación *-1 y su cuadrado, decidí solo volver b negativo (o positivo dependiendo del caso)

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

Глобальные переменные a, b, c, total а также resultado затруднить рассуждение о каждой функции в отдельности. Я даже не вижу, где на самом деле используется последний. Предпочитайте передавать значения в функции и из них, а не общаться через глобальные объекты.

Эти объявления функций лучше использовать в качестве прототипов (т.е. указать, что они не принимают аргументов, а не принимают неопределенные):

void value_input(void);
void display(void);

Я бы объявил всех помощников static связывание (это становится более важным, когда вы пишете программы с несколькими исходными файлами, но это хорошая привычка формировать), и я бы согласился с аргументами discriminante() в менее удивительном порядке a, b, c.

void main() неверно — main функция должна возвращать int, поэтому нам нужно int main(void). Ваш компилятор должен был вас предупредить об этом; убедитесь, что вы включили достаточно параметров предупреждений (например, gcc -Wall -Wextra).

Когда мы читаем ввод с помощью scanf(), важно использовать возвращаемое значение, которое указывает, сколько преобразований было успешным. Я бы написал функцию для получения единственного значения (и принятия нецелочисленных коэффициентов, поскольку это то, что мы можем ожидать от реальных уравнений — каламбур):

double read_double(const char *prompt)
{
    for (;;) {
        printf("%s: ", prompt);
        double x;
        int conversions = scanf("%lf", &x);
        if (conversions == 1) {
            return x;
        }
        if (conversions == EOF) {
            /* not much we can do now */
            fputs("Reading failedn", stderr);
            exit(1);
        }
        /* consume the invalid input and start again */
        puts("Input should be a valid number");
        scanf("%*[%n]");
    }
}

discriminante() заявляет total который он никогда не использует и возвращает значение, которое игнорируется. Там явно что-то не так.

Эта строка не делает того, что вы думаете:

   else if(total=0){

К счастью, это безвредно, поскольку если total>0 а также total<0 оба ложны, мы знаем, что total уже равен нулю, и присвоение не имеет никакого эффекта.

Когда мы знаем число n отрицательный, мы можем сделать его положительным просто с помощью -n вместо звонка abs().


Модифицированный код

Вот что у меня получилось после исправления вышеуказанного:

/* El programa calcula la ecuación general de segundo grado, no tiene
   mayor proposito más que practicar C y hacer trampa en los exámenes
   -w-, trato de usar funciones claras para hacer más legible el
   código, tómese nota de que me gusta main en primer lugar main y
   luego el resto de funciones.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

static double read_double(const char *prompt);
static double discriminante(double a, double b, double c);
static void display(double a, double b, double d);

int main(void)
{
    const double a = read_double("Dame el valor de a");
    const double b = read_double("Dame el valor de b");
    const double c = read_double("Dame el valor de c");

    const double d = discriminante(a, b, c);

    display(a, b, d);
}

double read_double(const char *prompt)
{
    for (;;) {
        printf("%s: ", prompt);
        double x;
        int conversions = scanf("%lf", &x);
        if (conversions == 1) {
            return x;
        }
        if (conversions == EOF) {
            /* not much we can do now */
            fputs("Reading failedn", stderr);
            exit(1);
        }
        /* consume the invalid input and start again */
        puts("Input should be a valid number");
        scanf("%*[%n]");
    }
}

// La función calcula la discriminante y devuelve su valor
static double discriminante(double a, double b, double c)
{
    return b * b - 4 * a * c;
}

// display calcula el divisor de la ecuación y determina como imprimir
// la ecuación basado en el discriminante
static void display(double a, double b, double d)
{
    printf("El resultado es: ");
    if (d > 0) {
        printf("(%g±%g)/%gn", -b, sqrt(d), a);
    } else if (d < 0) {
        printf("(%g±%gi)/%gn", -b, sqrt(-d), a);
    } else {                /* total == 0 */
        printf("%g/%gn", -b, a);
    }
}

  • 1

    Да. Я собирался сказать что-то о том, что эту функцию трудно выполнять, и случайно доказал это! Сейчас улучшилось.

    — Тоби Спейт


  • Насколько я понимаю, scanf показывает UB, когда ввод не может быть представлен с данным типом, поэтому это все еще не работает. C99, 7.19.6.2, p10: [The] элемент ввода […] преобразуется в тип, соответствующий спецификатору преобразования. [The] результат преобразования помещается в объект, на который указывает первый аргумент, следующий за format аргумент, который еще не получил результат преобразования. Если у этого объекта нет подходящего типа, или если результат преобразования не может быть представлен в объекте, поведение не определено.

    — JCC


  • @JCC, что бы вы порекомендовали вместо этого?

    — Тоби Спейт

  • 1

    Единственный способ сделать это правильно, о котором я знаю, — это прочитать всю строку, а затем проанализировать ее, используя strto*, что может указывать на ошибку, если она появляется. Видеть ramblings.implicit.net/c/2014/05/04/….

    — JCC


  • 1

    Ах да, это хорошо. Тем не менее, «прочитать всю строку» в общем случае сложнее, чем кажется. К счастью, мы можем уйти от чтения, скажем, до 40 символов в строке, и если строка длиннее и не удается преобразовать, то выполнить дополнительное чтение, чтобы отбросить оставшуюся часть строки.

    — Тоби Спейт


Читаемость и (легкая) ремонтопригодность

Читаемость кода очень субъективна, поэтому относитесь ко всему в этом разделе с недоверием.

Длина линии

Большинство стилей, используемых в дикой природе, явно ограничивают длину строк до менее 100 символов, если это возможно. Неважно, 72, 80, 96 или другое случайное значение, но> 140 — это слишком много.

Язык

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

Дать возможность дышать

Взгляните на следующие два фрагмента кода:

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int a,b,c,total,result; //total is used for the result of different operations of the equation, result is the final result of the equation.
void value_input();
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int a, b, c;
int total;  // total is used for the result of different operations of the equation, 
int result; // result is the final result of the equation.

void value_input();

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

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

Кроме того, мы смогли значительно сократить длину строки, разделив total а также result в свои строки. Больше нет полосы прокрутки, и мы можем видеть полные комментарии без каких-либо проблем.

Интервал также упрощает распознавание ошибки в текущем коде:

void display(){
        // <snip>
        else if(total=0){       //es igual a 0
                printf("El resultado es: (%dn",b/a);
                }
}

Вы заметили ошибку? Как насчет сейчас:

void display(){
        // <snip>
        else if(total = 0){       //es igual a 0
                printf("El resultado es: (%dn",b/a);
                }
}

Это задание, а не проверка на равенство.

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

Глобальные переменные и трудная ремонтопригодность

Вы, наверное, интересовались частью «(свет)» в предыдущем разделе. Хотя эти изменения в форматировании и интервале упрощают чтение кода, глобальные переменные затрудняют рассуждения о коде.

Чтобы знать, как a, b, c, total а также result изменить, нам нужно проверить все функции, потому что любая функция из того же файла может его изменить. Возможно, где-то даже есть опечатка, например

//          whoops:                 v 
int discriminante(int b, int a, int d) {
        int result;
        b *= b;
        c = 4 * a * c;                   // oh no, global c changed!
        total = b - c;
        return total;
}

Внезапно мы меняем глобальную c вместо локального, потому что мы случайно назвали наш аргумент d. Обычно компилятор с правильной конфигурацией в этот момент выдает нам предупреждение, но это может быть не конфигурация по умолчанию. Также неиспользованные result переменная должна выдать предупреждение, но это уже другая проблема.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *