c - 为什么我的 for 循环增量变化很大?
问题描述
我正在编写一个使用指针进行摊销的小程序
#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 上:
但是在 Unix 终端上,我的 for 循环中的增量变量在似乎是任意计数之后跳转:
在 Unix 终端上:
我相当肯定它可以完成所有我四处游荡的指针引用,但我不知道为什么它会影响 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;
在许多其他地方您可能会调用Undefined Behavior。您没有检查. scanf
如果您输入(意外或键盘的猫步)除数值以外的任何内容,则会发生匹配失败,不会读取任何字符,stdin
并且将跳过所有进一步的提示,然后您将继续进行不确定的处理将调用Undefined Behavior的值。
此外,如果您输入大于或小于零的值,您将调用7
Undefined Behavior。(结果也相当无趣)在接受输入时,不仅要验证转换是否成功,而且必须验证结果值是否在可用范围内——以避免未定义的行为,例如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
您是从to循环i <= number_of_payments
还是从i=0
to循环i < number_of_payments
很大程度上取决于您,但循环变量跟踪有效数组索引以保护数组边界是标准的。如上所述,支付号码的输出很简单通过调整i + 1
以产生所需的1,2,3...
)
(另请注意:在实践中,您希望避免对货币使用浮点数学。当您因舍入错误而亏损时,人们会非常沮丧。整数数学消除了这个问题)
如果您还有其他问题,请仔细查看并告诉我。
推荐阅读
- postgresql - Postgres:SELECT FOR UPDATE 在锁定释放后看不到新行
- kotlin - 未调用 ktor 中的应用程序级事件
- java - 无法将扫描仪转换为文件,扫描仪位于文件末尾,fileNotFoundException
- java - 在 IDEA 中,如何自动删除 .attach_pidxxx 文件
- swift4 - 如何处理标准和自定义 Swift 4 Decodable 属性的混合?
- r - 平均(或任何其他统计)计算省略零
- javascript - 从 href="path/of/file..WAV" 在 VLC 中打开文件
- reactjs - 如何从另一个用于 SSO 的 UI 应用程序获取 React 应用程序中的密钥(访问令牌)
- javascript - 如何使用 JS 打开一个 url 链接而不是嵌入“a”标签中的文本或图像
- java - jsf中如何使用I18n翻译异常信息