Karol Rawdo
Karol Rawdo

Reputation: 39

Why is my function not returning null pointer?

I have a problem with visual code, everything works fine on the online compiler, but while trying it on stm32 nucleo it doesn't return NULL, where is the problem? It cannot break the while loop.

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

int funk(char *skai) {
    char delim[] = "+-=";
    int i = 0;
    float x, d = 0;
    char *array[2];
    char *ptr = strtok(skai, delim);
    while (ptr != NULL) {
        array[i++] = ptr;
        ptr = strtok(NULL, delim); // <---- doesnt return null, endless loop
    }
    int a = atoi(array[0]);
    float b = atof(array[1]);
    int c = atoi(array[2]);
    if (c != 0) {
        d = b * b - 4 * a * c;
        if (d > 0) {
            float root1 = (-b + sqrt(d)) / (2 * a);
            float root2 = (-b - sqrt(d)) / (2 * a);
            if (root1 > root2) {
                x = root1;
            } else {
                x = root2;
            }
        } else {
            x = -b / (2 * a);
        }
    } else {
        x = b / a;
    } //printf("%0.3f\n", x);
    return x;
}

int main(void) {
    char rxd[20] = "2x^2+x/5+2=0";
    funk(rxd);
}

Upvotes: 2

Views: 409

Answers (3)

chqrlie
chqrlie

Reputation: 144923

Your program has multiple problems:

  • you should try not to modify the argument string: strtok() modifies the string pointed to by its first argument, which is a nasty side effect and in your case loses important information such as the sign of the next constant.
  • you do not handle /
  • array has just 2 entries, causing undefined behavior for most equations that have more terms.

Here is a modified version that uses strtod() and manual parsing:

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

int funk(const char *exp, double *x1, double *x2) {
    double coef[3] = { 0, 0, 0 };
    double d, e, a, b, c, sign, part;
    int n;
    const char *p = exp;
    char *q;

    part = 1;
    for (;;) {
        p += strspn(p, " \t\r\n"); // skip whitespace
        if (!*p)
            break;
        if (*p == '=') {
            p++;
            if (part < 0)
                return -1;
            part = -1;
            continue;
        }
        d = 1;
        sign = part;
        if (*p == '+') {
            p++;
        } else
        if (*p == '-') {
            sign = -1;
            p++;
        }
        if (*p != 'x') {
            d = strtod(p, &q);
            if (p == q)
                return -1;
            p = q;
        }
        d *= sign;
        n = 0;
        if (*p == '/') {
            p++;
            e = strtod(p, &q);
            if (p == q)
                return -1;
            p = q;
            d /= e;
        }
        if (*p == 'x') {
            p++;
            n = 1;
            if (*p == '^') {
                p++;
                n = strtol(p, &q, 10);
                if (p == q)
                    return -1;
                p = q;
                if (n < 0 || n > 2)
                    return -1;
            }
        }
        if (*p == '/') {
            p++;
            e = strtod(p, &q);
            if (p == q)
                return -1;
            p = q;
            d /= e;
        }
        coef[n] += d;
    }
    a = coef[2];
    b = coef[1];
    c = coef[0];
    d = b * b - 4 * a * c;
    if (a > 0 || a < 0) {
        /* quadratic equation */
        if (d < 0) {
            return -2;
        } else
        if (d > 0) {
            *x1 = (-b + sqrt(d)) / (2 * a);
            *x2 = (-b - sqrt(d)) / (2 * a);
            return 2;
        } else {
            *x1 = -b / (2 * a);
            return 1;
        }
    }
    if (b < 0 || b > 0) {
        /* first order equation */
        *x1 = 0 + -c / b;
        return 1;
    }
    /* constant equation */
    if (c > 0 || c < 0) {
        return 0;
    } else {
        return 3;
    }
}

void solve(const char *exp) {
    double x1, x2;
    switch (funk(exp, &x1, &x2)) {
    case 0:
        printf("%s -> no solution\n", exp);
        break;
    case 1:
        printf("%s -> single root x=%g\n", exp, x1);
        break;
    case 2:
        printf("%s -> two roots x1=%g, x2=%g\n", exp, x1, x2);
        break;
    case -2:
        printf("%s -> two imaginary roots\n", exp);
        break;
    case 3:
        printf("%s -> true for all x\n", exp);
        break;
    default:
        printf("not a quadratic equation: %s\n", exp);
        break;
    }
}

int main(int argc, char *argv[]) {
    if (argc > 1) {
        for (int i = 1; i < argc; i++)
            solve(argv[i]);
    } else {
        solve("2x^2+x/5+2=0");
        solve("x^2+2x+1=0");
        solve("x^2=49");
        solve("x=49");
        solve("x=0");
        solve("2=2");
        solve("1=0");
    }
    return 0;
}

Upvotes: 2

chux
chux

Reputation: 154130

Array too small - by two.*1

I made array[] much larger and added printf("%d '%s'\n", i, ptr)

char *array[20];
char *ptr = strtok(skai, delim);
while (ptr != NULL) {
  printf("%d '%s'\n", i, ptr);
  array[i++] = ptr;
  ptr = strtok(NULL, delim); // <---- doesnt return null, endless loop
}

The output was

0 '2x^2'
1 'x/5'
2 '2'
3 '0'

So apparently array[] needed to be at least array[4].

Recommend to add protection when parsing or use a different approach.

if (i+1 >= sizeof array/sizeof array[0]) {
  // Handle/report error somehow
}
array[i++] = ptr;

Aside: If using float variables, might as well use float functions.

// float root1 = (-b + sqrt(d)) / (2 * a);
// ---------------------v
float root1 = (-b + sqrtf(d)) / (2 * a);

*1

Post had changed from "2x+2=0" to "2x^2+x/5+2=0"

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 223101

Given skai pointing to an array of char containing “2x+2=0”, a sequence of calls to strtok with delimiters “+-=” is expected to return first a pointer to (the first character of) “2x”, then “2”, then “0”, then a null pointer. The code as written attempts to store these values in array[0], array[1], and array[2]. However, array has no element 2 since it was defined with only two elements. So the program overflows the array, and the resulting behavior of the program is not defined by the C standard. It may be the program is then overwriting memory in such a way as to cause strtok to misbehave.

Change the definition of array to be larger, and, inside the loop calling strtok, monitor the value of i: If it reaches the limit of the array size, print an error message and terminate the function (or the entire program).

Upvotes: 3

Related Questions