-2

I have a problem with a code I'm writting in C++. The compiler send me the Error C2440 and I don't know why. Here is the code, the problem presents in the void main () area, specially in the lines R = calloc and R[i] = calloc. Thanks for the answers.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <iostream>
using namespace std;

double F (double x);
double G (double x);
double P (double x);

void romberg(double f(double), double a, double b, int n, double **R);

double F (double x)
{
  return (1.0/ (1.0 + x));
}

double G (double x)
{ 
   return (exp(x));
}

double P (double x)
{
   return (sqrt(x));
}

 void romberg(double f(double), double a, double b, int n, double **R)
{
  int i, j, k;
  double h, sum;


  h = b - a;
  R[0][0] = 0.5 * h * (f(a) + f(b));   
  cout << " R[0][0] = ";
  cin >> R[0][0];


  for (i = 1; i <= n; i++)
  { 
     h *= 0.5;
     sum = 0;
     for (k = 1; k <= pow(2,i)-1; k+=2)
     {
       sum += f(a + k * h);
     } 
     R[i][0] = 0.5 * R[i-1][0] + sum * h;  
     cout << "R[i][0] = "; 
     cin>>i, R[i][0];
     for (j = 1; j <= i; j++)
     {
       R[i][j] = R[i][j-1] + (R[i][j-1] - R[i-1][j-1]) / (pow(4,j)-1); 
       cout << " R[i][j] = "; 
       cin>>i, j, R[i][j];
     }
   }
}

