c - 为什么我的 for 循环增量变化很大?

标签 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 会处理错误干净地。请多多指教!

最佳答案

您的代码存在大量问题,它们正等着引起问题。正如您所发现的,您未能通过使用 ptr += i 递增指针来保护数组边界。导致您通过访问和写入数组存储之外的内存来调用未定义的行为

interest为例和 ipoint :

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

注意:当未定义的行为被调用时,您的代码可能看起来正常工作,或者它可以 SEGFAULT (或介于两者之间的任何东西),您的代码操作只是未定义,从那时起就不能依赖了。

您需要做的是将每个指针前进 1 , 不是 'i' .这将确保您不会超出数组范围进行写入。您可以通过简单地更改 'i' 来解决问题。至 1对于每个,例如

        ipoint += 1;
        bpoint += 1;
        ppoint += 1;

还有许多其他地方可能会引发未定义行为。您未能检查 scanf返回 .如果您输入(不小心,或键盘的猫步)不是数字值,匹配失败将发生,将不会从stdin中读取任何字符。并且将跳过所有进一步的提示,然后您将继续处理不确定值,这将调用未定义的行为

此外,如果输入超过 7对于 number_of_payments或者您调用未定义行为 的小于零的值。 (number_of_payments = 0 的结果也相当无趣)当接受输入时,您不仅必须验证转换是否成功,还必须验证结果值是否在可用范围内——以避免未定义的行为,例如

    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=1 循环到 i <= number_of_payments 还是从 i=0 循环到 i < number_of_payments 很大程度上取决于您,但循环变量跟踪保护数组边界的有效数组索引。如上所述,支付编号的输出只需调整 i + 1 即可生成所需的 1,2,3... )

(另请注意:在实践中,您要避免对货币使用浮点数学。当您因舍入错误而亏损时,人们会非常沮丧。整数数学消除了这个问题)

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

关于c - 为什么我的 for 循环增量变化很大?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51406889/

相关文章:

c - 0 和 1 的排列

c++ - linux下c/c++的加密库和压缩库

C 关闭永远运行的 STDOUT

c - C 中的数组赋值使用指针运算

java - 在 toString 类中使用 for 循环返回多个数组

python - 带步骤的循环

c - 使用while循环在C中反转字符串

C++指针数组函数按地址传递

c++ - 在 C++ 中填充并返回读取的数据

javascript - 有没有办法修复我的 for 循环以启用或禁用基于单独列表的选项,但没有覆盖的潜力