I have been struggling with pointer and memory allocation in c for a while.
Here's my implementation for the max subarray problem. It seems to work fine (maybe have bugs). But I have a question about the memory storage for tuple struct object. As you can see, tuple is declared in the global storage. Later in the findMaxSubArray() function, three pointers to Tuple struct are declared. My question is we didn't declare Tuple struct object instances that the pointers (left, right, cross) are addressing how come the pointer dereferences (i.e., left->sum, etc) work. Does the GNU c compiler automatically allocate storage for them? (I don't understand x86 assembly code) Can someone please explain what's going on here? Much appreciated.
#include <iostream>
using namespace std;
#define NEGINFINITY -2 << 31
typedef struct {
int lowPosition;
int highPosition;
int sum;
} Tuple;
Tuple tuple;
Tuple* findMaxCrossingSubArray(int a[], int low, int mid, int high) {
int leftSum, rightSum;
int leftMax, rightMax;
int sum;
leftSum = rightSum = NEGINFINITY;
sum = 0;
for (int i = mid; i >= low; --i) {
sum += a[i];
if (sum > leftSum) {
leftSum = sum;
leftMax = i;
}
}
sum = 0;
for (int j = mid + 1; j <= high; ++j) {
sum += a[j];
if (sum > rightSum) {
rightSum = sum;
rightMax = j;
}
}
tuple.lowPosition = leftMax;
tuple.highPosition = rightMax;
tuple.sum = leftSum + rightSum;
return &tuple;
}
Tuple* findMaxSubArray(int* array, int low, int high) {
Tuple *left, *right, *cross;
if (high == low) {
// base case
tuple.lowPosition = low;
tuple.highPosition = high;
tuple.sum = array[low];
return &tuple;
}
else {
int mid = (low + high) / 2;
left = findMaxSubArray(array, low, mid);
right = findMaxSubArray(array, mid + 1, high);
cross = findMaxCrossingSubArray(array, low, mid, high);
if (left->sum > right->sum && left->sum > cross->sum)
return left;
else if (right->sum > left->sum && right->sum > cross->sum)
return right;
else
return cross;
}
}
int main() {
Tuple *result;
int data[] = {1, -2, 3, 10, -4, 7, 2, -5};
result = findMaxSubArray(data, 0, 7);
for (int i = 0; i < 8; ++i)
cout << data[i] << " ";
cout << endl;
cout << "The sum of max subarray is " << result->sum
<< " Starting at index " << result->lowPosition
<< " ending at index " << result->highPosition << endl;
}
The global variable tuple is the only actual Tuple in this program. Memory for global variables is managed by the compiler.
In main, Tuple *result is just a pointer which, when you declared it, contains a random number (whatever happened to previously be in the space it now occupies) and thus result points to garbage (not a valid Tuple object).
Then you assign the result of findMaxSubArray to tuple. Since findMaxSubArray returns &tuple (in one way or another) which is a global variable, result points to the global variable tuple. So when you do result->sum, it's the same as doing tuple.sum.
In findMaxSubArray, the line Tuple *left, *right, *cross; declares three pointers to Tuples which contain a garbage value. In one branch of the if you don't use them and just return &tuple, the address of the global variable tuple. In the other branch, you set left, right, and cross to either findMaxCrossingSubArray or findMaxSubArray, which both return &tuple one way or the other.
I do suggest reading a book on C++ and forgetting everything you know about C while using C++ (but remember it all again when you program C again). They are not the same language. This code is riddled with things you learned from your C training (such as #define and typedef struct ... Tuple) for which C++ offers better facilities.
The only Tuple ever actually allocated is the tuple at global scope. All the pointers to Tuple, e.g. Tuple* left just point to that global storage. So for example in:
left = findMaxSubArray(array, low, mid);
right = findMaxSubArray(array, mid + 1, high);
You will find that left and right both point to the same address and hence have the same pointed to values.
Without working out the detail, and noting that this is C++, not C, you can probably just change the Tuple* return types to Tuple return by value and work with automatic (stack) storage.
The code becomes something like:
Tuple findMaxSubArray(int* array, int low, int high)
{
Tuple left, right, cross;
// blah blah
if (something)
return left;
else
return right;
}
All of your pointers (left, right, and cross) will always point to same memory location of &tuple. If you want different tuples you need to use malloc to create different Tuple strucks on the heap.
Related
I intend to create a recursive merge sort function that returns a pointer to the sorted array. Below is my implementation of the program.
The output produced by the code mostly consists of garbage memory locations. Since it is a recursive function, I'm having trouble debugging it. What scares me is whether I have understood pointers, arrays and their interconversion incorrectly. It would be really helpful if someone could take a look at the code and tell me my mistake.
Function that prints an array given a pointer to its first element
void printArr(int *arr, int n){
int *ptr = arr;
for(int i = 0; i < n; i++){
cout << *ptr << " ";
ptr++;
}
cout << endl;
}
Merge Sort function
Here p is the pointer to the given array, n is the length of the array. l and r are the indices of the first and last element of the array respectively.
// returns pointer to the sorted array
int* mergeSort(int *p, int n, int l, int r){
if(l >= r){
return &p[l];
}
int mid = l + (r-l)/2;
int *leftArray = mergeSort(p, n, l, mid);
int *rightArray = mergeSort(p, n, mid+1, r);
int n1 = mid - l + 1;
int n2 = r - mid;
int sortedArray[n1+n2];
int *ptr = sortedArray; // pointer to the sorted array
int p1 = 0; // left array index pointer
int p2 = 0; // right array index pointer
int idx = 0; // sorted array index pointer
int flag = 0; /* flag = 1 => all elements of left array have been placed into the sorted array ; flag = 2 => all elements of right array have been placed into the sorted array */
// putting elements into the sorted array
for(int i = 0; i < n1+n2; i++){
if(p1 == n1){
flag = 1;
break;
}
if(p2 == n2){
flag = 2;
break;
}
if(*(leftArray+i) > *(rightArray+i)){
sortedArray[i] = *(leftArray+p1);
p1++;
idx++;
}
else{
sortedArray[i] = *(rightArray+p2);
p2++;
idx++;
}
}
if(flag == 1){
// put remaining elements of right array into the sorted array
for(int i = idx; i < n1+n2; i++){
sortedArray[i] = *(rightArray+p2);
p2++;
}
}
if(flag == 2){
// put remaining elements of left array into the sorted array
for(int i = idx; i < n1+n2; i++){
sortedArray[i] = *(leftArray+p1);
p1++;
}
}
// return the sorted array
return ptr;
}
Main function
int main(){
int arr[] = {7,2,1,5};
int n = sizeof(arr)/sizeof(int);
int *p = arr;
cout << "Original array: ";
printArr(arr, n);
int *f = mergeSort(arr, n, 0, 3);
cout << "New array: ";
printArr(f, n);
}
Your code have returned the local array's address, which is invalidated after function returned. Then gabarage data is printed:
int sortedArray[n1+n2];
int *ptr = sortedArray; // pointer to the sorted array
Change into
int *ptr = new int[n1 + n2];
auto sortedArray = ptr;
Then we get a non-garbage value, but we hit memory leak and it's hard to deal with memory deallocation since the retuned pointer may point to the array p under certain boundary conditions.
So return pointer is not a good design, and it just wastes the API call to allocate memory and deallocate memory. It's better to split the function into two: the first one allocates a temporary buffer, and the second one handles sorting with the buffer as a parameter and calls itself with a recursive. Or just sort in place, totally avoid the temporary buffer.
I implemented a mergesort algorithm but it returns the exact same array I pass as an input. The following is the code. I am suspecting the pseudocode that our professor gave us is wrong. But I am not sure. I have tried to implement is as best as I can.
int len(double *a) {
int count = 0;
while (a[count] != '\0') {
count++;
}
return count;
}
double* merge(double* b, double* c, int N) {
int i = 0;
int j = 0;
double* result = new double[N];
for(int k = 0; k < N; k++) {
if ((i < len(b)) && (j >= len(c) || b[i] <= c[j])) {
result[k] = b[i++];
} else {
result[k] = c[j++];
}
}
return result;
}
void merge_sort(double* a, int N) {
if (N >= 2) {
int mid = (N+1)/2;
double *left = new double[mid];
double *right = new double[mid];
for (int i = 0; i < mid; i++) {
left[i] = a[i];
}
for (int j = 0; j < mid; j++) {
right[j] = a[mid + j];
}
merge_sort(left, mid);
merge_sort(right, mid);
a = merge(left, right, N);
}
}
Any help would be really appreciated.
In the last line, you assign your result to the local var a, which is then lost. You need to return a, or pass the input as a reference/pointer, otherwise any changes are only to the local copy.
Arguments in the function are basically local variables, they behave like any local variable in this function, except their initial value is set by code that calls this function. a is a pointer that stores the address of first element of your double array.
As it's a local variable, you can modify it but when the function ends, it will be discarded like all other local variables of the function.
There are several ways to deal with this problem, each with their own up and downsides. The most obvious is to return final value of a when you're done sorting. You could also pass a pointer TO a pointer to this function, and then you would be able to modify the pointer outside the function:
void function(int** argument){
*argument = another_function();
}
, but that severely restricts the source of your input. It no longer could be a local array passed by address like this:
int x = 10;
int *y = &x; // if this is what you want to change
function(&y); // this works
// now x is still 10, y points to a different place in memory which can store a different value
int x[1]; // if you would like to change this array in place though...
function(x); // this is how you would call the function, but it would fail because it can't change the address that x refers to
You main issue is here:
// You pass in a pointer to the data here.
// the parameter `a` holds a pointer to the data.
void merge_sort(double* a, int N) {
if (N >= 2) {
// STUFF
// Here you write over `a` (which is fine)
// BUT: You don't pass the value back.
// So the caller never knows what the new value is.
a = merge(left, right, N);
}
}
To fix this. I think it is a mistake to allocate a new array in merge(). Rather re-use the array you have. You have already copied the data into left and right to be sorted. The merge should merge the data back into the original array a.
// Change this:
a = merge(left, right, N);
into
merge(a, left, right, N);
Now in merge() you can use a as the destination.
void merge(double* result, double* b, double* c, int N)
// No longer need to allocate space for result now.
There are a couple of other issues:
1: What do you need len() for?
int len(double *a) {
int count = 0;
while (a[count] != '\0') {
count++;
}
return count;
}
You should already know the length of all parts you should not be re-measuring it. Also this function is completely wrong (the double array is not \0 terminated).
2: The length of b and c is not obvious.
double* merge(double* b, double* c, int N) {
You get the wrong value because you call len() which is not correct.
You could calculate from N but that has issues in that you need to make sure both you merge functions use exactly the same method and that is error prone in the long run. I would personally pass the size of each array as parameters to the function.
3: You leak your intermediate arrays!
You call new to allocate storage.
double *left = new double[mid];
double *right = new double[mid];
But you don't deallocate these objects so they are leaked (for every call to new there should be a corespodning call to delete).
Overall. You can solve a cople of issues by using more C++ style techniques (rather than the C style you are using). Iterators and std::vector spring to mind.
I edited the code. But now it's showing runtime error. Can anyone tell why ? This is a program to sum 2 arrays and display output in third array.
I also wanted to know if this code could be optimized ?
void sumOfTwoArrays(int arr[], int size1, int brr[], int size2, int crr[])
{
int k;
if(size1>size2){
k = size1;
}
else
k = size2;
int c = k;
int r = 0;
int i = size1-1;
int j = size2-1;
for(;i>=0&&j>=0;i--,j--){
int n = arr[i] + brr[j] + r;
if(n<=9){
crr[c] = n;
}
else
{
int r = n/10;
n = n%10;
crr[c] = n;
}
c--;
}
while(arr[i]>=0){
crr[c] = arr[i] + r;
r = 0;
c--;
}
while(brr[j]>=0){
crr[c] = brr[j] + r;
r = 0;
c--;
}
if(r!=0){
crr[c] = r;
}
}
You declare variables in a block scope, i.e. inside { ... }, and these variables are visible only within this block:
if(size1>size2){
int crr[size1+1];
int c = size1;
}
else{
int crr[size2+1];
int c = size2;
}
...
crr[c] = ... // neither crr nor c are valid here any more
BTW: C++ does not support variable length arrays like int crr[size1+1] (when size is not a compile-time-constant).
To overcome this, write...
int *crr;
int c;
if(size1>size2){
crr = new int[size1+1];
c = size1;
}
else{
crr = new int[size2+1];
c = size2;
}
...
delete[] crr;
About scope issue: see Stephan's answer.
I also wanted to know if this code could be optimized
By use of std::vector. OK, the following is only a fine option if you can use vectors outside as well – copying the raw arrays into vectors wouldn't be efficient either... But if you can, then you might like this variant:
template <typename T> // optional: you're more flexible if you make a template of...
void sumOfTwoArrays(std::vector<T> const& va, std::vector<T> const& vb, std::vector<T>& vr)
{
vr.resize(std::max(va.size(), vb.size() + 1));
int carry = 0; // renamed r to something more meaningful
// these pairs will help to avoid code duplication later
std::pair pa(va, va.rbegin());
std::pair pb(vb, vb.rbegin());
auto ir = vr.rbegin();
while(pa.second != pa.first.rend() && pb.second != pb.first.rend())
{
// just skip the if/else:
// assume you have arbitrary number, the else case will be entered anyway
// in 50 % of the cases - in the other 50 %, the else branch calculates
// the correct result, too; and on most modern machines, the branch is
// rather expensive, so you result in easier code and have quite a good
// chance to even perform better...
carry += *pa.second + *pb.second;
*ir = carry % 10;
carry /= 10;
++ir, ++pa.second, ++pb.second;
}
// avoiding of two identical while loops: iterate over the two pairs...
for(auto p : { pa, pb })
{
// just loop over, if we are already at the end, won't enter...
while(p.second != p.first.rend())
{
// STILL need to continue calculating the carry!
// imagine we have set it and ciphers following are all 9!
carry += *p.second;
*ir = carry % 10;
carry /= 10;
++ir, ++p.second;
}
}
// assign either 0 or 1...
*ir = carry;
}
Variant: instead of assigning 0, you could erase first element at the very end:
if(carry == 0)
{
vr.erase(vr.begin());
}
else
{
*ir = carry;
}
Note that this will move all the elements one position to front. On the other hand, if you repeatedly add vectors already containing a leading zero, you might prepend another one again and again without need, if you don't drop it again.
You wouldn't experience any of these issues if you inverted the order of digits in the vector, having least significant one at position 0 (you'd exchange rbegin() and rend() with begin() and end(), but would use the former for printing data to display...). Erasure at the end would be an O(1) operation then:
if(carry == 0)
{
vr.erase(std::previous(vr.end())
}
// ...
All this above will only work as expected if you keep your vectors normalised (i. e. all digits in between 0 and 9 inclusive). You might consider packing the vector into a separate class such that the data is hidden away from the user and only can be modified in controlled manner (assume you have a fine vector, but a user does v[7] = -1012...).
A runtime error suggests that it is a memory issue i.e. you are writing to some memory which is not allocated to be used by your code. So, as mentioned by other contributors, you should allocate proper memory for your arrays.
Following is modified version of your code which is working fine. You can see it working here:
void sumOfTwoArrays(int arr1[], int size1, int arr2[], int size2, int sumArr[])
{
int maxLen;
int* tArry;
int l;
if(size1>size2) { maxLen = size1; tArry = arr1; l = size1 - size2; }
else { maxLen = size2; tArry = arr2; l = size2 - size1; }
int carry = 0;
while(size1 && size2){
carry += arr1[--size1] + arr2[--size2];
sumArr[maxLen--] = carry%10;
carry /= 10;
}
while(l){
carry += tArry[--l];
sumArr[maxLen--] = carry%10;
carry /= 10;
}
sumArr[maxLen] = carry;
}
Calling code looks something like this:
...
int a[] = {9,9,9,9,9};
int b[] = {1};
int l1 = sizeof(a) / sizeof(int), l2 = sizeof(b)/sizeof(int);
int l3 = ((l1 > l2) ? l1 : l2) + 1;
int *c = new int[l3];
sumOfTwoArrays(a, l1, b, l2, c);
...
delete [] c;
...
I took into consideration the hints I received, I applied some modular thinking and then tried again. The program runs. Depending on the set of values I hard-wire into the elements of the array, I receive as output, the index where the sum of the elements on the left is equal to the sum of the elements on the right. I understand this to have been the objective of the exercise.
I chose not to use a vector in this exercise because I need the practice for remembering that an array has a constant pointer to position 1 and as such, when an array is passed to a function, one must remember to also pass along the size of the array, or,alternately inside the function where the array is being passed, one can loop through the array and count the number of elements therein, thereafter using count as the array size.
Please criticize my new and functional code and point out anything else that I have done wrong.
Thank you.
#include "stdafx.h"
#include <iostream>
using namespace std;
/***************************************
* RIGHT SIDE OF ARRAY
* Calculates sum of elements right of n
***************************************/
int rightSideOfArray(int arrayOne[], int size, int i)
{
int n = 0;
//loop through array and get right hand sum
for (int j = 1 + i; j < size; j++)
{
n += arrayOne[j];
}
return n;
}
/***************************************
* LEFT SIDE OF ARRAY
* Calculates sum of elements left of n
***************************************/
int leftSideOfArray(int arrayOne[], int size, int i)
{
int n2 = 0;
//find left hand sum
for (int j = i - 1; j >= 0; j--)
{
n2 += arrayOne[j];
}
return n2;
}
int main()
{
//define and declare array
int const SIZE = 7;
int arrayOne[SIZE] =
{ 1,2,3,4,3,2,1 };
int n = 0;
int n2 = 0;
int count = 0;
//do comparison
for (int i = 0; i < SIZE; i++)
{
//compare right hand and left hand side and return right values
if (rightSideOfArray(arrayOne, SIZE, i) ==
leftSideOfArray(arrayOne, SIZE, i))
counter++;
cout << i << endl;
}
if (counter == 0)
cout << -1 << endl;
system("PAUSE");
return 0;
}
Old Code: First attempt
I read a previous solution to this same query but I can't figure out where I went wrong. The challenge as I understand it is to loop through an integer array, at each, element 'i', I must add all the elements to the left of 'i' to get the 'left side sum'. Then I must add all the elements to the right of 'i' to get the 'right hand sum'. There after, I should compare the sums for the right hand and left hand sides of my array.
If both sums are equal, I should have my function return the index at which the equalization of right hand and left hand side occurred. Else, I should return -1.
Can anyone tell me why I am getting only '-1' as my answer?
int equalSidesOfAnArray(int arrayOne[], int n, int n2)
{
//loop through array and get right hand sum
for (int i = 0; i < sizeof(arrayOne); i++)
{
for (int j = 1 + i; j < sizeof(arrayOne); j++)
{
n += arrayOne[j];
n2 += arrayOne[j - 1];
}
if (n == n2)
return arrayOne[i];
else
return -1;
}
}
int main()
{
// define and declare array
int const SIZE = 7;
int arrayOne[SIZE] = { 1, 2, 3, 4, 3, 2, 1 };
int n = 0;
int n2 = 0;
int answer = equalSidesOfAnArray(arrayOne, n, n2);
cout << answer << endl;
system("PAUSE");
return 0;
}
First of all, arrayOne as parameter of the function is a pointer to the first element of the array, and sizeof(arrayOne) is the size of this pointer, not the size SIZE of your array.
And even within main(), sizeof(arrayOne) would return SIZE * sizeof(int).
As you are coding in C++, use std::vector/std::array and banish C arrays. This you save you such trouble and much more.
And think about where you are initializing n and n2 (which you don't need to pass as parameter), and returning -1.
I am trying to figure out why my merge sort function is not working. I believe the problem is within the merge() part of it. here is my code:
int mergeSort(int *data, int left, int right) {
//divide
if (left < right) {
int q = floor((left + right) / 2);
//conquer
mergeSort(data, left, q);
mergeSort(data, q + 1, right);
//combine
merge(data, left, q, right);
}
//print results for testing purposes
for (int i = 0; i < n; i++) {
cout << data[i] << "\n";
}
return 0;
}
And here is the merge() part of it. I believe the problem is within this part.
int merge(int *data, int left, int q, int right) {
int *temp = data;
int i = left, j = q + 1, z = left;
int t1 = 0, t2 = 0;
while (i <= q && j <= right) { //while both variables have not reached the end of their sub arrays
if (temp[i] <= temp[j]) {
data[z] = temp[i];
i++;
}
else {
data[z] = temp[j];
j++;
}
z++;
}
//if subarrays are both sorted and in order, combine them
if (i < q) {
t1 = z, t2 = i;
for (t1; t1 < right;) {
data[t1] = temp[t2];
t1++;
t2++;
}
}
else if (j < right) {
int t1 = z; int t2 = j;
for (t1; t1 <= right;) {
data[t1] = temp[t2];
t1++;
t2++;
}
}
I think that my problem is coming from declaring int *temp = data; at the beginning of merge(). My thought is that I'm getting some kind of memory address conflict between these two arrays, but I'm not sure.
I have tried arranging the data in different order, and have found that when a data needs to be moved from index n to index n-i, each index between these two indices is replaced with the value of n. For example:
the passing in the array {4, 13, 8, 12, 9 } would return {4, 8, 8, 9, 9}
My other thought is that the i and j variables are not incrementing correctly. I have been over this code repeatedly and can't seem to find a solution.
This (first line of the merge function)
int *temp = data;
does NOT copy an array; it just creates another pointer pointing to the same memory. This causes your code to overwrite your data.
You probably need to do something like this:
int * temp = new int [right - left + 1];
memcpy (temp, data + left, (right - left + 1) * sizeof(int));
and don't forget to delete[] the memory for temp at the end of your function:
delete[] temp;
Note that you'll need to change your use of z; it should start from 0, not from left.
(The following are optional performance improvements; you can ignore them.)
Allocating and freeing memory in each merge function is a bad idea. Specially since we exactly know how much total extra memory we need for merging: an array of exactly n integers.
For this reason, it's a better idea to pass in another array of the same size along with data to mergeSort, so it can be used as scratch memory (i.e. temp memory) for merging. Or if you are really clever, you can ping-pong between the actual and the scratch memory to minimize copying.