C sprintf()精度.16错误

C sprintf()精度.16错误,c,string,printf,double,C,String,Printf,Double,我必须为大学做一个项目,所以我选择编写一个计算器/方程解析器 整个程序(无(语法)错误处理): 我遇到了一个错误,只有当我在sprintf()调用中将精度设置为16时才会出现。在任何精度高于或低于16的情况下,一切都可以正常工作,在调试模式下也可以正常工作 导致程序崩溃的表达式: 12/(-9)+3,但12/(-9)工作正常 (1) +(2)+(3)和类似的东西 下面是代码的作用: -它在用户输入的字符串中查找“upper”parantesses对 ->j:第一个字符在偏执词中的位置 ->i

我必须为大学做一个项目,所以我选择编写一个计算器/方程解析器

整个程序(无(语法)错误处理):

我遇到了一个错误,只有当我在sprintf()调用中将精度设置为16时才会出现。在任何精度高于或低于16的情况下,一切都可以正常工作,在调试模式下也可以正常工作

导致程序崩溃的表达式:

12/(-9)+3,但12/(-9)工作正常

(1) +(2)+(3)和类似的东西


下面是代码的作用:

-它在用户输入的字符串中查找“upper”parantesses对

->j:第一个字符在偏执词中的位置

->i:偏执后第一个字符的位置

-使用相同的函数递归计算副命题的内部,直到不再有副命题对为止

-调用另一个函数将此计算转换为双精度

-函数的返回值是字符串[0到j-2]##result_str[j到i-2]##字符串[i到end]串联的递归调用


下面是代码(上面我描述的重要部分在末尾):

