-1

I get following error when I compile my C code. I am using Numerical Recipes 2nd ed. functions rk4() for solving a first order differential equation.

I am not expert in this field. Any help will be highly appreciated.

Error is:

first_order_DE_RK4_example1.c:75: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token

code is:

#include "nrutil.h"
#include <stdio.h>
#include <math.h>

void rk4(float y[], float dydx[], int n, float x, float h, float yout[],
    void (*derivs)(float, float [], float []));

void (*derivs)(float, float[], float[]);


int main()
{
  int n; float h; float x;
  float y[1];
  float dydx[1];

  n=1;
  h=0.2;
  x=0;
  y[0] = 1;
  dydx[0] = 5.0;

void rk4(float y[], float dydx[], int n, float x, float h, float yout[], 
     void (*derivs)(float, float [], float []));

  return 0;
}


void rk4(float y[], float dydx[], int n, float x, float h, float yout[],
    void (*derivs)(float, float [], float []))
{
    int i;
    float xh,hh,h6,*dym,*dyt,*yt;

    dym=vector(1,n);
    dyt=vector(1,n);
    yt=vector(1,n);
    hh=h*0.5;
    h6=h/6.0;
    xh=x+hh;

    for (i=1;i<=n;i++) 
          { 
            yt[i]=y[i]+hh*dydx[i];
        (*derivs)(xh,yt,dyt);
          }

    for (i=1;i<=n;i++) 
          { yt[i]=y[i]+hh*dyt[i];
        (*derivs)(xh,yt,dym);
          }

    for (i=1;i<=n;i++) 
         {
        yt[i]=y[i]+h*dym[i];
        dym[i] += dyt[i];
     }

    (*derivs)(x+h,yt,dyt);

    for (i=1;i<=n;i++) 
          {
        yout[i]=y[i]+h6*(dydx[i]+dyt[i]+2.0*dym[i]);
          }

    free_vector(yt,1,n);
    free_vector(dyt,1,n);
    free_vector(dym,1,n);
}


void (*derivs)(float x, float y, float dydx)
 {
   float rhs;
   rhs = 1-x+4*y;
 }
atom
  • 153
  • 1
  • 9
  • 5
    `void (*derivs)(float x, float y, float dydx) { ... }` --> `void derivs(float x, float y, float dydx) { ... }` Or delete this. – BLUEPIXY Feb 02 '17 at 07:39
  • 1
    You want to at least read chapter 1.2 of this book, if not taking the time to go through a C primer. – alk Feb 02 '17 at 08:21
  • when compiling, always enable all the warnings then fix those warnings. The posted code causes the compiler to output about 3 dozen messages about problems. (for `gcc`, at a minimum use: `-Wall -Wextra -pedantic` I also use: `-Wconversion -std=gnu99) – user3629249 Feb 03 '17 at 02:25
  • for ease of readability and understanding: 1) use meaningful variable names. Variable names should indicate content or usage (or better, both) Most of the posted variable names are meaningless, even the current context. 2) consistently indent the code. indent after every opening brace '{'. unindent before every closing brace '}'. Do not use tabs for indenting. Suggest using 4 spaces for each indent level as that is visible even with variable width fonts. – user3629249 Feb 03 '17 at 02:29
  • this kind of statement/parameter: `void (*derivs)(float, float [], float []))` says it is/will be a pointer to a function that contains those 3 parameter types and does not return anything. – user3629249 Feb 03 '17 at 02:32
  • this line: `void rk4(float y[], float dydx[], int n, float x, float h, float yout[], void (*derivs)(float, float [], float []));` inside the main() function is a prototype for a function, NOT a call to that function. – user3629249 Feb 03 '17 at 02:33
  • the code contains a reference to the 'home grown' header file: `nrutil.h`. How do you expect us to be able to reproduce your problem when you haven;t posted the whole code? – user3629249 Feb 03 '17 at 02:36
  • this line: `void (*derivs)(float x, float y, float dydx)` is a good way to make an instance of a variable type that contains a pointer to a function. The actual function should be similar to: `void myFunction( float x, float y, float dydx ) { .... } – user3629249 Feb 03 '17 at 02:38
  • of what use is the variable: `rhs`? it disappears when the function exits, so most likely the compiler will optimize it out of existence, including the calculation of its' value – user3629249 Feb 03 '17 at 02:42
  • note: a `float` constant is declared as `1.2f`. With out the `f` a `double` is being declared. – user3629249 Feb 03 '17 at 02:47
  • to declare a `float` value, the value must contain a decimal point `.` and a trailing `f` – user3629249 Feb 03 '17 at 02:48
  • in `main()`, in the call to `rk4()`, the parameter `yout` does not exist. – user3629249 Feb 03 '17 at 02:53
  • in C, referencing the name of an array degrades to the address of the first byte of the array. Also, when calling `rk4()` should pass the array pointer parameters as simply the name of the array, NOT `float dydx[]` (and similar statements) and do not include the type of the parameter I.E. just use: `dydx` Suggest the statement be similar to: `rk4(y, dydx, n, x, h, yout);` Note the address of the sub function is not listed and not used and not needed – user3629249 Feb 03 '17 at 02:56
  • The helper methods of the "Numerical recipes" `nrutils.h` and `nrutils.c` can be found at http://numerical.recipes/public-domain.html – Lutz Lehmann Feb 03 '17 at 12:49