void main()
{
  int n = 10;
  int i;
  double **R;
  double F(double), G(double), P(double);

  R = calloc((n+1), sizeof(double *));
  for (i = 0; i <= n; i++)
    R[i] = calloc((n+1), sizeof(double));
  cout<<"The first function is F(x) = 1/(1 + x)\n";
  romberg(F, 0.0, 2.0,3, R);
  cout<<"The second function is G(x) = exp(x)\n";
  romberg(G,-1.0, 1.0, 4, R);
  cout<<"The third function is P(x) = sqrt(x)\n";
  romberg(P,0.0, 1.0, 7, R);
}    
tsunakata
  • 3
  • 2
  • 7
    Never `void main`! `int main`! – aschepler Apr 17 '17 at 17:35
  • 4
    1. use `new` when you *must* allocate memory 2. you very rarely need to allocate memory (use standard containers like `std::vector` or use smart pointers). – crashmstr Apr 17 '17 at 17:39
  • 4
    @crashmstr: No, don't use `new`, ever. – Lightness Races in Orbit Apr 17 '17 at 17:39
  • `k <= pow(2,i)` -- Not only are you computing `pow` every single time that loop iterates, thus slowing down your program, [pow() may not give you correct results when using integer exponents](http://stackoverflow.com/questions/25678481/why-does-pown-2-return-24-when-n-5-with-my-compiler-and-os) – PaulMcKenzie Apr 17 '17 at 17:52
  • 1
    Please read [MCVE] guidance on posting code - post contains way to much code at this point, usually 1-2 lines are enough to demonstrate such compiler error. – Alexei Levenkov Apr 17 '17 at 17:53

3 Answers3

1

Why the error?

C++ has much tighter rules than C (and calloc is a C function) about what kind of conversions the compiler will perform for you when performing an assignment. Assigning void * to anything but void * requires you to tell the compiler, "Yes, this looks stupid and is risky, but I know what I'm doing."

Why is this an error?

There is a long history of botched implicit conversions through the annals of C and C++ programming. Making this conversion an error at least makes the programmer stop and think about it before continuing.

What can I do about it?

In this case the most logical simple solution is

std::vector<std::vector<double>> descriptive_name_goes_here(n+1, std::vector<double>(n+1));

This will allocate all of the required storage and set it all to 0. It manages its own memory including deleting and resizing as required. It's pretty much fire and forget.

Documentation on std::vector

After this comes a variety of smart pointers, particularly

std::unique_ptr<std::unique_ptr<double[]>[]> descriptive_name_goes_here =
    std::make_unique<std::unique_ptr<double[]>[]>(n+1);

and a for loop to make_unique the inner dimensions and assign all of the allocated values to 0.

A unique_ptr handles memory management, making sure the allocated data it refers to is destroyed when it is no longer referenced. As a member variable, unique_ptr can make passing around or storing an object in a container a pain in the neck because if a unique_ptr could be copied, it wouldn't necessarily be all that unique, would it? vector handles that nonsense for you.

Documentation on std::unique_ptr

Documentation on std::make_unique

If somehow the above two are impossible, and by 2017 this should be a wholly artificial restriction or the result of operating on legacy code requiring a legacy compiler, use

double ** descriptive_name_goes_here = new double*[n+1]

and a for loop to new the inner dimensions and assign all of the allocated values to 0.

This option places all of the memory management responsibility on the programmer, and even the gods of programming occasionally miss a case where a delete[] was required. It also means you the programmer are responsible to supporting the Rules of Three and Five to make certain the destruction, copy, assignment, and move logic in any wrapping classes is correct.

This is the absolute last resort for low-level work like writing a vector or smart pointer.

The only place for calloc in c++ would be in bizarre edge cases and frankly I can't think of any right now. It's very possible there aren't any.

With the options out of the way...

May I recommend making a simple matrix class out of a 1D array? The array of arrays approach looks nice and simple and elegant, and often has utterly terrible cache performance. Each of these arrays of arrays (or vectors of vectors) is comprised of many different blocks of memory scattered around in storage. This makes it more difficult for the CPU to predict where to go next and what data to load. Every time the CPU has to stop and wait for the cache to be loaded you suffer performance degradation measured in orders of magnitude.

#include <iostream>
#include <vector>

class Matrix
{
private:
    size_t rows, columns;
    std::vector<double> matrix;
public:
    Matrix(size_t numrows, size_t numcols) :
            rows(numrows), columns(numcols), matrix(rows * columns)
    {
    }

    double & operator()(size_t row, size_t column)
    {
        // check bounds here
        return matrix[row * columns + column]; // note 2D coordinates are flattened to 1D
    }

    double operator()(size_t row, size_t column) const
    {
        // check bounds here
        return matrix[row * columns + column];
    }

    size_t getRows() const
    {
        return rows;
    }
    size_t getColumns() const
    {
        return columns;
    }
};

This is easily turned into a template for generalizing the data stored

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
0

In c++ there is a vector container (a C++ Standard Template) which is similar to an array with an exception that it automatically handles its own storage requirements in case it grows

You need to switch to RAII
For example:

From this:

double **R;

  R = calloc((n+1), sizeof(double *));
  for (i = 0; i <= n; i++)
    R[i] = calloc((n+1), sizeof(double));

To:

vector <vector <double> >R(n+1, vector<int>(n+1));

You dont need to free alocated memory, it will be free when vector goes out of scope.

An you just need to change the signature of romberg to:

 void romberg(double f(double), double a, double b, int n, vector <vector <double> >R)

Also you can read about RAII c++ concept.

PS: The error you get can be avoided by simply explicit casting from void * to double * and double **, but this way can lead you to have more complications like memory leaks, etc.

Rama
  • 3,222
  • 2
  • 11
  • 26
-1

Your main looks pretty weird:

  • void main instead of int main
  • double F(double), G(double), P(double); -- ?!
  • If you use malloc/calloc/realloc you have to cast returned void pointers in C++.
  • do not forget to delete allocated memory.

For example:

int main()
{
    const int n = 10;
    double **R;

    R = new double*[n+1];
    for (int i = 0; i <= n; i++)
        R[i] = new double[n+1];
    cout << "The first function is F(x) = 1/(1 + x)\n";
    romberg(F, 0.0, 2.0,3, R);
    cout << "The second function is G(x) = exp(x)\n";
    romberg(G,-1.0, 1.0, 4, R);
    cout<<"The third function is P(x) = sqrt(x)\n";
    romberg(P,0.0, 1.0, 7, R);

    // free allocated memory
    for (int i = 0; i <= n; i++)
        delete[] R[i];
    delete[] R;
}

note that loop that inits doubles doesn't set them all to 0.

Or better use std::vector and update your other code to use it as well:

#include <vector>
using namespace std;

int main()
{
    const int n = 10;
    vector<vector<double> > R(n+1, vector<double>(n+1));
    cout << "The first function is F(x) = 1/(1 + x)\n";
    romberg(F, 0.0, 2.0,3, R);
    cout << "The second function is G(x) = exp(x)\n";
    romberg(G,-1.0, 1.0, 4, R);
    cout<<"The third function is P(x) = sqrt(x)\n";
    romberg(P,0.0, 1.0, 7, R);
}
Pavel P
  • 15,789
  • 11
  • 79
  • 128