为什么我的for循环增量会发生巨大变化?

时间:2018-07-18 16:27:13

标签: c pointers for-loop

我正在编写一个使用指针进行摊销的小程序

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

double power(double a, double b);

int main(void)
{
    int loanAmount, number_of_payments, i = 0;
    double interestRate, monthlyInterestRate, monthlyPayment;
    printf("Enter amount of loan : $ ");
    scanf(" %i",&loanAmount);
    printf("Enter Interest rate per year : ");
    scanf(" %lf",&interestRate);
    printf("Enter number of payments : ");
    scanf(" %i",&number_of_payments);

    monthlyInterestRate = ((interestRate / 100) / 12); //AKA 'r' or rate.
    monthlyPayment = (monthlyInterestRate) * (loanAmount/(1 - 1/(power((1 + 
    monthlyInterestRate), number_of_payments))));

    double interest[7] = {0}; //Arbitrarily set to 7 - assuming less payments.
    double principal[7] = {0};
    double balance[7] = {0};
    balance[0] = loanAmount;
    double *ipoint,*ppoint,*bpoint,*bpointprev;

    ipoint = &interest[0];
    ppoint = &principal[0];
    bpoint = &balance[0];
    bpointprev = bpoint;

    printf("Monthly payment should be $ %lf\n",monthlyPayment);
    printf("# \t Payment \t Principal \t Interest \t Balance \n");
    for (i = 1; i <= number_of_payments; i++) {
        ipoint += i;
        bpoint += i;
        ppoint += i;

        *ipoint = *bpointprev * monthlyInterestRate;
        *ppoint = monthlyPayment - *ipoint;
        *bpoint = *bpointprev - *ppoint;

        printf("%i \t %.2f \t %.2f \t\t %.2f \t\t %.2f\n",i,monthlyPayment,*ppoint,*ipoint,*bpoint);

        bpointprev += i; //Iterates after logic for next calculation.
    }

    return 0;
}

double power(double a, double b)
{
    double i, sum = 1;
    for (i = 0; i < b; i++) {
        sum = sum * a;
    }
    return sum;
}

遇到一个问题,其中我正在编写的IDE可以很好地运行该程序:

在Cloud9 IDE上:

on Cloud9 IDE, Correct

但是在Unix终端上,我的for循环中的增量变量在似乎是任意计数之后跳转:

在Unix终端上:

on Unix Terminal, Error

我相当确定它可以做所有我绕过的指针引用,但是我不知道为什么它会影响for循环中的int变量i或为什么IDE会处理错误,所以干净地请教育!

2 个答案:

答案 0 :(得分:2)

您的代码存在大量问题,这些问题正等待引起问题。如您所知,您无法通过使用ptr += i来增加指针来保护数组边界,从而导致您无法访问并写入数组存储之外的内存来调用 Undefined Behavior

interestipoint为例:

double interest[7] = {0};
ipoint = &interest[0];

因此,您在interest数组中的索引如下,并且ipoint初始化为指向interest中的第一个元素:

            +---+---+---+---+---+---+---+
interest    | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
            +---+---+---+---+---+---+---+
            ^
            |
            ipoint

在循环中,您前进ipoint += i。在循环的第一次迭代中,您将ipoint前进了一个:

            +---+---+---+---+---+---+---+
interest    | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
            +---+---+---+---+---+---+---+
                ^
                |
                ipoint

第二次迭代,您将前进两步:

            +---+---+---+---+---+---+---+
interest    | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
            +---+---+---+---+---+---+---+
                        ^
                        |
                        ipoint

您的第三次迭代,您前进了三步:

            +---+---+---+---+---+---+---+
interest    | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
            +---+---+---+---+---+---+---+
                                    ^
                                    |
                                    ipoint

i = 4前进ipoint超出数组末尾时,当您为ipoint分配值并尝试存储时调用未定义行为您不拥有的内存中的值:

            +---+---+---+---+---+---+---+---+---+---+---+
interest    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | out of bounds |
            +---+---+---+---+---+---+---+---+---+---+---+
                                                    ^
                                                    |
                                                    ipoint

注意:当调用 Undefined Behavior 时,您的代码看起来可以正常工作,或者可以SEGFAULT(或两者之间的任何内容)进行操作您的代码只是 Undefined ,从那时起就不能再依赖了。

您需要做的是将每个指针前进1,而不是'i'。这样可以确保您不会写超出数组范围的内容。您可以通过简单地将'i'更改为1,例如

来解决此问题。
        ipoint += 1;
        bpoint += 1;
        ppoint += 1;

还有许多其他地方可能会导致您调用未定义行为。您无法检查scanf返回。如果您输入(偶然或键盘的敲击声)非数字值,则会发生匹配失败,将不会从stdin中读取任何字符,并且会进一步提示将被跳过,然后您将继续使用不确定的值进行处理,这将调用未定义行为

此外,如果您为7输入大于number_of_payments或小于零的值,则会调用未定义行为。 (对于number_of_payments = 0,结果也很有趣)。在进行输入时,不仅要验证转换是否成功,而且还必须验证结果值是否在可用范围内-避免 Undefined Behavior ,例如

    printf ("Enter number of payments : ");
    if (scanf (" %i", &number_of_payments) != 1) {
        fprintf (stderr, "error: invalide no. of payments.\n");
        return 1;
    }
    /* validate no. pmts in range */
    if (number_of_payments < 1 || number_of_payments > MAXPMTS) {
        fprintf (stderr, "error: no. of payments exceed MAXPMTS (%d)\n",
                MAXPMTS);
        return 1;
    }