#包括
#包括
#包括
#包括
#包括
#定义长度(x)(sizeof(x)/sizeof(x[0]))
int hasPar(char*)//Überprüft,在Klammer zurück的前十个Klammer位置的弦和基布上
char*subStr(char*,char*,int,int)//功能,子串zurückgibt
双calcStr(字符*)//Wertet Terme ohne Klamern aus
双重计算(字符*)//条款的修订
int main(/*int argc,char*argv[]*/){
字符*s/*,c*/;
双麦角碱;
做{
看跌期权(“Taschenrechner.Ignoriert alles,au\xe1”“er 0-9,,,,,,,-,*,/,^,(,)”;
fflush(stdin);
s=calloc(100,1);
scanf(“%99[^\n]”,s);//[^\n]贝德乌特(bedeutet,dass alle Zeichen außer Zeilenumbruch eingelesen werden sollen)
printf(“Erkannter Ausdruck:\n%s\n”,s);
ergebnis=计算值;
printf(“Berechnetes Ergebnis:\n%f\n”,Ergebnis);
放置(“Erfolg!”);;
免费的;
fflush(stdin);
//如果(getc(stdin)='c'){break;}
}而(0);
返回0;
}
int hasPar(字符*s){
对于(unsigned int k=0;k input_len){//Wenn子串gröer sein sollte als Usprungsstring order子串Null Zeichen enthalten soll
返回NULL;
}else if(长字符串为Zahl;双精度铸造字符串(aus stdlib.h)
}
双计算(字符*s){
双结果_d=0。;
字符*t,*result\u str;
int check=1,i=hasPar(s),j=0;
如果(i==(-1)){
返回calcStr(s);
}否则{
j=++i;//j=++i是erstenöffnenden Klammer的职位
while(检查>0){
如果(s[i]=='('){
check++;
}如果(s[i]==')'){
检查--;
}
i++;
}//在“奥伯斯滕”克拉默总统的任期内,我是克拉默总统的最高职位
t=calloc(strlen,sizeof(char));//用于存储子字符串的字符串
result_str=calloc(strlen,sizeof(char));//用于存储Paranthes计算结果的字符串
result_d=calc(subStr(result_str,s,j,i-j-1));//调用我们已经使用的函数
sprintf(result_str,“%-.16f”,result_d);//将结果转换回字符串,我认为这就是导致崩溃的原因
return calc(strcat(subStr(t,s,0,j-1),strcat(result_str,s+i));//上述连接字符串的递归调用
}
}
-
subStr(char*dest,char*src,int offset,int len)
只是一个使用
strncpy()和错误处理的函数
calc()
是我们使用的函数

-
s
是用户给定的*字符

-
result\u d
是子串在子串中的计算结果(双变量)

-
result\u str
是存储
result\u d
类型转换的*字符


我希望我没有忘记任何东西。如果需要更多的代码或信息,请发表评论。我也可以提供*.exe进行尝试

记住:
sprintf(result_str,%.17f,result_d);
一切都很好,所以我想它不可能是数组越界的(我想)


附言:如果有人知道如何避免将双精度转换为字符串,请说出来。

您在这里占用了大量内存,有时不注意正确的维度。此外,如果您在堆栈上分配字符串
S
,所有早期返回都是la
返回计算
引入内存泄漏

您使用C99,那么为什么不使用可变长度数组(VLA)?您分配的数量很小,很容易放在堆栈上。您甚至可以尝试不分配任何内容,而是使用起始和结束指针操作原始(只读)数组。对于将输入读取到缓冲区,分配固定大小的内存与使用本地固定大小的缓冲区(如
s[100]
)相比没有任何优势

无论如何,主要问题是您的
calc
,其中您构造了一个新字符串,其中包含参数前的部分、子表达式的结果和参数后的部分。您对传递给
calcStr
的子字符串使用相同的缓冲区,并以16的精度打印结果。前者可以,因为分配字符串长度。后者不行,因为缓冲区可能太短,无法打印16位数字,但该缓冲区还必须保存串联的结果,即原始字符串长度减去子字符串长度加上大约20个字符(16位加上点加零终止符)

您可以使用单个
snprintf
表达式代替串联。使用变量字符串精度格式
%.*s
,您可以编写子字符串

这里有一个更好的
calc
,它可以大量猜测维度,并注意不泄漏临时buff
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#define LENGTH(x)  (sizeof(x) / sizeof(x[0]))

int hasPar (char *); //Überprüft, ob Klammern im String sind und gibt Position der ersten Klammer zurück
char* subStr (char* , char* , int , int ); //Funktion, die Substring zurückgibt
double calcStr(char *); //Wertet Terme ohne Klammern aus
double calc (char *); //Rekursive Berechnung des Terms


int main(/*int argc, char *argv[]*/) {
    char *s/*, c*/;
    double ergebnis;

    do {
        puts("Taschenrechner. Ignoriert alles, au\xe1""er 0-9,.,+,-,*,/,^,(,)");

        fflush(stdin);
        s= calloc(100,1);
        scanf("%99[^\n]", s); //[^\n] bedeutet, dass alle Zeichen außer Zeilenumbruch eingelesen werden sollen

        printf("Erkannter Ausdruck:\n%s\n", s);

        ergebnis= calc(s);  
        printf("Berechnetes Ergebnis:\n%f\n", ergebnis);


        puts("Erfolg!");;
        free(s);
        fflush(stdin);
        //if (getc(stdin) == 'c') {break;}
    } while (0);

    return 0;
}




int hasPar (char *s) {
    for (unsigned int k=0; k<strlen(s); k++) { //k verwendet, da mit i Interferenzen mit calc() aufgetreten sind
        if ( s[k] == '(' ) {
            return k;
        }
    }

    return (-1);
}



char* subStr (char* dest, char* src, int offset, int len) {
    int input_len = strlen (src);

    if ( offset+len > input_len ) { //Wenn Substring größer sein sollte als Usprungsstring oder Substring Null Zeichen enthalten soll
        return NULL;
    } else if (len <= 0) {
        dest[0]= '\0';
    }

    strncpy(dest, src + offset, len); //len Zeichen werden aus s ab offset in t kopiert
    dest[len]= '\0';
    return dest;
}



double calcStr (char *s) {
    char *t, *t_first;
    int len_s= strlen(s), len_first=0;
    t= calloc(len_s, sizeof(char)); //Kopie von s zum Arbeiten erstellen, Schritt 1
    strcpy(t,s); //Kopie von s zum Arbeiten erstellen, Schritt 2

    //ACHTUNG: REIHENFOLGE WICHTIG FÜR KORREKTE ANWENDUNG VON RECHENREGELN

    if(t[0] != '+') { //+ als unärer Operator
        t_first= strtok(t,"+"); //String auf + prüfen, Nach Ausführung von strtok: t_first: String bis exklusiv +
        len_first=strlen(t_first); //Länge des Ergebnisstrings berechnen zum Vergleich mit Länge des Ursprungsstrings
        if (len_first != len_s) { //Wenn Länge gleich, dann ist hier auch der Inhalt gleich, also kein Plus enthalten
            switch ( t_first[len_first-1] ) { //Wenn + unär, also vor dem + ein anderes OpSym
                case '+':
                    t[len_first-1]= '\0';
                    return calcStr(t) + (calcStr(s+len_first));
                case '-':
                    t[len_first-1]= '\0';
                    return calcStr(t) - (calcStr(s+len_first));
                case '*':
                    t[len_first-1]= '\0';
                    return calcStr(t) * (calcStr(s+len_first));
                case '/':
                    t[len_first-1]= '\0';
                    return calcStr(t) / (calcStr(s+len_first));
                case '^':
                    t[len_first-1]= '\0';
                    return pow(calcStr(t), (calcStr(s+len_first)));
                default:
                    return calcStr(t_first) + (calcStr(s+len_first+1)); //Rekursives Aufrufen der Strings links und rechts des Operationszeichens
            }
        }
    }

    strcpy(t,s); //da t bei Überprüfung auf + verändert wurde, Wiederherstellung der Arbeitskopie aus Ursprungsstring
    if(t[0] != '-') { //- als unärer Operator
        t_first= strtok(t,"-"); //analog oben
        len_first=strlen(t_first); 
        if (len_first != len_s) {
            switch ( t_first[len_first-1] ) {
                case '+':
                    t[len_first-1]= '\0';
                    return calcStr(t) + (calcStr(s+len_first));
                case '-':
                    t[len_first-1]= '\0';
                    return calcStr(t) - (calcStr(s+len_first));
                case '*':
                    t[len_first-1]= '\0';
                    return calcStr(t) * (calcStr(s+len_first));
                case '/':
                    t[len_first-1]= '\0';
                    return calcStr(t) / (calcStr(s+len_first));
                case '^':
                    t[len_first-1]= '\0';
                    return pow(calcStr(t), (calcStr(s+len_first)));
                default:
                    return calcStr(t_first) + (calcStr(s+len_first+1)); //Rekursives Aufrufen der Strings links und rechts des Operationszeichens
            }
        }
    }

    strcpy(t,s);
    t_first= strtok(t,"*");
    len_first=strlen(t_first);
    if (len_first != len_s) {
        return calcStr(t_first) * (calcStr(s+len_first+1));
    }

    strcpy(t,s); //analog
    t_first= strtok(t,"/");
    len_first=strlen(t_first);
    if (len_first != len_s) {
        return calcStr(t_first) * (1 / calcStr(s+len_first+1));
    }

    strcpy(t,s);
    t_first= strtok(t,"^");
    len_first=strlen(t_first);
    if (len_first != len_s) {
        if (t_first[len_first-1]=='e') {
            if (t_first[0]=='-') {
                return (-1)*exp(calcStr(s+len_first+1));
            } else if (t_first[0]=='+') {
                return exp(calcStr(s+len_first+1));
            } else {
                return pow(calcStr(t_first), (calcStr(s+len_first+1)));
            }
        }
    }

    return atof(s); //String ist bei keinem Operationszeichen zerfallen => String ist Zahl ; atof castet string zu double (aus stdlib.h)

}


double calc (char *s) {
    double result_d=0.;
    char *t, *result_str;
    int check=1, i=hasPar(s), j=0;
    if ( i == (-1) ) {
        return calcStr(s);
    } else {
        j= ++i; //j=++i ist Position des Chars nach der ersten öffnenden Klammer
        while (check > 0) {
            if (s[i] == '(') {
                check++;
            } else if (s[i] == ')') {
                check--;
            }
            i++;
        }   //Bestimmen der Länge der "obersten" Klammer, i ist Position des ersten Zeichens nach der Klammer

        t= calloc (strlen(s), sizeof(char)); //string to store substring in
result_str= calloc (strlen(s), sizeof(char)); //string to store result of parantheses-calculation in
result_d= calc (subStr (result_str, s, j, i-j-1)); //call the function we're already in
sprintf (result_str, "%-.16f", result_d); //cast result back to string, this is where I think the crash is caused
return calc (strcat (subStr (t, s, 0, j-1), strcat (result_str, s+i))); //recursive call of concatenated string described as above
    }
}
double calc(char *s)
{
    double result_d;
    char *t;
    int check = 1;
    int i = hasPar(s);
    int j = 0;

    if (i == -1) return calcStr(s);

    j = ++i;
    while (check > 0) {
        if (s[i] == '(') {
            check++;
        } else if (s[i] == ')') {
            check--;
        }
        i++;
    }

    t = calloc(strlen(s) + 32, sizeof(*t));
    result_d = calc(subStr(t, s, j, i - j - 1));

    snprintf(t, strlen(s) + 32, "%.*s%.16f%s",
        j - 1, s, result_d, s + i);

    result_d = calc(t);    
    free(t);

    return result_d;
}
double calcStr(char *s)
{
    char *t_first;
    int len_s = strlen(s), len_first = 0;
    char t[len_s + 1]; //
    strcpy(t, s);

    ...
}