2 Answers2

1

You appear to be using function pointers for no reason and confusing prototypes and declarations with calls and definitions.

At the top, this change:

void rk4(float y[], float dydx[], int n, float x, float h, float yout[],
    void (*derivs)(float, float [], float []));

void (*derivs)(float, float[], float[]);
void rk4(float y[], float dydx[], int n, float x, float h, float yout[]);

void derivs(float, float[], float[]);

In main, this:

void rk4(float y[], float dydx[], int n, float x, float h, float yout[], 
 void (*derivs)(float, float [], float []));
rk4(y, dydx, n, x, h, yout); // with some appropriate declared yout

In the definition of rk4,

void rk4(float y[], float dydx[], int n, float x, float h, float yout[],
    void (*derivs)(float, float [], float []))
void rk4(float y[], float dydx[], int n, float x, float h, float yout[])

For calls to derivs in rk4,

(*derivs)(xh,yt,dyt);
derivs(xh, yt, dyt);

In the definition of derivs,

void (*derivs)(float x, float y, float dydx)
void derivs(float x, float y[], float dydx[])

and then fix the computation of rhs.

If you find yourself writing sometype (*name in the future without intending to use function pointers, stop. =)

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 2
    if `void derivs(float, float[], float[]);` is prototype, `void derivs(float x, float y, float dydx)` --> `void derivs(float x, float y[], float dydx[])` but might mismatch (to `rhs = 1-x+4*y;`). – BLUEPIXY Feb 02 '17 at 07:47
  • your suggested fix in main() for the call to `rk4()` is incorrect as it still contains the types for each of the parameters, suggest: `rk4(y, dydx, n, x, h, yout);` – user3629249 Feb 03 '17 at 03:02
  • @user3629249: Is that last bit a quote or a suggestion? Because it’s what I wrote. – Ry- Feb 03 '17 at 04:17
0

After correcting to a compiling version, I ran diff to report the changes in a systematic manner. These changes with their reasons are:

8c8
< void (*derivs)(float, float[], float[]);
---
> void myprime(float, float[], float[]);

To avoid confusion, I will use a different name for the parameter and the user-defined function that will fill that parameter.

The user-defined derivative function is just an ordinary function (procedure, subroutine, method), the function pointer declaration is restricted to the places where actually a variable is declared as a function pointer.

14a15
>   float yout[1];

Add the missing variable declaration. This will help the program compile, but not run, as you are mixing two different index ranges. The standard range of float arr[N] is 0..N-1. The vector helper function of "Numerical recipes" uses pointer arithmetic so that the index range for the valid, allocated part of float *arr=vector(L,H) is L..H. In your present code you are mixing ranges 0..0 and 1..1 and thus accessing memory outside the allocated range. Switch everything to vector and the index range 1..n.

23,24c24
< void rk4(float y[], float dydx[], int n, float x, float h, float yout[], 
<      void (*derivs)(float, float [], float []));
---
>   rk4(y, dydx, n, x, h, yout, myprime);

To call a function looks different than to declare that function. In the call you just use the variable names for the parameters.

44,47c44,47
<           { 
<             yt[i]=y[i]+hh*dydx[i];
<         (*derivs)(xh,yt,dyt);
<           }
---
>     {
>         yt[i]=y[i]+hh*dydx[i];
>     }
>     derivs(xh,yt,dyt);

First you run the loop to compute the next state vector, then you call the derivative computation once.

The idea to pass the function pointer as a parameter is that you then use it like it were a function. No need to add any dereferencing etc., the C mechanism is constructed like that.

Both points also apply to the other two derivative computations. By the way, you are missing the computation of the derivative in dydx. The idea may be that this value is also used outside the routine for other purposes, so passing it avoids double computation.

73c74
< void (*derivs)(float x, float y, float dydx)
---
> void myprime(float x, float y[], float dydx[])

Implement the user-defined derivative function with the name change.

75,76c76
<    float rhs;
<    rhs = 1-x+4*y;
---
>    dydx[0] = 1-x+4*y[0];

The passed arguments for y and dydx are vectors, they have to be treated as vectors with appropriate de-referencing or element addressing even in the scalar case.

You need to implement the intent behind the function declaration. The result of the derivative computation is to be stored in dydx.

Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51