最后,尽管您可以自由初始化ipoint = &interest[0];,但这不是必需的。访问数组时,该数组将转换为指向其第一个元素的指针,因此ipoint = interest;就足够了。下面的注释中还解决了其他问题,但总的来说,您可以执行以下操作以确保在整个代码中定义行为:

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

#define MAXPMTS 32  /* if you need a constant, define one (or more) */

double power (double a, double b);

int main (void)
{
    int loanAmount = 0,             /* initialize all variables */
        number_of_payments = 0, 
        i = 0;
    double interestRate = 0.0, 
        monthlyInterestRate = 0.0, 
        monthlyPayment = 0.0;

    /* numeric conversions consume leading whitespace (as does %s)
     * the ' ' in the conversion doesn't hurt, but isn't required.
     */

    printf ("Enter amount of loan : $ ");
    if (scanf (" %i", &loanAmount) != 1) {  /* validate conversion */
        fprintf (stderr, "error: invalid loan amount.\n");
        return 1;
    }

    printf ("Enter Interest rate per year : ");
    if (scanf (" %lf", &interestRate) != 1) {
        fprintf (stderr, "error: invalid interest rate.\n");
        return 1;
    }

    printf ("Enter number of payments : ");
    if (scanf (" %i", &number_of_payments) != 1) {
        fprintf (stderr, "error: invalide no. of payments.\n");
        return 1;
    }
    /* validate no. pmts in range */
    if (number_of_payments < 1 || number_of_payments > MAXPMTS) {
        fprintf (stderr, "error: no. of payments exceed MAXPMTS (%d)\n",
                MAXPMTS);
        return 1;
    }

    monthlyInterestRate = ((interestRate / 100) / 12); //AKA 'r' or rate.
    monthlyPayment = (monthlyInterestRate) * (loanAmount/(1 - 1/(power((1 + 
                        monthlyInterestRate), number_of_payments))));

    double interest[MAXPMTS] = {0};
    double principal[MAXPMTS] = {0};
    double balance[MAXPMTS] = {0};
    balance[0] = loanAmount;
    double  *ipoint = NULL,
            *ppoint = NULL,
            *bpoint = NULL,
            *bpointprev = NULL;

    ipoint = interest;
    ppoint = principal;
    bpoint = balance;
    bpointprev = bpoint;

    printf ("Monthly payment should be $ %lf\n", monthlyPayment);
    printf ("# \t Payment \t Principal \t Interest \t Balance \n");

    /* standard loop is from 0 to i < number_of_payments */
    for (i = 0; i < number_of_payments; i++) {
        ipoint += 1;
        bpoint += 1;
        ppoint += 1;

        *ipoint = *bpointprev * monthlyInterestRate;
        *ppoint = monthlyPayment - *ipoint;
        *bpoint = *bpointprev - *ppoint;

        /* adjust 'i + 1' for payment no. output */
        printf ("%i \t %.2f \t %.2f \t %.2f \t\t %.2f\n",
                i + 1, monthlyPayment, *ppoint, *ipoint, *bpoint);

        bpointprev += 1; //Iterates after logic for next calculation.
    }

    return 0;
}

double power(double a, double b)
{
    double i, sum = 1;
    for (i = 0; i < b; i++) {
        sum = sum * a;
    }
    return sum;
}

注意:是从i=1i <= number_of_payments还是从i=0i < number_of_payments进行循环,但这取决于您循环变量跟踪有效数组索引以保护数组边界的标准。如上所述,付款编号的输出只需通过i + 1进行调整即可产生所需的{{ 1}})

另请注意:在实践中,您想避免使用浮点数作为货币。当您由于舍入错误而亏损时,人们会感到非常沮丧。消除了这个问题)

仔细检查一下,如果还有其他问题,请告诉我。

答案 1 :(得分:0)

这归因于您在+= i;中的for-loop语句,将其更改为'++'。当您使用+= i;时,您的指针 发生了太多的增量(不是增加 1 而是增加了 i

修改后的for-loop:-

for (i = 1; i <= number_of_payments; i++)
{
    ipoint ++;  // not +=i
    bpoint ++;  // not +=i
    ppoint ++;  // not +=i

    *ipoint = *bpointprev * monthlyInterestRate;
    *ppoint = monthlyPayment - *ipoint;
    *bpoint = *bpointprev - *ppoint;

    printf("%i \t %.2f \t %.2f \t\t %.2f \t\t %.2f\n", i, monthlyPayment, *ppoint, *ipoint, *bpoint);

    bpointprev ++; //Iterates after logic for next calculation. not +=i
}

因为+=i消耗大量内存(增量过多),导致 堆栈粉碎 错误。

输出:-

Enter amount of loan : $ 2000
Enter Interest rate per year : 7.5
Enter number of payments : 6
Monthly payment should be $ 340.662858
#    Payment     Principal   Interest    Balance 
1    340.66      328.16          12.50       1671.84
2    340.66      330.21          10.45       1341.62
3    340.66      332.28          8.39        1009.35
4    340.66      334.35          6.31        674.99
5    340.66      336.44          4.22        338.55
6    340.66      338.55          2.12        0.00