0
#include <stdio.h>


double seed=0.579832467;

main(ac, av)
int ac;
char *av[];
  {
   /* declare variables */
   float *buf, fac;
   int sf, ne, i;

   /* prototypes? ( shouldn't they be outside the main ) */
   double rnd(), sd;

   /* gets the number of elements from command line */
   ne = atoi(av[1]);

   /* assigns the size of float ( in bytes ) to integer value */
   sf = sizeof(float);

   /* allocates appropriate memory for random number generation */
   buf = (float *)malloc(ne*sf);

   /* type cast, why?? */
   sd = (double)(ne);

   /* no idea what initrnd does */
   initrnd(sd/(sd+187.9753));

   /* checks if memory allocation is successful */
   if (buf == NULL)
     {
      fprintf(stderr, "rndneg: can't allocate %d bytes for buffer\n", ne*sf);
      exit(-1);
     }

   /* fills buffer with random number */
   for (i=0; i<ne; i++)
    {
     buf[i] = (float)(rnd());
    }

   /* writes the buffer, how does it know the file name? */ 
   write(1, buf, ne*sf);
}

/* random number generating function */
double rnd()
{
seed *= 997.0;
seed -= (double)((int)(seed));
return(seed);
}


initrnd(sd)

/* again no idea, why isn't this function void */
double sd;
{
seed = sd;
return(0);
}

This is some code for a PRNG. I am not very experienced with C and some of the things in this code make absolutely no sense to me. I tried to comment to code to track what's going on. I would appreciate it if some of the things I don't understand could be cleared up. Especially the declarations of variables and functions with the same name, as well as the initrnd subroutine, that doesn't seem to be defined in the program or any library I could find on the internet.

Thanks a lot.

wsavran
  • 327
  • 1
  • 2
  • 9

3 Answers3

2

This looks positively ancient.

A few answers to your questions:

  1. No, prototypes don't need to be outside functions. It's most common, perhaps, but not required.
  2. initrnd() just sets the global seed variable to a specific value, that is then used in the PRNG.
  3. The data is written to stdout; which is assumed to be using file descriptor 1. This use of a magical constant is not very pretty, it should be written as stdout (from <stdio.h>).
unwind
  • 391,730
  • 64
  • 469
  • 606
  • There's nothing ugly about assumption that stdout is fd 1. This is part of the (POSIX) standard. – R.. GitHub STOP HELPING ICE May 18 '11 at 17:09
  • Thank you, this script was written about 14 years ago. That clears a lot of stuff up. – wsavran May 18 '11 at 17:10
  • The only other question I have is: does the compiler recognize the > to precede a filename. I don't see anywhere in the code where a specific filename is handled. – wsavran May 18 '11 at 17:11
  • 2
    What seems to also confuse the OP, is this really is ancient. It's using K&R style function declaration. The function `initrnd(sd) double sd; { ... } ` Would be written as `int initrnd(double sd) { ... } ` nowadays. – nos May 18 '11 at 17:13
  • @wsavran: If by `>` you mean the output redirection syntax used on the command line, no, that is handled by the shell, and so the program does not need to have any knowledge of it. – jwodder May 18 '11 at 17:14
  • yes that is what i mean. okay so the program just writes to stdout and then the shell takes care of the file creation etc? – wsavran May 18 '11 at 17:16
  • 1
    @wsavran: Yes, that is how output redirecting works, it's a shell feature, not a feature implemented by every individual program. The program doesn't know if it's writing to a terminal or to a file. – unwind May 18 '11 at 17:30
1
   /* type cast, why?? */
   sd = (double)(ne);

because ne is an integer and sd is a double, therefore the cast is needed

   /* no idea what initrnd does */
   initrnd(sd/(sd+187.9753));

it is the last function, it sets the global variable seed with its parameter

   /* writes the buffer, how does it know the file name? */ 
   write(1, buf, ne*sf);

the file descriptor is 1, which stands for standard output, so this is like calling printf()

initrnd(sd)

/* again no idea, why isn't this function void */

this function is int, but it should be void (it doesn't make any difference anyway), perhaps the original programmer was lazy :P

BlackBear
  • 22,411
  • 10
  • 48
  • 86
  • Why is the seed declared with an initial value then? it seems like the seed is actually based on the number of random numbers to be generated and differs every time – wsavran May 18 '11 at 17:46
  • @wsavran: yeah, the seed keeps changing. if you look at initrnd() you'll se that seed is changed based on its value, that's why it need initialization. Don't know why 0.579832467 anyway – BlackBear May 18 '11 at 17:50
  • @wsavran, @BlackBear: The typecast is bogus. The compiler can properly convert from `int` to `double` all by itself. Even 14 years ago, the proper way to write that assignment was `sd = ne;` – pmg May 18 '11 at 19:05
0

The code is pre-standard C, so it's not using prototypes (but does declare functions, though apparently not unless absolutely necessary). The function definitions use the pre-standard K&R style of declaring the parameters to the function. With non-protoyped functions, it incumbent on the programmer to ensure that the functions are called with the correct set of arguments, and that if a function returns nothing, then nothing is done with the function's 'value'.

If something other than an int is returned from a function, then the function must be declared (which is not necessarily a prototype) so the compiler is aware of the type of data returned from the function. Without a declaration, the compiler will assume that an int is returned (but if nothing is returned by the function, that's OK as long as you don't try to do anything with the function 'result').

Here are some direct comments to your questions:

   /* prototypes? ( shouldn't they be outside the main ) */
   //   this declares that function `rnd()` returns a double.  Technically, it's 
   //   not a prototype. Without the declaration the compiler would assume that it 
   //   returns an `int` so trying to use it wouldn't work.  It could be declared 
   //   outside `main()`, but it's OK to have it declared within the scope of 
   //   `main()`, just like it would be for a prototype.  That just means that 
   //    outside of `main()` the declaration is no longer in effect, so any calls
   //    to `rnd()` would assume that `int` is returned (incorrectly).
   double rnd(), sd;


   /* type cast, why?? */
   //   the cast is unnecessary and redundant, but OK
   sd = (double)(ne);

   /* no idea what initrnd does */
   //   apparently `initrnd()` initializes the rng seed (see below). There's
   //   no declaration in sight, so the compiler will default the return type 
   //   to `int` (unless it's in `stdio.h`).
   initrnd(sd/(sd+187.9753));

   /* writes the buffer, how does it know the file name? */ 
   //   `1` is the file descriptor for `stdout`.  Today this would probably
   //   be specified using `STDOUT_FILENO`, but even today `STDOUT_FILENO` is 
   //   required to be 1 (by POSIX).
   write(1, buf, ne*sf);
}

initrnd(sd)

/* again no idea, why isn't this function void */
//  `void` didn't exist pre-ANSI standard.
//  so this function 'returns' `int` by default.
double sd;
{
seed = sd;
return(0);
}
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Thank you very much. That clears up a lot. This algorithm is actually from a 1982 HP model calculator. So you're right, the code is incredibly archaic. Before I was born even. – wsavran May 19 '11 at 16:29