Recursively creating and displaying a pyramid of people - C
up vote
1
down vote
favorite
Issue: I'm trying to take two arrays, and pass them through to a function where I'm supposed to recursively display a list of letters "people", and position them in a pyramid, where I display the weight on the knees of the bottom person as holding the half weight of the person above them, plus their own. So the person on top holds on their own weight, and the people on the side of the pyramid hold only half of 1 person's, everyone else hold's 2. I'm having an absolute nightmare of a time understand and figuring out how to create a recursive call that will handle this.
I've created two functions, one that generates how many people will be in a list after a user enters how many are on the bottom row; the next function just gives each person a letter based on how many people are in the total pyramid.
I've passed the two arrays (weights and letter-chars) to the function, along with the total amount of people.
How can I create a way to recursively generate a pyramid based on the weight on their knees?
Example:
How many people are on the bottom row? 4
Each person's own weight:
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Weight on each person's knees:
51.18
81.49 156.84
109.80 252.82 211.24
108.32 320.92 366.09 227.25
I ideally would like to attach their respective letter to the value, but that isn't necessary.
Not even sure if I have enough values for my function, or if I should be creating 2D arrays instead of single ones. Having trouble wrapping my head around the problem overall.
CODE:
The final function has errors, couldn't work out what to send to recursively call a proper output.
int bottomRow(int userNum);
float weightedKness(float arr, int totalpeople, char letters);
int main()
int bottomrow, total_people, k = 1, j = 1;
char letterPerson;
printf("How many people on the bottom row?n");
//if 7 is entered, the letter assignment will go out of alphabet's range
scanf("%d", &bottomrow);
//recursively finding the total number of people in the pyramid
//printf("Total people in pyramid is: %dn", bottomRow(bottomrow));
total_people = bottomRow(bottomrow);//total_people is an integer of all people
//populating array
float people[total_people];
for (; j <= total_people; ++j)
//insert randomized weight here between 250 and 50 (child - hopefully they aren't on the bottom)
people[j] = 50;
//printf("Weight of person %d is %.2flbsn", j, people[j]);
//printf("Total people is %dn", total_people);
//populating an array of chars to align to the array of weights
char assignedLetter[total_people];
letterPerson = 'A';
for (; k <= total_people; ++k)
assignedLetter[k] = letterPerson++;
//printf("%d is now %cn", k, array[k]);
for (int i = 1; i <= total_people; ++i)
// printf("Weight of person %c is %.2flbsn", assignedLetter[i], people[i]);
//weightedKness(people, total_people);
/* char letterPerson = '@';//holds an array of letters based on the amount of people
//starting the ascii value to 1 before capital A
for (; i <= total_people; ++i)
++letterPerson;
printf("Weight of person %c is %.2flbsn", letterPerson, people[i]);
//send through corresponding letter and weight based on i
*/
return 0;
int bottomRow(int userNum)
if (userNum == 1)
return 1;
//printf("Num is %dn", userNum);
//finding total of people in pyramid based on the given bottom
return bottomRow(userNum - 1) + userNum;
float weightedKness(float arr, int totalpeople, char letters)
int list_start = totalpeople;
if (totalpeople < 0)
return 0;
if (arr[1] && letters[1] )
return 0;
return weightedKness(arr[totalpeople-1],totalpeople-1, letters[totalpeople-1] + )
Thank you for any guidance you can send my way!
c recursion
add a comment |
up vote
1
down vote
favorite
Issue: I'm trying to take two arrays, and pass them through to a function where I'm supposed to recursively display a list of letters "people", and position them in a pyramid, where I display the weight on the knees of the bottom person as holding the half weight of the person above them, plus their own. So the person on top holds on their own weight, and the people on the side of the pyramid hold only half of 1 person's, everyone else hold's 2. I'm having an absolute nightmare of a time understand and figuring out how to create a recursive call that will handle this.
I've created two functions, one that generates how many people will be in a list after a user enters how many are on the bottom row; the next function just gives each person a letter based on how many people are in the total pyramid.
I've passed the two arrays (weights and letter-chars) to the function, along with the total amount of people.
How can I create a way to recursively generate a pyramid based on the weight on their knees?
Example:
How many people are on the bottom row? 4
Each person's own weight:
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Weight on each person's knees:
51.18
81.49 156.84
109.80 252.82 211.24
108.32 320.92 366.09 227.25
I ideally would like to attach their respective letter to the value, but that isn't necessary.
Not even sure if I have enough values for my function, or if I should be creating 2D arrays instead of single ones. Having trouble wrapping my head around the problem overall.
CODE:
The final function has errors, couldn't work out what to send to recursively call a proper output.
int bottomRow(int userNum);
float weightedKness(float arr, int totalpeople, char letters);
int main()
int bottomrow, total_people, k = 1, j = 1;
char letterPerson;
printf("How many people on the bottom row?n");
//if 7 is entered, the letter assignment will go out of alphabet's range
scanf("%d", &bottomrow);
//recursively finding the total number of people in the pyramid
//printf("Total people in pyramid is: %dn", bottomRow(bottomrow));
total_people = bottomRow(bottomrow);//total_people is an integer of all people
//populating array
float people[total_people];
for (; j <= total_people; ++j)
//insert randomized weight here between 250 and 50 (child - hopefully they aren't on the bottom)
people[j] = 50;
//printf("Weight of person %d is %.2flbsn", j, people[j]);
//printf("Total people is %dn", total_people);
//populating an array of chars to align to the array of weights
char assignedLetter[total_people];
letterPerson = 'A';
for (; k <= total_people; ++k)
assignedLetter[k] = letterPerson++;
//printf("%d is now %cn", k, array[k]);
for (int i = 1; i <= total_people; ++i)
// printf("Weight of person %c is %.2flbsn", assignedLetter[i], people[i]);
//weightedKness(people, total_people);
/* char letterPerson = '@';//holds an array of letters based on the amount of people
//starting the ascii value to 1 before capital A
for (; i <= total_people; ++i)
++letterPerson;
printf("Weight of person %c is %.2flbsn", letterPerson, people[i]);
//send through corresponding letter and weight based on i
*/
return 0;
int bottomRow(int userNum)
if (userNum == 1)
return 1;
//printf("Num is %dn", userNum);
//finding total of people in pyramid based on the given bottom
return bottomRow(userNum - 1) + userNum;
float weightedKness(float arr, int totalpeople, char letters)
int list_start = totalpeople;
if (totalpeople < 0)
return 0;
if (arr[1] && letters[1] )
return 0;
return weightedKness(arr[totalpeople-1],totalpeople-1, letters[totalpeople-1] + )
Thank you for any guidance you can send my way!
c recursion
3
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
Issue: I'm trying to take two arrays, and pass them through to a function where I'm supposed to recursively display a list of letters "people", and position them in a pyramid, where I display the weight on the knees of the bottom person as holding the half weight of the person above them, plus their own. So the person on top holds on their own weight, and the people on the side of the pyramid hold only half of 1 person's, everyone else hold's 2. I'm having an absolute nightmare of a time understand and figuring out how to create a recursive call that will handle this.
I've created two functions, one that generates how many people will be in a list after a user enters how many are on the bottom row; the next function just gives each person a letter based on how many people are in the total pyramid.
I've passed the two arrays (weights and letter-chars) to the function, along with the total amount of people.
How can I create a way to recursively generate a pyramid based on the weight on their knees?
Example:
How many people are on the bottom row? 4
Each person's own weight:
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Weight on each person's knees:
51.18
81.49 156.84
109.80 252.82 211.24
108.32 320.92 366.09 227.25
I ideally would like to attach their respective letter to the value, but that isn't necessary.
Not even sure if I have enough values for my function, or if I should be creating 2D arrays instead of single ones. Having trouble wrapping my head around the problem overall.
CODE:
The final function has errors, couldn't work out what to send to recursively call a proper output.
int bottomRow(int userNum);
float weightedKness(float arr, int totalpeople, char letters);
int main()
int bottomrow, total_people, k = 1, j = 1;
char letterPerson;
printf("How many people on the bottom row?n");
//if 7 is entered, the letter assignment will go out of alphabet's range
scanf("%d", &bottomrow);
//recursively finding the total number of people in the pyramid
//printf("Total people in pyramid is: %dn", bottomRow(bottomrow));
total_people = bottomRow(bottomrow);//total_people is an integer of all people
//populating array
float people[total_people];
for (; j <= total_people; ++j)
//insert randomized weight here between 250 and 50 (child - hopefully they aren't on the bottom)
people[j] = 50;
//printf("Weight of person %d is %.2flbsn", j, people[j]);
//printf("Total people is %dn", total_people);
//populating an array of chars to align to the array of weights
char assignedLetter[total_people];
letterPerson = 'A';
for (; k <= total_people; ++k)
assignedLetter[k] = letterPerson++;
//printf("%d is now %cn", k, array[k]);
for (int i = 1; i <= total_people; ++i)
// printf("Weight of person %c is %.2flbsn", assignedLetter[i], people[i]);
//weightedKness(people, total_people);
/* char letterPerson = '@';//holds an array of letters based on the amount of people
//starting the ascii value to 1 before capital A
for (; i <= total_people; ++i)
++letterPerson;
printf("Weight of person %c is %.2flbsn", letterPerson, people[i]);
//send through corresponding letter and weight based on i
*/
return 0;
int bottomRow(int userNum)
if (userNum == 1)
return 1;
//printf("Num is %dn", userNum);
//finding total of people in pyramid based on the given bottom
return bottomRow(userNum - 1) + userNum;
float weightedKness(float arr, int totalpeople, char letters)
int list_start = totalpeople;
if (totalpeople < 0)
return 0;
if (arr[1] && letters[1] )
return 0;
return weightedKness(arr[totalpeople-1],totalpeople-1, letters[totalpeople-1] + )
Thank you for any guidance you can send my way!
c recursion
Issue: I'm trying to take two arrays, and pass them through to a function where I'm supposed to recursively display a list of letters "people", and position them in a pyramid, where I display the weight on the knees of the bottom person as holding the half weight of the person above them, plus their own. So the person on top holds on their own weight, and the people on the side of the pyramid hold only half of 1 person's, everyone else hold's 2. I'm having an absolute nightmare of a time understand and figuring out how to create a recursive call that will handle this.
I've created two functions, one that generates how many people will be in a list after a user enters how many are on the bottom row; the next function just gives each person a letter based on how many people are in the total pyramid.
I've passed the two arrays (weights and letter-chars) to the function, along with the total amount of people.
How can I create a way to recursively generate a pyramid based on the weight on their knees?
Example:
How many people are on the bottom row? 4
Each person's own weight:
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Weight on each person's knees:
51.18
81.49 156.84
109.80 252.82 211.24
108.32 320.92 366.09 227.25
I ideally would like to attach their respective letter to the value, but that isn't necessary.
Not even sure if I have enough values for my function, or if I should be creating 2D arrays instead of single ones. Having trouble wrapping my head around the problem overall.
CODE:
The final function has errors, couldn't work out what to send to recursively call a proper output.
int bottomRow(int userNum);
float weightedKness(float arr, int totalpeople, char letters);
int main()
int bottomrow, total_people, k = 1, j = 1;
char letterPerson;
printf("How many people on the bottom row?n");
//if 7 is entered, the letter assignment will go out of alphabet's range
scanf("%d", &bottomrow);
//recursively finding the total number of people in the pyramid
//printf("Total people in pyramid is: %dn", bottomRow(bottomrow));
total_people = bottomRow(bottomrow);//total_people is an integer of all people
//populating array
float people[total_people];
for (; j <= total_people; ++j)
//insert randomized weight here between 250 and 50 (child - hopefully they aren't on the bottom)
people[j] = 50;
//printf("Weight of person %d is %.2flbsn", j, people[j]);
//printf("Total people is %dn", total_people);
//populating an array of chars to align to the array of weights
char assignedLetter[total_people];
letterPerson = 'A';
for (; k <= total_people; ++k)
assignedLetter[k] = letterPerson++;
//printf("%d is now %cn", k, array[k]);
for (int i = 1; i <= total_people; ++i)
// printf("Weight of person %c is %.2flbsn", assignedLetter[i], people[i]);
//weightedKness(people, total_people);
/* char letterPerson = '@';//holds an array of letters based on the amount of people
//starting the ascii value to 1 before capital A
for (; i <= total_people; ++i)
++letterPerson;
printf("Weight of person %c is %.2flbsn", letterPerson, people[i]);
//send through corresponding letter and weight based on i
*/
return 0;
int bottomRow(int userNum)
if (userNum == 1)
return 1;
//printf("Num is %dn", userNum);
//finding total of people in pyramid based on the given bottom
return bottomRow(userNum - 1) + userNum;
float weightedKness(float arr, int totalpeople, char letters)
int list_start = totalpeople;
if (totalpeople < 0)
return 0;
if (arr[1] && letters[1] )
return 0;
return weightedKness(arr[totalpeople-1],totalpeople-1, letters[totalpeople-1] + )
Thank you for any guidance you can send my way!
c recursion
c recursion
edited Nov 12 at 2:52
asked Nov 12 at 2:36
user7823016
686
686
3
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49
add a comment |
3
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49
3
3
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
A recursive function, requires (1) a recursive test condition that will stop the recursion, and (2) a recursive call within the function.
Note... avoid using recursion where a simple procedural approach will work. Each recursive call is itself a separate function call requiring a separate function stack, local variables, etc... If your recursion requires too many recursive calls, you can exhaust your available memory rather easily. Make sure you understand how many times your function can be called before choosing a recursive solution.
That said, there are some problems where recursion provides a very elegant solution without the potential for memory exhaustion. Permutations, factorials, etc... are good examples. Recursive functions also make great homework questions because it is a necessary area of programming, and it requires that you think carefully about what happens as you are making the recursive calls -- as well as what happens after your recursive test condition is met (and you have to "unwind" from the recursion as each recursive call returns.
In your case you are given the array containing the weight for each person and pass that along with a separate array of the same size to compute the weight of the pyramid of people at each point in the pyramid. You have to output both the array of people and the array of the weight at each point in the pyramid.
Your recursive test condition is fairly simple, you are going to make a recursive call to cover each row in your people
array in order to compute the weights. You will be passing the current row as a function parameter, so your recursive test is simply if your row count has reached the size of your pyramid (size of your arrays).
On the initial and each recursive call, you need to (1) print the people array and (2) compute the weights based on the people above, before you make your recursive call in your function. Then after making your recursive call, you will need to print the weights you have computed -- but careful, as you return from each recursive call and "unwind" the recursion, the row counter you use will start at its limit and return toward zero. This will take a bit of planning in handling your array indexes after the recursive call.
For example, setting up your recursive function, you could think something like:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* handle all computations/print people here */
pyramid (people, weight, row + 1); /* recursive call */
/* print your weights here (but reverse the indexes) */
Now that you have an outline, working though how to handle writing the function is no different that writing any function. You think about any special conditions that have to be met (e.g. 1st row, no weight on top and only the person to count, 1st and last elements of the array on edge of pyramid only carries weight of single person above, etc..) So it simply becomes a matter of then incorporating your special conditions in the flow of your recursive function -- but remember, you only have one recursive function, so the function itself must handle every one of those special conditions each time it is called -- whether or not they apply to the current row-count.
The approach here is pretty straight forward, you want to check if you are on the first row of the pyramid and just copy the weight to the weight
array. For all other rows, you will need three conditions (1) handle the 1st element (left-edge) calculation; (2) handle all inner-positions in the pyramid (if any), and (3) handle the last element in the row (right-edge). You will both print and compute weights in the same fashion, before making your recursive call, e.g.
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
...
Now what happens when row == SIZEP
?? Your recursive function begins to return from the recursive call. So if you take the last recursive call where you pass row + 1
and row == SIZEP
, then the returning and unwinding begins immediately after your recursive call. What will be the value of row
here? (if you made the recursive call passing row + 1
and it returned on your test condition, then row
hasn't changed it will still be the last row (e.g. 3
in your case).
All that happened in the final recursive call was:
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition -- FAILED */
and you are now back in at the previous call ready to begin the returns and unwinding with row
holding the index to the final row in your arrays. What happens below the recursive call is simply the printing of your weight
array. However, you don't want to print it upside-down, so you need to handle the value of row
to map your unwinding indexes to the same 0-3
instead of 3-0
.
Using a simple variable to reverse the row (say revrow
-- I don't like typing) and using subtracting the current row
value (+1
) from SIZEP
and using that as your index to print the weights
, e.g.
...
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
}
You don't have to worry about passing anything, as your recursive calls unwind, the value of row
following each return will be the value it had in that function before the recursive call was made.
Putting it altogether in a short example, you would have something like the following:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
int main (void)
double people[SIZEP][SIZEP] = 51.18 ,
55.90, 131.25 ,
69.05, 133.66, 132.82 ,
53.43, 139.61, 134.06, 121.63 ,
weight[SIZEP][SIZEP] = 0 ;
pyramid (people, weight, 0);
return 0;
Example Use/Output
$ ./bin/pyramidrecurse
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
Give it some thought. Recursive functions require a slightly different way of thinking to make them make sense. But, when you realize you are just repeated making calls to the same function on the way in, and then handling the return from each of those calls as your recursion unwinds -- it will start to sink in.
Using a VLA with C99+
Rather than declaring an integer constant SIZEP
to declare both people
and weight
as arrays with automatic storage type, you have two other options that will allow you to size both arrays based on user-input. The first, if you have a C99+ compile, is to use a Variable Length Array. While arrays with automatic storage duration require an Integer Constant as the size for the array declaration, a VLA allows a variable to be used to size the array. (with the caveat that a VLA cannot be initialized with the normal initializer e.g. 0
, etc., instead you must manually initialize a VLA with memset
or a loop)
Further, as noted in the comments, when passing a VLA as a parameter, you must pass the size as a parameter before the array so the size of the array is know, e.g. function (int size, vla1[size], vla2[size])
. If size
was not passed before the arrays, then size
would not be known in vla1[size]
, etc.
The primary difference between using a VLA or dynamically allocating with malloc
, etc.. is that will a VLA, you still must obtain input of the size of the arrays before they are declared. VLAs cannot be resized. malloc
and realloc
provide the ability to dynamically grow the amount of storage for your arrays without knowing the number of rows or elements before hand. You simply allocate some reasonably anticipated size, and if the size is reached before you have finished reading all input, you can call realloc
for that block of memory and allocate more on-the-fly (but it does require a few more lines of code. It's not any more difficult, it just takes few more variables to track how much memory you have allocated, how much you have used, and when used == allocated
, you realloc
, update the variable with the new size allocate, and keep on going.
In the example that follows, the size
is read as the first input (from a file below, but it could just as easily be input on stdin
), VLAs are created using size
, the VLAs are set to all zero with memset
and then passed to the recursive pyramid
function. note also that the recursive function has been split into three-functions to simplify understanding the actual recursive pyramid
function while the computation and printing was moved to the pre-recursive call compute
function and to the post-recursion unwinding in the unwind
function.
(it makes no difference functionally, but "factoring" your code into simply digestible bits of code can help keep things straight)
#include <stdio.h>
#include <string.h>
void compute (int size, double (*people)[size], double (*weight)[size], int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double (*weight)[size], int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (size, people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
return 1;
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
return 1;
double people[size][size], /* declare VLAs, but you */
weight[size][size]; /* can't initialize VLAs */
memset (people, 0, size * sizeof *people); /* zero both arrays */
memset (weight, 0, size * sizeof *weight);
for (int i = 0; i < size; i++)
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
return 0;
Example Input File
$ cat dat/pyramidweight.txt
4
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Example Use/Output
$ ./bin/pyramidrecursefnvla dat/pyramidweight.txt
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
malloc
and calloc
- Completing the Storage Puzzle
As mentioned above, the primary advantage of using malloc, calloc, realloc
to dynamically allocate storage is that there is no need for size
to be known before-hand. Meaning, if the user (or your file) doesn't include size
, you can simply dynamically allocate some reasonable number of pointers and allocate storage for some number of double
values per-pointer and start reading your data.
There is no requirement that each block allocated to hold your row value have the same number of double
values allocated as the row before or after. (you can enforce this requirement by storing the number of values read in your first line of data and compare that to every other line read if, for instance, you are reading a square matrix worth of data). But here, since you have size
as input, use it, it simplifies the process by eliminating the guess work for "How many pointers to initially allocate for?" (we know, size
of them)
Before going further, we have used the word "pointer" in our discussion about dynamically allocating, while we used "array" in our other two cases. This is an important distinction. An array is not a pointer, and a pointer is not an array, but understand that an array is converted to a pointer on access. Specifically: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
(p3) Except when it is the operand of the
sizeof
operator, the
_Alignof
operator, or the unary'&'
operator, or is a string
literal used to initialize an array, an expression that has type
"array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is
not an lvalue.
Since you are passing a pointer to pointer to double
, your parameters for your functions for people
and weight
will need to change from a pointer to array of double
to a pointer to pointer to double
, e.g.
void pyramid (int size, double **people, double **weight, int row)
If you Allocate... You must Validate!
When allocating dynamically, malloc, calloc & realloc
can, and do fail on memory exhaustion. When they fail, they each return NULL
indicating failure. Otherwise, they return the starting address for the allocated block of memory which you assign to your pointer and can then access using array notation -- or pointer notation (e.g. people[2]
in array notation is equivalent to *(people + 2)
in pointer notation -- and since arrays are converted to pointers on access, you can use either notation with arrays as well)
What changes in main()
? Your declarations for people
and weight
to begin with, e.g.
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
After taking size
as input, allocate your pointers (size
for each), e.g.
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
(note: when allocating storage you need size
multiplied the sizeof
whatever you are allocating. While you can use the type, e.g. sizeof (double*)
, it is always better to use the dereferenced variable itself to size the type. E.g., if you are allocating for people
which is double**
, if you use sizeof *people
, then *people
is just double*
. This will ensure you never get the type-size wrong. With complex types mistakes are likely and common guessing whether the type is, e.g., a pointer to array of or array of pointers to, etc...)
At this point you have size
pointers for each allocated. While you could just make guess at the number of values, allocate that number of doubles, and realloc
if you reach the aljust located limit -- (but that would require re-working your read of the input from value-at-a-time to line-at-a-time and then parsing -- which I leave to you if you are curious), but here since we know we are modeling an array[size][size]
and we have array[size]
pointers allocated, all that remains is allocating size
doubles for each pointer, e.g.
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
(note again the type-size with sizeof *people[i]
and sizeof *weight[i]
)
Putting it altogether, you end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compute (double **people, double **weight, int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double **weight, int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double **people, double **weight, int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
exit (EXIT_FAILURE);
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
exit (EXIT_FAILURE);
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
/* the rest is the same - except for parameter types for pyramid */
for (int i = 0; i < size; i++) /* read people values from file */
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
for (int i = 0; i < size; i++)
free (people[i]);
free (weight[i]);
free (people);
free (weight);
return 0;
Example Input & Use/Output
The input and output is the same.
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851== Memcheck, a memory error detector
==1851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1851== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1851== Command: ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851==
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
==1851==
==1851== HEAP SUMMARY:
==1851== in use at exit: 0 bytes in 0 blocks
==1851== total heap usage: 11 allocs, 11 frees, 872 bytes allocated
==1851==
==1851== All heap blocks were freed -- no leaks are possible
==1851==
==1851== For counts of detected and suppressed errors, rerun with: -v
==1851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define yourpeople
andweight
arrays inmain()
. You can just pick a size that is sufficient (e.g.50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate withmalloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enterssize
and you declare, e.g.people[size][size]
, you will need to passvoid pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and usesize
notSIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must passint size
as a parameter before the arrays, e.g.double (*people)[size]
or the function will not know whatdouble (*people)[size]
is if you try and pass that parameter first (doesn't know whatsize
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.
– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilizemalloc
syntactically in place of thedefine SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?
– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?malloc
is simple enough. Instead of declaringpeople
andweight
asdouble
, you would declare each as a pointer to pointer todouble
(e.g.double **people;
) and then allocatesize
pointers (e.g.people = malloc (size * sizeof *people);
(sincepeople
isdouble**
,*people
isdouble*
used withsizeof
). Then you allocatesize
number ofdouble
to hold the values and assign each block allocated to each pointer (e.g.for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.
– David C. Rankin
Nov 12 at 23:44
|
show 7 more comments
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53255313%2frecursively-creating-and-displaying-a-pyramid-of-people-c%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
A recursive function, requires (1) a recursive test condition that will stop the recursion, and (2) a recursive call within the function.
Note... avoid using recursion where a simple procedural approach will work. Each recursive call is itself a separate function call requiring a separate function stack, local variables, etc... If your recursion requires too many recursive calls, you can exhaust your available memory rather easily. Make sure you understand how many times your function can be called before choosing a recursive solution.
That said, there are some problems where recursion provides a very elegant solution without the potential for memory exhaustion. Permutations, factorials, etc... are good examples. Recursive functions also make great homework questions because it is a necessary area of programming, and it requires that you think carefully about what happens as you are making the recursive calls -- as well as what happens after your recursive test condition is met (and you have to "unwind" from the recursion as each recursive call returns.
In your case you are given the array containing the weight for each person and pass that along with a separate array of the same size to compute the weight of the pyramid of people at each point in the pyramid. You have to output both the array of people and the array of the weight at each point in the pyramid.
Your recursive test condition is fairly simple, you are going to make a recursive call to cover each row in your people
array in order to compute the weights. You will be passing the current row as a function parameter, so your recursive test is simply if your row count has reached the size of your pyramid (size of your arrays).
On the initial and each recursive call, you need to (1) print the people array and (2) compute the weights based on the people above, before you make your recursive call in your function. Then after making your recursive call, you will need to print the weights you have computed -- but careful, as you return from each recursive call and "unwind" the recursion, the row counter you use will start at its limit and return toward zero. This will take a bit of planning in handling your array indexes after the recursive call.
For example, setting up your recursive function, you could think something like:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* handle all computations/print people here */
pyramid (people, weight, row + 1); /* recursive call */
/* print your weights here (but reverse the indexes) */
Now that you have an outline, working though how to handle writing the function is no different that writing any function. You think about any special conditions that have to be met (e.g. 1st row, no weight on top and only the person to count, 1st and last elements of the array on edge of pyramid only carries weight of single person above, etc..) So it simply becomes a matter of then incorporating your special conditions in the flow of your recursive function -- but remember, you only have one recursive function, so the function itself must handle every one of those special conditions each time it is called -- whether or not they apply to the current row-count.
The approach here is pretty straight forward, you want to check if you are on the first row of the pyramid and just copy the weight to the weight
array. For all other rows, you will need three conditions (1) handle the 1st element (left-edge) calculation; (2) handle all inner-positions in the pyramid (if any), and (3) handle the last element in the row (right-edge). You will both print and compute weights in the same fashion, before making your recursive call, e.g.
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
...
Now what happens when row == SIZEP
?? Your recursive function begins to return from the recursive call. So if you take the last recursive call where you pass row + 1
and row == SIZEP
, then the returning and unwinding begins immediately after your recursive call. What will be the value of row
here? (if you made the recursive call passing row + 1
and it returned on your test condition, then row
hasn't changed it will still be the last row (e.g. 3
in your case).
All that happened in the final recursive call was:
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition -- FAILED */
and you are now back in at the previous call ready to begin the returns and unwinding with row
holding the index to the final row in your arrays. What happens below the recursive call is simply the printing of your weight
array. However, you don't want to print it upside-down, so you need to handle the value of row
to map your unwinding indexes to the same 0-3
instead of 3-0
.
Using a simple variable to reverse the row (say revrow
-- I don't like typing) and using subtracting the current row
value (+1
) from SIZEP
and using that as your index to print the weights
, e.g.
...
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
}
You don't have to worry about passing anything, as your recursive calls unwind, the value of row
following each return will be the value it had in that function before the recursive call was made.
Putting it altogether in a short example, you would have something like the following:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
int main (void)
double people[SIZEP][SIZEP] = 51.18 ,
55.90, 131.25 ,
69.05, 133.66, 132.82 ,
53.43, 139.61, 134.06, 121.63 ,
weight[SIZEP][SIZEP] = 0 ;
pyramid (people, weight, 0);
return 0;
Example Use/Output
$ ./bin/pyramidrecurse
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
Give it some thought. Recursive functions require a slightly different way of thinking to make them make sense. But, when you realize you are just repeated making calls to the same function on the way in, and then handling the return from each of those calls as your recursion unwinds -- it will start to sink in.
Using a VLA with C99+
Rather than declaring an integer constant SIZEP
to declare both people
and weight
as arrays with automatic storage type, you have two other options that will allow you to size both arrays based on user-input. The first, if you have a C99+ compile, is to use a Variable Length Array. While arrays with automatic storage duration require an Integer Constant as the size for the array declaration, a VLA allows a variable to be used to size the array. (with the caveat that a VLA cannot be initialized with the normal initializer e.g. 0
, etc., instead you must manually initialize a VLA with memset
or a loop)
Further, as noted in the comments, when passing a VLA as a parameter, you must pass the size as a parameter before the array so the size of the array is know, e.g. function (int size, vla1[size], vla2[size])
. If size
was not passed before the arrays, then size
would not be known in vla1[size]
, etc.
The primary difference between using a VLA or dynamically allocating with malloc
, etc.. is that will a VLA, you still must obtain input of the size of the arrays before they are declared. VLAs cannot be resized. malloc
and realloc
provide the ability to dynamically grow the amount of storage for your arrays without knowing the number of rows or elements before hand. You simply allocate some reasonably anticipated size, and if the size is reached before you have finished reading all input, you can call realloc
for that block of memory and allocate more on-the-fly (but it does require a few more lines of code. It's not any more difficult, it just takes few more variables to track how much memory you have allocated, how much you have used, and when used == allocated
, you realloc
, update the variable with the new size allocate, and keep on going.
In the example that follows, the size
is read as the first input (from a file below, but it could just as easily be input on stdin
), VLAs are created using size
, the VLAs are set to all zero with memset
and then passed to the recursive pyramid
function. note also that the recursive function has been split into three-functions to simplify understanding the actual recursive pyramid
function while the computation and printing was moved to the pre-recursive call compute
function and to the post-recursion unwinding in the unwind
function.
(it makes no difference functionally, but "factoring" your code into simply digestible bits of code can help keep things straight)
#include <stdio.h>
#include <string.h>
void compute (int size, double (*people)[size], double (*weight)[size], int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double (*weight)[size], int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (size, people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
return 1;
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
return 1;
double people[size][size], /* declare VLAs, but you */
weight[size][size]; /* can't initialize VLAs */
memset (people, 0, size * sizeof *people); /* zero both arrays */
memset (weight, 0, size * sizeof *weight);
for (int i = 0; i < size; i++)
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
return 0;
Example Input File
$ cat dat/pyramidweight.txt
4
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Example Use/Output
$ ./bin/pyramidrecursefnvla dat/pyramidweight.txt
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
malloc
and calloc
- Completing the Storage Puzzle
As mentioned above, the primary advantage of using malloc, calloc, realloc
to dynamically allocate storage is that there is no need for size
to be known before-hand. Meaning, if the user (or your file) doesn't include size
, you can simply dynamically allocate some reasonable number of pointers and allocate storage for some number of double
values per-pointer and start reading your data.
There is no requirement that each block allocated to hold your row value have the same number of double
values allocated as the row before or after. (you can enforce this requirement by storing the number of values read in your first line of data and compare that to every other line read if, for instance, you are reading a square matrix worth of data). But here, since you have size
as input, use it, it simplifies the process by eliminating the guess work for "How many pointers to initially allocate for?" (we know, size
of them)
Before going further, we have used the word "pointer" in our discussion about dynamically allocating, while we used "array" in our other two cases. This is an important distinction. An array is not a pointer, and a pointer is not an array, but understand that an array is converted to a pointer on access. Specifically: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
(p3) Except when it is the operand of the
sizeof
operator, the
_Alignof
operator, or the unary'&'
operator, or is a string
literal used to initialize an array, an expression that has type
"array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is
not an lvalue.
Since you are passing a pointer to pointer to double
, your parameters for your functions for people
and weight
will need to change from a pointer to array of double
to a pointer to pointer to double
, e.g.
void pyramid (int size, double **people, double **weight, int row)
If you Allocate... You must Validate!
When allocating dynamically, malloc, calloc & realloc
can, and do fail on memory exhaustion. When they fail, they each return NULL
indicating failure. Otherwise, they return the starting address for the allocated block of memory which you assign to your pointer and can then access using array notation -- or pointer notation (e.g. people[2]
in array notation is equivalent to *(people + 2)
in pointer notation -- and since arrays are converted to pointers on access, you can use either notation with arrays as well)
What changes in main()
? Your declarations for people
and weight
to begin with, e.g.
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
After taking size
as input, allocate your pointers (size
for each), e.g.
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
(note: when allocating storage you need size
multiplied the sizeof
whatever you are allocating. While you can use the type, e.g. sizeof (double*)
, it is always better to use the dereferenced variable itself to size the type. E.g., if you are allocating for people
which is double**
, if you use sizeof *people
, then *people
is just double*
. This will ensure you never get the type-size wrong. With complex types mistakes are likely and common guessing whether the type is, e.g., a pointer to array of or array of pointers to, etc...)
At this point you have size
pointers for each allocated. While you could just make guess at the number of values, allocate that number of doubles, and realloc
if you reach the aljust located limit -- (but that would require re-working your read of the input from value-at-a-time to line-at-a-time and then parsing -- which I leave to you if you are curious), but here since we know we are modeling an array[size][size]
and we have array[size]
pointers allocated, all that remains is allocating size
doubles for each pointer, e.g.
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
(note again the type-size with sizeof *people[i]
and sizeof *weight[i]
)
Putting it altogether, you end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compute (double **people, double **weight, int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double **weight, int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double **people, double **weight, int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
exit (EXIT_FAILURE);
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
exit (EXIT_FAILURE);
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
/* the rest is the same - except for parameter types for pyramid */
for (int i = 0; i < size; i++) /* read people values from file */
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
for (int i = 0; i < size; i++)
free (people[i]);
free (weight[i]);
free (people);
free (weight);
return 0;
Example Input & Use/Output
The input and output is the same.
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851== Memcheck, a memory error detector
==1851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1851== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1851== Command: ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851==
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
==1851==
==1851== HEAP SUMMARY:
==1851== in use at exit: 0 bytes in 0 blocks
==1851== total heap usage: 11 allocs, 11 frees, 872 bytes allocated
==1851==
==1851== All heap blocks were freed -- no leaks are possible
==1851==
==1851== For counts of detected and suppressed errors, rerun with: -v
==1851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define yourpeople
andweight
arrays inmain()
. You can just pick a size that is sufficient (e.g.50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate withmalloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enterssize
and you declare, e.g.people[size][size]
, you will need to passvoid pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and usesize
notSIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must passint size
as a parameter before the arrays, e.g.double (*people)[size]
or the function will not know whatdouble (*people)[size]
is if you try and pass that parameter first (doesn't know whatsize
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.
– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilizemalloc
syntactically in place of thedefine SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?
– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?malloc
is simple enough. Instead of declaringpeople
andweight
asdouble
, you would declare each as a pointer to pointer todouble
(e.g.double **people;
) and then allocatesize
pointers (e.g.people = malloc (size * sizeof *people);
(sincepeople
isdouble**
,*people
isdouble*
used withsizeof
). Then you allocatesize
number ofdouble
to hold the values and assign each block allocated to each pointer (e.g.for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.
– David C. Rankin
Nov 12 at 23:44
|
show 7 more comments
up vote
1
down vote
accepted
A recursive function, requires (1) a recursive test condition that will stop the recursion, and (2) a recursive call within the function.
Note... avoid using recursion where a simple procedural approach will work. Each recursive call is itself a separate function call requiring a separate function stack, local variables, etc... If your recursion requires too many recursive calls, you can exhaust your available memory rather easily. Make sure you understand how many times your function can be called before choosing a recursive solution.
That said, there are some problems where recursion provides a very elegant solution without the potential for memory exhaustion. Permutations, factorials, etc... are good examples. Recursive functions also make great homework questions because it is a necessary area of programming, and it requires that you think carefully about what happens as you are making the recursive calls -- as well as what happens after your recursive test condition is met (and you have to "unwind" from the recursion as each recursive call returns.
In your case you are given the array containing the weight for each person and pass that along with a separate array of the same size to compute the weight of the pyramid of people at each point in the pyramid. You have to output both the array of people and the array of the weight at each point in the pyramid.
Your recursive test condition is fairly simple, you are going to make a recursive call to cover each row in your people
array in order to compute the weights. You will be passing the current row as a function parameter, so your recursive test is simply if your row count has reached the size of your pyramid (size of your arrays).
On the initial and each recursive call, you need to (1) print the people array and (2) compute the weights based on the people above, before you make your recursive call in your function. Then after making your recursive call, you will need to print the weights you have computed -- but careful, as you return from each recursive call and "unwind" the recursion, the row counter you use will start at its limit and return toward zero. This will take a bit of planning in handling your array indexes after the recursive call.
For example, setting up your recursive function, you could think something like:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* handle all computations/print people here */
pyramid (people, weight, row + 1); /* recursive call */
/* print your weights here (but reverse the indexes) */
Now that you have an outline, working though how to handle writing the function is no different that writing any function. You think about any special conditions that have to be met (e.g. 1st row, no weight on top and only the person to count, 1st and last elements of the array on edge of pyramid only carries weight of single person above, etc..) So it simply becomes a matter of then incorporating your special conditions in the flow of your recursive function -- but remember, you only have one recursive function, so the function itself must handle every one of those special conditions each time it is called -- whether or not they apply to the current row-count.
The approach here is pretty straight forward, you want to check if you are on the first row of the pyramid and just copy the weight to the weight
array. For all other rows, you will need three conditions (1) handle the 1st element (left-edge) calculation; (2) handle all inner-positions in the pyramid (if any), and (3) handle the last element in the row (right-edge). You will both print and compute weights in the same fashion, before making your recursive call, e.g.
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
...
Now what happens when row == SIZEP
?? Your recursive function begins to return from the recursive call. So if you take the last recursive call where you pass row + 1
and row == SIZEP
, then the returning and unwinding begins immediately after your recursive call. What will be the value of row
here? (if you made the recursive call passing row + 1
and it returned on your test condition, then row
hasn't changed it will still be the last row (e.g. 3
in your case).
All that happened in the final recursive call was:
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition -- FAILED */
and you are now back in at the previous call ready to begin the returns and unwinding with row
holding the index to the final row in your arrays. What happens below the recursive call is simply the printing of your weight
array. However, you don't want to print it upside-down, so you need to handle the value of row
to map your unwinding indexes to the same 0-3
instead of 3-0
.
Using a simple variable to reverse the row (say revrow
-- I don't like typing) and using subtracting the current row
value (+1
) from SIZEP
and using that as your index to print the weights
, e.g.
...
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
}
You don't have to worry about passing anything, as your recursive calls unwind, the value of row
following each return will be the value it had in that function before the recursive call was made.
Putting it altogether in a short example, you would have something like the following:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
int main (void)
double people[SIZEP][SIZEP] = 51.18 ,
55.90, 131.25 ,
69.05, 133.66, 132.82 ,
53.43, 139.61, 134.06, 121.63 ,
weight[SIZEP][SIZEP] = 0 ;
pyramid (people, weight, 0);
return 0;
Example Use/Output
$ ./bin/pyramidrecurse
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
Give it some thought. Recursive functions require a slightly different way of thinking to make them make sense. But, when you realize you are just repeated making calls to the same function on the way in, and then handling the return from each of those calls as your recursion unwinds -- it will start to sink in.
Using a VLA with C99+
Rather than declaring an integer constant SIZEP
to declare both people
and weight
as arrays with automatic storage type, you have two other options that will allow you to size both arrays based on user-input. The first, if you have a C99+ compile, is to use a Variable Length Array. While arrays with automatic storage duration require an Integer Constant as the size for the array declaration, a VLA allows a variable to be used to size the array. (with the caveat that a VLA cannot be initialized with the normal initializer e.g. 0
, etc., instead you must manually initialize a VLA with memset
or a loop)
Further, as noted in the comments, when passing a VLA as a parameter, you must pass the size as a parameter before the array so the size of the array is know, e.g. function (int size, vla1[size], vla2[size])
. If size
was not passed before the arrays, then size
would not be known in vla1[size]
, etc.
The primary difference between using a VLA or dynamically allocating with malloc
, etc.. is that will a VLA, you still must obtain input of the size of the arrays before they are declared. VLAs cannot be resized. malloc
and realloc
provide the ability to dynamically grow the amount of storage for your arrays without knowing the number of rows or elements before hand. You simply allocate some reasonably anticipated size, and if the size is reached before you have finished reading all input, you can call realloc
for that block of memory and allocate more on-the-fly (but it does require a few more lines of code. It's not any more difficult, it just takes few more variables to track how much memory you have allocated, how much you have used, and when used == allocated
, you realloc
, update the variable with the new size allocate, and keep on going.
In the example that follows, the size
is read as the first input (from a file below, but it could just as easily be input on stdin
), VLAs are created using size
, the VLAs are set to all zero with memset
and then passed to the recursive pyramid
function. note also that the recursive function has been split into three-functions to simplify understanding the actual recursive pyramid
function while the computation and printing was moved to the pre-recursive call compute
function and to the post-recursion unwinding in the unwind
function.
(it makes no difference functionally, but "factoring" your code into simply digestible bits of code can help keep things straight)
#include <stdio.h>
#include <string.h>
void compute (int size, double (*people)[size], double (*weight)[size], int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double (*weight)[size], int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (size, people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
return 1;
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
return 1;
double people[size][size], /* declare VLAs, but you */
weight[size][size]; /* can't initialize VLAs */
memset (people, 0, size * sizeof *people); /* zero both arrays */
memset (weight, 0, size * sizeof *weight);
for (int i = 0; i < size; i++)
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
return 0;
Example Input File
$ cat dat/pyramidweight.txt
4
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Example Use/Output
$ ./bin/pyramidrecursefnvla dat/pyramidweight.txt
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
malloc
and calloc
- Completing the Storage Puzzle
As mentioned above, the primary advantage of using malloc, calloc, realloc
to dynamically allocate storage is that there is no need for size
to be known before-hand. Meaning, if the user (or your file) doesn't include size
, you can simply dynamically allocate some reasonable number of pointers and allocate storage for some number of double
values per-pointer and start reading your data.
There is no requirement that each block allocated to hold your row value have the same number of double
values allocated as the row before or after. (you can enforce this requirement by storing the number of values read in your first line of data and compare that to every other line read if, for instance, you are reading a square matrix worth of data). But here, since you have size
as input, use it, it simplifies the process by eliminating the guess work for "How many pointers to initially allocate for?" (we know, size
of them)
Before going further, we have used the word "pointer" in our discussion about dynamically allocating, while we used "array" in our other two cases. This is an important distinction. An array is not a pointer, and a pointer is not an array, but understand that an array is converted to a pointer on access. Specifically: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
(p3) Except when it is the operand of the
sizeof
operator, the
_Alignof
operator, or the unary'&'
operator, or is a string
literal used to initialize an array, an expression that has type
"array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is
not an lvalue.
Since you are passing a pointer to pointer to double
, your parameters for your functions for people
and weight
will need to change from a pointer to array of double
to a pointer to pointer to double
, e.g.
void pyramid (int size, double **people, double **weight, int row)
If you Allocate... You must Validate!
When allocating dynamically, malloc, calloc & realloc
can, and do fail on memory exhaustion. When they fail, they each return NULL
indicating failure. Otherwise, they return the starting address for the allocated block of memory which you assign to your pointer and can then access using array notation -- or pointer notation (e.g. people[2]
in array notation is equivalent to *(people + 2)
in pointer notation -- and since arrays are converted to pointers on access, you can use either notation with arrays as well)
What changes in main()
? Your declarations for people
and weight
to begin with, e.g.
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
After taking size
as input, allocate your pointers (size
for each), e.g.
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
(note: when allocating storage you need size
multiplied the sizeof
whatever you are allocating. While you can use the type, e.g. sizeof (double*)
, it is always better to use the dereferenced variable itself to size the type. E.g., if you are allocating for people
which is double**
, if you use sizeof *people
, then *people
is just double*
. This will ensure you never get the type-size wrong. With complex types mistakes are likely and common guessing whether the type is, e.g., a pointer to array of or array of pointers to, etc...)
At this point you have size
pointers for each allocated. While you could just make guess at the number of values, allocate that number of doubles, and realloc
if you reach the aljust located limit -- (but that would require re-working your read of the input from value-at-a-time to line-at-a-time and then parsing -- which I leave to you if you are curious), but here since we know we are modeling an array[size][size]
and we have array[size]
pointers allocated, all that remains is allocating size
doubles for each pointer, e.g.
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
(note again the type-size with sizeof *people[i]
and sizeof *weight[i]
)
Putting it altogether, you end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compute (double **people, double **weight, int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double **weight, int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double **people, double **weight, int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
exit (EXIT_FAILURE);
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
exit (EXIT_FAILURE);
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
/* the rest is the same - except for parameter types for pyramid */
for (int i = 0; i < size; i++) /* read people values from file */
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
for (int i = 0; i < size; i++)
free (people[i]);
free (weight[i]);
free (people);
free (weight);
return 0;
Example Input & Use/Output
The input and output is the same.
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851== Memcheck, a memory error detector
==1851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1851== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1851== Command: ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851==
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
==1851==
==1851== HEAP SUMMARY:
==1851== in use at exit: 0 bytes in 0 blocks
==1851== total heap usage: 11 allocs, 11 frees, 872 bytes allocated
==1851==
==1851== All heap blocks were freed -- no leaks are possible
==1851==
==1851== For counts of detected and suppressed errors, rerun with: -v
==1851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define yourpeople
andweight
arrays inmain()
. You can just pick a size that is sufficient (e.g.50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate withmalloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enterssize
and you declare, e.g.people[size][size]
, you will need to passvoid pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and usesize
notSIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must passint size
as a parameter before the arrays, e.g.double (*people)[size]
or the function will not know whatdouble (*people)[size]
is if you try and pass that parameter first (doesn't know whatsize
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.
– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilizemalloc
syntactically in place of thedefine SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?
– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?malloc
is simple enough. Instead of declaringpeople
andweight
asdouble
, you would declare each as a pointer to pointer todouble
(e.g.double **people;
) and then allocatesize
pointers (e.g.people = malloc (size * sizeof *people);
(sincepeople
isdouble**
,*people
isdouble*
used withsizeof
). Then you allocatesize
number ofdouble
to hold the values and assign each block allocated to each pointer (e.g.for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.
– David C. Rankin
Nov 12 at 23:44
|
show 7 more comments
up vote
1
down vote
accepted
up vote
1
down vote
accepted
A recursive function, requires (1) a recursive test condition that will stop the recursion, and (2) a recursive call within the function.
Note... avoid using recursion where a simple procedural approach will work. Each recursive call is itself a separate function call requiring a separate function stack, local variables, etc... If your recursion requires too many recursive calls, you can exhaust your available memory rather easily. Make sure you understand how many times your function can be called before choosing a recursive solution.
That said, there are some problems where recursion provides a very elegant solution without the potential for memory exhaustion. Permutations, factorials, etc... are good examples. Recursive functions also make great homework questions because it is a necessary area of programming, and it requires that you think carefully about what happens as you are making the recursive calls -- as well as what happens after your recursive test condition is met (and you have to "unwind" from the recursion as each recursive call returns.
In your case you are given the array containing the weight for each person and pass that along with a separate array of the same size to compute the weight of the pyramid of people at each point in the pyramid. You have to output both the array of people and the array of the weight at each point in the pyramid.
Your recursive test condition is fairly simple, you are going to make a recursive call to cover each row in your people
array in order to compute the weights. You will be passing the current row as a function parameter, so your recursive test is simply if your row count has reached the size of your pyramid (size of your arrays).
On the initial and each recursive call, you need to (1) print the people array and (2) compute the weights based on the people above, before you make your recursive call in your function. Then after making your recursive call, you will need to print the weights you have computed -- but careful, as you return from each recursive call and "unwind" the recursion, the row counter you use will start at its limit and return toward zero. This will take a bit of planning in handling your array indexes after the recursive call.
For example, setting up your recursive function, you could think something like:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* handle all computations/print people here */
pyramid (people, weight, row + 1); /* recursive call */
/* print your weights here (but reverse the indexes) */
Now that you have an outline, working though how to handle writing the function is no different that writing any function. You think about any special conditions that have to be met (e.g. 1st row, no weight on top and only the person to count, 1st and last elements of the array on edge of pyramid only carries weight of single person above, etc..) So it simply becomes a matter of then incorporating your special conditions in the flow of your recursive function -- but remember, you only have one recursive function, so the function itself must handle every one of those special conditions each time it is called -- whether or not they apply to the current row-count.
The approach here is pretty straight forward, you want to check if you are on the first row of the pyramid and just copy the weight to the weight
array. For all other rows, you will need three conditions (1) handle the 1st element (left-edge) calculation; (2) handle all inner-positions in the pyramid (if any), and (3) handle the last element in the row (right-edge). You will both print and compute weights in the same fashion, before making your recursive call, e.g.
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
...
Now what happens when row == SIZEP
?? Your recursive function begins to return from the recursive call. So if you take the last recursive call where you pass row + 1
and row == SIZEP
, then the returning and unwinding begins immediately after your recursive call. What will be the value of row
here? (if you made the recursive call passing row + 1
and it returned on your test condition, then row
hasn't changed it will still be the last row (e.g. 3
in your case).
All that happened in the final recursive call was:
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition -- FAILED */
and you are now back in at the previous call ready to begin the returns and unwinding with row
holding the index to the final row in your arrays. What happens below the recursive call is simply the printing of your weight
array. However, you don't want to print it upside-down, so you need to handle the value of row
to map your unwinding indexes to the same 0-3
instead of 3-0
.
Using a simple variable to reverse the row (say revrow
-- I don't like typing) and using subtracting the current row
value (+1
) from SIZEP
and using that as your index to print the weights
, e.g.
...
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
}
You don't have to worry about passing anything, as your recursive calls unwind, the value of row
following each return will be the value it had in that function before the recursive call was made.
Putting it altogether in a short example, you would have something like the following:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
int main (void)
double people[SIZEP][SIZEP] = 51.18 ,
55.90, 131.25 ,
69.05, 133.66, 132.82 ,
53.43, 139.61, 134.06, 121.63 ,
weight[SIZEP][SIZEP] = 0 ;
pyramid (people, weight, 0);
return 0;
Example Use/Output
$ ./bin/pyramidrecurse
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
Give it some thought. Recursive functions require a slightly different way of thinking to make them make sense. But, when you realize you are just repeated making calls to the same function on the way in, and then handling the return from each of those calls as your recursion unwinds -- it will start to sink in.
Using a VLA with C99+
Rather than declaring an integer constant SIZEP
to declare both people
and weight
as arrays with automatic storage type, you have two other options that will allow you to size both arrays based on user-input. The first, if you have a C99+ compile, is to use a Variable Length Array. While arrays with automatic storage duration require an Integer Constant as the size for the array declaration, a VLA allows a variable to be used to size the array. (with the caveat that a VLA cannot be initialized with the normal initializer e.g. 0
, etc., instead you must manually initialize a VLA with memset
or a loop)
Further, as noted in the comments, when passing a VLA as a parameter, you must pass the size as a parameter before the array so the size of the array is know, e.g. function (int size, vla1[size], vla2[size])
. If size
was not passed before the arrays, then size
would not be known in vla1[size]
, etc.
The primary difference between using a VLA or dynamically allocating with malloc
, etc.. is that will a VLA, you still must obtain input of the size of the arrays before they are declared. VLAs cannot be resized. malloc
and realloc
provide the ability to dynamically grow the amount of storage for your arrays without knowing the number of rows or elements before hand. You simply allocate some reasonably anticipated size, and if the size is reached before you have finished reading all input, you can call realloc
for that block of memory and allocate more on-the-fly (but it does require a few more lines of code. It's not any more difficult, it just takes few more variables to track how much memory you have allocated, how much you have used, and when used == allocated
, you realloc
, update the variable with the new size allocate, and keep on going.
In the example that follows, the size
is read as the first input (from a file below, but it could just as easily be input on stdin
), VLAs are created using size
, the VLAs are set to all zero with memset
and then passed to the recursive pyramid
function. note also that the recursive function has been split into three-functions to simplify understanding the actual recursive pyramid
function while the computation and printing was moved to the pre-recursive call compute
function and to the post-recursion unwinding in the unwind
function.
(it makes no difference functionally, but "factoring" your code into simply digestible bits of code can help keep things straight)
#include <stdio.h>
#include <string.h>
void compute (int size, double (*people)[size], double (*weight)[size], int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double (*weight)[size], int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (size, people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
return 1;
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
return 1;
double people[size][size], /* declare VLAs, but you */
weight[size][size]; /* can't initialize VLAs */
memset (people, 0, size * sizeof *people); /* zero both arrays */
memset (weight, 0, size * sizeof *weight);
for (int i = 0; i < size; i++)
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
return 0;
Example Input File
$ cat dat/pyramidweight.txt
4
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Example Use/Output
$ ./bin/pyramidrecursefnvla dat/pyramidweight.txt
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
malloc
and calloc
- Completing the Storage Puzzle
As mentioned above, the primary advantage of using malloc, calloc, realloc
to dynamically allocate storage is that there is no need for size
to be known before-hand. Meaning, if the user (or your file) doesn't include size
, you can simply dynamically allocate some reasonable number of pointers and allocate storage for some number of double
values per-pointer and start reading your data.
There is no requirement that each block allocated to hold your row value have the same number of double
values allocated as the row before or after. (you can enforce this requirement by storing the number of values read in your first line of data and compare that to every other line read if, for instance, you are reading a square matrix worth of data). But here, since you have size
as input, use it, it simplifies the process by eliminating the guess work for "How many pointers to initially allocate for?" (we know, size
of them)
Before going further, we have used the word "pointer" in our discussion about dynamically allocating, while we used "array" in our other two cases. This is an important distinction. An array is not a pointer, and a pointer is not an array, but understand that an array is converted to a pointer on access. Specifically: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
(p3) Except when it is the operand of the
sizeof
operator, the
_Alignof
operator, or the unary'&'
operator, or is a string
literal used to initialize an array, an expression that has type
"array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is
not an lvalue.
Since you are passing a pointer to pointer to double
, your parameters for your functions for people
and weight
will need to change from a pointer to array of double
to a pointer to pointer to double
, e.g.
void pyramid (int size, double **people, double **weight, int row)
If you Allocate... You must Validate!
When allocating dynamically, malloc, calloc & realloc
can, and do fail on memory exhaustion. When they fail, they each return NULL
indicating failure. Otherwise, they return the starting address for the allocated block of memory which you assign to your pointer and can then access using array notation -- or pointer notation (e.g. people[2]
in array notation is equivalent to *(people + 2)
in pointer notation -- and since arrays are converted to pointers on access, you can use either notation with arrays as well)
What changes in main()
? Your declarations for people
and weight
to begin with, e.g.
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
After taking size
as input, allocate your pointers (size
for each), e.g.
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
(note: when allocating storage you need size
multiplied the sizeof
whatever you are allocating. While you can use the type, e.g. sizeof (double*)
, it is always better to use the dereferenced variable itself to size the type. E.g., if you are allocating for people
which is double**
, if you use sizeof *people
, then *people
is just double*
. This will ensure you never get the type-size wrong. With complex types mistakes are likely and common guessing whether the type is, e.g., a pointer to array of or array of pointers to, etc...)
At this point you have size
pointers for each allocated. While you could just make guess at the number of values, allocate that number of doubles, and realloc
if you reach the aljust located limit -- (but that would require re-working your read of the input from value-at-a-time to line-at-a-time and then parsing -- which I leave to you if you are curious), but here since we know we are modeling an array[size][size]
and we have array[size]
pointers allocated, all that remains is allocating size
doubles for each pointer, e.g.
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
(note again the type-size with sizeof *people[i]
and sizeof *weight[i]
)
Putting it altogether, you end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compute (double **people, double **weight, int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double **weight, int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double **people, double **weight, int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
exit (EXIT_FAILURE);
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
exit (EXIT_FAILURE);
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
/* the rest is the same - except for parameter types for pyramid */
for (int i = 0; i < size; i++) /* read people values from file */
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
for (int i = 0; i < size; i++)
free (people[i]);
free (weight[i]);
free (people);
free (weight);
return 0;
Example Input & Use/Output
The input and output is the same.
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851== Memcheck, a memory error detector
==1851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1851== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1851== Command: ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851==
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
==1851==
==1851== HEAP SUMMARY:
==1851== in use at exit: 0 bytes in 0 blocks
==1851== total heap usage: 11 allocs, 11 frees, 872 bytes allocated
==1851==
==1851== All heap blocks were freed -- no leaks are possible
==1851==
==1851== For counts of detected and suppressed errors, rerun with: -v
==1851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
A recursive function, requires (1) a recursive test condition that will stop the recursion, and (2) a recursive call within the function.
Note... avoid using recursion where a simple procedural approach will work. Each recursive call is itself a separate function call requiring a separate function stack, local variables, etc... If your recursion requires too many recursive calls, you can exhaust your available memory rather easily. Make sure you understand how many times your function can be called before choosing a recursive solution.
That said, there are some problems where recursion provides a very elegant solution without the potential for memory exhaustion. Permutations, factorials, etc... are good examples. Recursive functions also make great homework questions because it is a necessary area of programming, and it requires that you think carefully about what happens as you are making the recursive calls -- as well as what happens after your recursive test condition is met (and you have to "unwind" from the recursion as each recursive call returns.
In your case you are given the array containing the weight for each person and pass that along with a separate array of the same size to compute the weight of the pyramid of people at each point in the pyramid. You have to output both the array of people and the array of the weight at each point in the pyramid.
Your recursive test condition is fairly simple, you are going to make a recursive call to cover each row in your people
array in order to compute the weights. You will be passing the current row as a function parameter, so your recursive test is simply if your row count has reached the size of your pyramid (size of your arrays).
On the initial and each recursive call, you need to (1) print the people array and (2) compute the weights based on the people above, before you make your recursive call in your function. Then after making your recursive call, you will need to print the weights you have computed -- but careful, as you return from each recursive call and "unwind" the recursion, the row counter you use will start at its limit and return toward zero. This will take a bit of planning in handling your array indexes after the recursive call.
For example, setting up your recursive function, you could think something like:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* handle all computations/print people here */
pyramid (people, weight, row + 1); /* recursive call */
/* print your weights here (but reverse the indexes) */
Now that you have an outline, working though how to handle writing the function is no different that writing any function. You think about any special conditions that have to be met (e.g. 1st row, no weight on top and only the person to count, 1st and last elements of the array on edge of pyramid only carries weight of single person above, etc..) So it simply becomes a matter of then incorporating your special conditions in the flow of your recursive function -- but remember, you only have one recursive function, so the function itself must handle every one of those special conditions each time it is called -- whether or not they apply to the current row-count.
The approach here is pretty straight forward, you want to check if you are on the first row of the pyramid and just copy the weight to the weight
array. For all other rows, you will need three conditions (1) handle the 1st element (left-edge) calculation; (2) handle all inner-positions in the pyramid (if any), and (3) handle the last element in the row (right-edge). You will both print and compute weights in the same fashion, before making your recursive call, e.g.
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
...
Now what happens when row == SIZEP
?? Your recursive function begins to return from the recursive call. So if you take the last recursive call where you pass row + 1
and row == SIZEP
, then the returning and unwinding begins immediately after your recursive call. What will be the value of row
here? (if you made the recursive call passing row + 1
and it returned on your test condition, then row
hasn't changed it will still be the last row (e.g. 3
in your case).
All that happened in the final recursive call was:
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition -- FAILED */
and you are now back in at the previous call ready to begin the returns and unwinding with row
holding the index to the final row in your arrays. What happens below the recursive call is simply the printing of your weight
array. However, you don't want to print it upside-down, so you need to handle the value of row
to map your unwinding indexes to the same 0-3
instead of 3-0
.
Using a simple variable to reverse the row (say revrow
-- I don't like typing) and using subtracting the current row
value (+1
) from SIZEP
and using that as your index to print the weights
, e.g.
...
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
}
You don't have to worry about passing anything, as your recursive calls unwind, the value of row
following each return will be the value it had in that function before the recursive call was made.
Putting it altogether in a short example, you would have something like the following:
#include <stdio.h>
#define SIZEP 4 /* constant for rows/cols - otherwise VLA or allocate */
void pyramid (double (*people)[SIZEP], double (*weight)[SIZEP], int row)
if (row < SIZEP) /* recursive test condition */
/* recursion */
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set innter weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
pyramid (people, weight, row + 1); /* recursive call */
/* return from recursion */
int revrow = SIZEP - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
int main (void)
double people[SIZEP][SIZEP] = 51.18 ,
55.90, 131.25 ,
69.05, 133.66, 132.82 ,
53.43, 139.61, 134.06, 121.63 ,
weight[SIZEP][SIZEP] = 0 ;
pyramid (people, weight, 0);
return 0;
Example Use/Output
$ ./bin/pyramidrecurse
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
Give it some thought. Recursive functions require a slightly different way of thinking to make them make sense. But, when you realize you are just repeated making calls to the same function on the way in, and then handling the return from each of those calls as your recursion unwinds -- it will start to sink in.
Using a VLA with C99+
Rather than declaring an integer constant SIZEP
to declare both people
and weight
as arrays with automatic storage type, you have two other options that will allow you to size both arrays based on user-input. The first, if you have a C99+ compile, is to use a Variable Length Array. While arrays with automatic storage duration require an Integer Constant as the size for the array declaration, a VLA allows a variable to be used to size the array. (with the caveat that a VLA cannot be initialized with the normal initializer e.g. 0
, etc., instead you must manually initialize a VLA with memset
or a loop)
Further, as noted in the comments, when passing a VLA as a parameter, you must pass the size as a parameter before the array so the size of the array is know, e.g. function (int size, vla1[size], vla2[size])
. If size
was not passed before the arrays, then size
would not be known in vla1[size]
, etc.
The primary difference between using a VLA or dynamically allocating with malloc
, etc.. is that will a VLA, you still must obtain input of the size of the arrays before they are declared. VLAs cannot be resized. malloc
and realloc
provide the ability to dynamically grow the amount of storage for your arrays without knowing the number of rows or elements before hand. You simply allocate some reasonably anticipated size, and if the size is reached before you have finished reading all input, you can call realloc
for that block of memory and allocate more on-the-fly (but it does require a few more lines of code. It's not any more difficult, it just takes few more variables to track how much memory you have allocated, how much you have used, and when used == allocated
, you realloc
, update the variable with the new size allocate, and keep on going.
In the example that follows, the size
is read as the first input (from a file below, but it could just as easily be input on stdin
), VLAs are created using size
, the VLAs are set to all zero with memset
and then passed to the recursive pyramid
function. note also that the recursive function has been split into three-functions to simplify understanding the actual recursive pyramid
function while the computation and printing was moved to the pre-recursive call compute
function and to the post-recursion unwinding in the unwind
function.
(it makes no difference functionally, but "factoring" your code into simply digestible bits of code can help keep things straight)
#include <stdio.h>
#include <string.h>
void compute (int size, double (*people)[size], double (*weight)[size], int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double (*weight)[size], int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (size, people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
return 1;
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
return 1;
double people[size][size], /* declare VLAs, but you */
weight[size][size]; /* can't initialize VLAs */
memset (people, 0, size * sizeof *people); /* zero both arrays */
memset (weight, 0, size * sizeof *weight);
for (int i = 0; i < size; i++)
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
return 0;
Example Input File
$ cat dat/pyramidweight.txt
4
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
Example Use/Output
$ ./bin/pyramidrecursefnvla dat/pyramidweight.txt
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
malloc
and calloc
- Completing the Storage Puzzle
As mentioned above, the primary advantage of using malloc, calloc, realloc
to dynamically allocate storage is that there is no need for size
to be known before-hand. Meaning, if the user (or your file) doesn't include size
, you can simply dynamically allocate some reasonable number of pointers and allocate storage for some number of double
values per-pointer and start reading your data.
There is no requirement that each block allocated to hold your row value have the same number of double
values allocated as the row before or after. (you can enforce this requirement by storing the number of values read in your first line of data and compare that to every other line read if, for instance, you are reading a square matrix worth of data). But here, since you have size
as input, use it, it simplifies the process by eliminating the guess work for "How many pointers to initially allocate for?" (we know, size
of them)
Before going further, we have used the word "pointer" in our discussion about dynamically allocating, while we used "array" in our other two cases. This is an important distinction. An array is not a pointer, and a pointer is not an array, but understand that an array is converted to a pointer on access. Specifically: C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
(p3) Except when it is the operand of the
sizeof
operator, the
_Alignof
operator, or the unary'&'
operator, or is a string
literal used to initialize an array, an expression that has type
"array of type" is converted to an expression with type "pointer to type" that points to the initial element of the array object and is
not an lvalue.
Since you are passing a pointer to pointer to double
, your parameters for your functions for people
and weight
will need to change from a pointer to array of double
to a pointer to pointer to double
, e.g.
void pyramid (int size, double **people, double **weight, int row)
If you Allocate... You must Validate!
When allocating dynamically, malloc, calloc & realloc
can, and do fail on memory exhaustion. When they fail, they each return NULL
indicating failure. Otherwise, they return the starting address for the allocated block of memory which you assign to your pointer and can then access using array notation -- or pointer notation (e.g. people[2]
in array notation is equivalent to *(people + 2)
in pointer notation -- and since arrays are converted to pointers on access, you can use either notation with arrays as well)
What changes in main()
? Your declarations for people
and weight
to begin with, e.g.
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
After taking size
as input, allocate your pointers (size
for each), e.g.
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
(note: when allocating storage you need size
multiplied the sizeof
whatever you are allocating. While you can use the type, e.g. sizeof (double*)
, it is always better to use the dereferenced variable itself to size the type. E.g., if you are allocating for people
which is double**
, if you use sizeof *people
, then *people
is just double*
. This will ensure you never get the type-size wrong. With complex types mistakes are likely and common guessing whether the type is, e.g., a pointer to array of or array of pointers to, etc...)
At this point you have size
pointers for each allocated. While you could just make guess at the number of values, allocate that number of doubles, and realloc
if you reach the aljust located limit -- (but that would require re-working your read of the input from value-at-a-time to line-at-a-time and then parsing -- which I leave to you if you are curious), but here since we know we are modeling an array[size][size]
and we have array[size]
pointers allocated, all that remains is allocating size
doubles for each pointer, e.g.
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
(note again the type-size with sizeof *people[i]
and sizeof *weight[i]
)
Putting it altogether, you end up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void compute (double **people, double **weight, int row)
if (row == 0) /* print people, set weight */
printf ("%6.2lfn", people[row][0]);
weight[row][0] = people[row][0];
else
/* print 1st person, set 1st weight */
printf ("%6.2lf", people[row][0]);
weight[row][0] = people[row][0] + weight[row-1][0] / 2.0;
/* print inner people, set inner weights */
for (int i = 1; i < row; i++)
printf (" %6.2lf", people[row][i]);
weight[row][i] = people[row][i] +
(weight[row-1][i-1] + weight[row-1][i]) / 2.0;
/* print last person, set last weight */
printf (" %6.2lfn", people[row][row]);
weight[row][row] = people[row][row] + weight[row-1][row-1] / 2.0;
void unwind (int size, double **weight, int row)
int revrow = size - (row + 1); /* print weights in reverse order */
if (revrow == 0) /* same logic as computing weights applies */
printf ("n%6.2lfn", weight[revrow][0]);
else
printf ("%6.2lf", weight[revrow][0]);
for (int i = 1; i < revrow; i++)
printf (" %6.2lf", weight[revrow][i]);
printf (" %6.2lfn", weight[revrow][revrow]);
void pyramid (int size, double **people, double **weight, int row)
if (row < size) /* recursive test condition */
/* computations before recursive call */
compute (people, weight, row);
/* recursive call */
pyramid (size, people, weight, row + 1);
/* return from recursion */
unwind (size, weight, row);
int main (int argc, char **argv)
int size; /* read from user or file */
double **people = NULL, /* pointer to pointer to double */
**weight = NULL;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) /* validate file open for reading */
perror ("file open failed");
exit (EXIT_FAILURE);
if (fscanf (fp, "%d", &size) != 1) /* read size */
fprintf (stderr, "error: invalid format '%s'n", argv[1]);
exit (EXIT_FAILURE);
/* first allocate size pointers to each people and weight */
people = malloc (size * sizeof *people);
if (people == NULL) /* validate every allocation */
perror ("malloc-people");
exit (EXIT_FAILURE);
weight = malloc (size * sizeof *weight);
if (!weight) /* validate every allocation */
perror ("malloc-weight");
exit (EXIT_FAILURE);
/* now allocate a block of size double for each pointer,
* let's use calloc here to both allocate and set all bytes zero.
*/
for (int i = 0; i < size; i++) /* loop over pointers */
people[i] = calloc (size, sizeof *people[i]); /* allocate doubles */
if (!people[i]) /* validate every allocation */
perror ("calloc-people[i]");
exit (EXIT_FAILURE);
weight[i] = calloc (size, sizeof *weight[i]); /* allocate doubles */
if (!weight[i]) /* validate every allocation */
perror ("calloc-weight[i]");
exit (EXIT_FAILURE);
/* the rest is the same - except for parameter types for pyramid */
for (int i = 0; i < size; i++) /* read people values from file */
for (int j = 0; j <= i; j++)
if (fscanf (fp, "%lf", &people[i][j]) != 1)
fprintf (stderr, "error: reading people[%d][%d]n", i, j);
return 1;
fclose (fp); /* close file */
pyramid (size, people, weight, 0); /* compute/print arrays */
for (int i = 0; i < size; i++)
free (people[i]);
free (weight[i]);
free (people);
free (weight);
return 0;
Example Input & Use/Output
The input and output is the same.
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind
is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
$ valgrind ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851== Memcheck, a memory error detector
==1851== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==1851== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==1851== Command: ./bin/pyramidrecursemalloc dat/pyramidweight.txt
==1851==
51.18
55.90 131.25
69.05 133.66 132.82
53.43 139.61 134.06 121.63
51.18
81.49 156.84
109.79 252.82 211.24
108.33 320.92 366.09 227.25
==1851==
==1851== HEAP SUMMARY:
==1851== in use at exit: 0 bytes in 0 blocks
==1851== total heap usage: 11 allocs, 11 frees, 872 bytes allocated
==1851==
==1851== All heap blocks were freed -- no leaks are possible
==1851==
==1851== For counts of detected and suppressed errors, rerun with: -v
==1851== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
edited Nov 13 at 15:20
answered Nov 12 at 13:35
David C. Rankin
39.9k32647
39.9k32647
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define yourpeople
andweight
arrays inmain()
. You can just pick a size that is sufficient (e.g.50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate withmalloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enterssize
and you declare, e.g.people[size][size]
, you will need to passvoid pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and usesize
notSIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must passint size
as a parameter before the arrays, e.g.double (*people)[size]
or the function will not know whatdouble (*people)[size]
is if you try and pass that parameter first (doesn't know whatsize
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.
– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilizemalloc
syntactically in place of thedefine SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?
– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?malloc
is simple enough. Instead of declaringpeople
andweight
asdouble
, you would declare each as a pointer to pointer todouble
(e.g.double **people;
) and then allocatesize
pointers (e.g.people = malloc (size * sizeof *people);
(sincepeople
isdouble**
,*people
isdouble*
used withsizeof
). Then you allocatesize
number ofdouble
to hold the values and assign each block allocated to each pointer (e.g.for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.
– David C. Rankin
Nov 12 at 23:44
|
show 7 more comments
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define yourpeople
andweight
arrays inmain()
. You can just pick a size that is sufficient (e.g.50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate withmalloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enterssize
and you declare, e.g.people[size][size]
, you will need to passvoid pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and usesize
notSIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must passint size
as a parameter before the arrays, e.g.double (*people)[size]
or the function will not know whatdouble (*people)[size]
is if you try and pass that parameter first (doesn't know whatsize
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.
– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilizemalloc
syntactically in place of thedefine SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?
– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?malloc
is simple enough. Instead of declaringpeople
andweight
asdouble
, you would declare each as a pointer to pointer todouble
(e.g.double **people;
) and then allocatesize
pointers (e.g.people = malloc (size * sizeof *people);
(sincepeople
isdouble**
,*people
isdouble*
used withsizeof
). Then you allocatesize
number ofdouble
to hold the values and assign each block allocated to each pointer (e.g.for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.
– David C. Rankin
Nov 12 at 23:44
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
My god, what an excellent job of breaking down the problem and explaining it thoroughly. I can't thank you enough. I had one question though, you define a constant of '4' for the bottom row, but can I create a constant based on the user input? Say the user inputs 5 or 6, will I be able to pass that to a function and use it in the same way?
– user7823016
Nov 12 at 15:57
Yes, you will still need some constant to define your
people
and weight
arrays in main()
. You can just pick a size that is sufficient (e.g. 50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate with malloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enters size
and you declare, e.g. people[size][size]
, you will need to pass void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and use size
not SIZEP
– David C. Rankin
Nov 12 at 16:38
Yes, you will still need some constant to define your
people
and weight
arrays in main()
. You can just pick a size that is sufficient (e.g. 50
) and then make sure what the user inputs is less than that. Otherwise you can use a VLA (Variable Length Array C99+) or you can allocate with malloc
, etc... The VLA is the easiest, but not portable to windows before Win10. If you use a VLA and the user enters size
and you declare, e.g. people[size][size]
, you will need to pass void pyramid (int size, double (*people)[size], double (*weight)[size], int row)
and use size
not SIZEP
– David C. Rankin
Nov 12 at 16:38
note: when using the VLA, you must pass
int size
as a parameter before the arrays, e.g. double (*people)[size]
or the function will not know what double (*people)[size]
is if you try and pass that parameter first (doesn't know what size
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.– David C. Rankin
Nov 12 at 16:40
note: when using the VLA, you must pass
int size
as a parameter before the arrays, e.g. double (*people)[size]
or the function will not know what double (*people)[size]
is if you try and pass that parameter first (doesn't know what size
is yet) And... glad the example helped -- recursive functions can be a brain twister before you get the recuse-in/unwind-out sorted out.– David C. Rankin
Nov 12 at 16:40
I'm not sure how to utilize
malloc
syntactically in place of the define SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?– user7823016
Nov 12 at 23:37
I'm not sure how to utilize
malloc
syntactically in place of the define SIZEP
constant. Is it as simple as swapping it out and placing it inside of the array size?– user7823016
Nov 12 at 23:37
Why don't you start with a VLA first?
malloc
is simple enough. Instead of declaring people
and weight
as double
, you would declare each as a pointer to pointer to double
(e.g. double **people;
) and then allocate size
pointers (e.g. people = malloc (size * sizeof *people);
(since people
is double**
, *people
is double*
used with sizeof
). Then you allocate size
number of double
to hold the values and assign each block allocated to each pointer (e.g. for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.– David C. Rankin
Nov 12 at 23:44
Why don't you start with a VLA first?
malloc
is simple enough. Instead of declaring people
and weight
as double
, you would declare each as a pointer to pointer to double
(e.g. double **people;
) and then allocate size
pointers (e.g. people = malloc (size * sizeof *people);
(since people
is double**
, *people
is double*
used with sizeof
). Then you allocate size
number of double
to hold the values and assign each block allocated to each pointer (e.g. for (int i = 0; i < size; i++) people[i] = malloc (size * sizeof *people[i]);
. I'll add a VLA example first.– David C. Rankin
Nov 12 at 23:44
|
show 7 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53255313%2frecursively-creating-and-displaying-a-pyramid-of-people-c%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
3
When asking about homework you should (1) Make a good faith attempt to solve the problem yourself first (include your code in your question). (2) Ask about specific problems with your existing implementation (including any errors you are receiving). (3) Admit that the question is homework. (4) Be aware of your school policy. (5) Never use code you don't understand. See: How do I ask and answer homework questions?
– David C. Rankin
Nov 12 at 2:40
@tod I apologize, I thought my functions would be almost worthless being shown. I'll edit.
– user7823016
Nov 12 at 2:49
@D I apologize, I thought my functions would be almost worthless being shown. I'll edit. – user7823016 just now edit: I would never submit something I don't understand, I'm truly trying to internalize recursion, I'm just feeling demoralized. Yes this is an assignment problem!
– user7823016
Nov 12 at 2:49