Reputation: 68
I am writing a small program for amortization using pointers
#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;
}
and came across an issue where the IDE I am writing in runs the program fine:
on Cloud9 IDE:
but on a Unix terminal, the increment variable in my for loop jumps after what seems to be an arbitrary count:
on Unix Terminal:
I am fairly certain it has something to do all of the pointer references I have whizzing around, but I don't know why it would affect the int variable i in the for-loop or why the IDE would handle the error so cleanly. Please educate!
Upvotes: 1
Views: 111
Reputation: 84607
You have a large number of problems with your code that are just waiting to cause problems. As you have discovered, you failed to protect your array bounds by incrementing your pointers with ptr += i
causing you to invoke Undefined Behavior by accessing and writing to memory outside of the storage for your arrays.
Take for example interest
and ipoint
:
double interest[7] = {0};
ipoint = &interest[0];
So your indexes within interest
array are as follows and ipoint
is initialized to point to the first element in interest
:
+---+---+---+---+---+---+---+
interest | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+---+---+---+---+---+---+---+
^
|
ipoint
Within your loop you are advancing ipoint += i
. On the first iteration of your loop, you advance ipoint
by one:
+---+---+---+---+---+---+---+
interest | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+---+---+---+---+---+---+---+
^
|
ipoint
Your second iteration, you advance by two:
+---+---+---+---+---+---+---+
interest | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+---+---+---+---+---+---+---+
^
|
ipoint
Your third iteration, you advance by three:
+---+---+---+---+---+---+---+
interest | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+---+---+---+---+---+---+---+
^
|
ipoint
When i = 4
you advance ipoint
beyond the end of your array, and you invoke Undefined Behavior when you assign a value to ipoint
and attempt to store the value in memory you do no own:
+---+---+---+---+---+---+---+---+---+---+---+
interest | 0 | 1 | 2 | 3 | 4 | 5 | 6 | out of bounds |
+---+---+---+---+---+---+---+---+---+---+---+
^
|
ipoint
Note: when Undefined Behavior is invoked, your code can appear to work normally, or it can SEGFAULT
(or anything in between), the operation of your code is simply Undefined and cannot be relied upon from that point forward.
What you need to do is advance each of your pointers by 1
, not 'i'
. That will insure you do not write beyond your array bounds. You can fix the problem by simply changing 'i'
to 1
for each, e.g.
ipoint += 1;
bpoint += 1;
ppoint += 1;
There are a number of other places you risk invoking Undefined Behavior. You fail to check the return of scanf
. If you input (by accident, or a cat steps of the keyboard) anything other than a numeric value, a matching failure will occur, no characters will be read from stdin
and all further prompts will be skipped and you will then proceed to process with indeterminate value which will invoke Undefined Behavior.
Further, if you enter more than 7
for the number_of_payments
or a value less than zero you invoke Undefined Behavior. (the results are also rather uninteresting for number_of_payments = 0
) When taking input, not only to you have to validate the conversion succeeds, but you must validate the resulting value is within a usable range -- to avoid Undefined Behavior, e.g.
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;
}
Lastly, while you are free to initialize ipoint = &interest[0];
, it is not necessary. Accessing an array, the array is converted to a pointer to its first element, so ipoint = interest;
is all that is required. There are other issues addressed in the comments below, but putting it altogether, you could do something like the following to insure behavior is defined throughout your code:
#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;
}
(note: whether you loop from i=1
to i <= number_of_payments
or from i=0
to i < number_of_payments
is largely up to you, but it is standard for the loop variable to track the valid array indexes to protect the bounds of the array. As above, the output for the payment number is simply adjust by i + 1
to produce the desired 1,2,3...
)
(also note: in practice you want to avoid using floating-point math for currency. People get really upset when you lose money due to rounding errors. Integer math eliminates that problem)
Look things over and let me know if you have further questions.
Upvotes: 2
Reputation: 3355
This is due to your += i;
statements in for-loop
, Change it to '++'
. When you use += i;
, Too much increment is happening for your pointers (not incremented by 1 but by i).
Modified 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
}
because +=i
consumes lot of memory (Too much increment) causing stack smashing error.
Output :-
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
Upvotes: 0