Need help with multi-dimensional arrays and functions

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Paul David Buchan

    Need help with multi-dimensional arrays and functions

    Hello,

    I'm attempting to write a program to read in database files (.dbf).
    When I do it all as a single procedure in main, everything works.
    However, what I really want, is to pass the database filename to a function,
    and have it pass back an array containing the database contents, and some
    parameters telling me the dimensions of the array.
    I've succeeded in getting my function to read in the dbf file, and it
    returns the dimensions of the array to main, but the array is my stumbling
    block. I keep seg-faulting.
    I allocate memory for the array inside the function, because I won't know
    how big the database is until the function opens it up and analyzes it.

    So my question is: can I make this program work such that the function
    "readfile" opens the database file, allocates memory for an array, and
    then passes that array back to main, which had no prior knowledge of the
    required size of the array?

    I'm really struggling with the pointer concept, I'm afraid.
    Any help is appreciated.

    Below are two versions separated by asterisk lines. The difference is in
    my treatment of array "input" and "input_arra y".
    Sorry they're so long, but I didn't want to trim too much for fear of missing
    something important.

    I'm using GCC on windows XP.

    Dave Buchan
    pdbuchan@yahoo. com

    *************** *************** ***************
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>

    /*int readfile (char *, int, int, int, char ***); */

    int main ()
    {
    int i,j,nrecords,nf ields,nchars;
    char filename[100];
    char ***input;

    strcpy (filename, "input.dbf" );

    readfile (filename,&nrec ords,&nfields,& nchars,input);
    printf ("\n%u %u %u", nrecords,nfield s,nchars);
    printf (" %s",filename) ;

    for (i=0; i<nrecords; i=i+1) {
    for (j=0; j<nfields; j=j+1) {
    printf ("%s ",input[i][j]);
    }
    printf ("\n");
    }

    }





    int readfile (filename,nreco rds,nfields,nch ars,input_array )
    int *nrecords, *nfields, *nchars;
    char filename[];
    char ***input_array;
    {
    int i,j,k,c,b1,b2,b 3,b4,headlen,re clen;
    int nrows, ncols,max;
    int *dbf,*fieldlen;
    FILE *fi;

    /* Attempt to open .dbf file */
    fi = fopen (filename, "rb");
    if (fi==NULL) {
    printf ("Can't open .dbf file.\n");
    exit (EXIT_FAILURE);
    }

    /* Count number of bytes in .dbf file */
    i=0;
    while ((b1=fgetc(fi)) !=EOF) {
    i=i+1;
    }
    fclose (fi);

    /* Allocate array for file contents */
    dbf = (int *)malloc(i*size of(int));

    /* Read .dbf file into array dbf */
    i=0;
    fi = fopen (filename, "rb");
    while ((dbf[i]=fgetc(fi)) !=EOF) {
    i=i+1;
    }
    fclose (fi);

    /* Number of records (4 bytes) */
    *nrecords=(dbf[7]*256*256*256)+( dbf[6]*256*256)+(dbf[5]*256)+dbf[4];

    /* Length of header (2 bytes) */
    headlen=(dbf[9]*256)+dbf[8];

    /* Length of each record (2 bytes) */
    reclen=(dbf[11]*256)+dbf[10];

    /* Count number of fields in each record */
    *nfields=0;
    j=32;
    while (dbf[j]!=13) {
    j=j+32;
    *nfields=*nfiel ds+1;
    }

    /* Allocate array for field lengths */
    fieldlen = (int *)malloc(*nfiel ds*sizeof(int)) ;

    /* Populate array of field lengths (1 byte each) */
    *nchars=0;
    for (i=0; i<*nfields; i=i+1) {
    fieldlen[i]=dbf[48+(i*32)];
    if (fieldlen[i]>*nchars) {
    *nchars=fieldle n[i];
    }
    }

    nrows=*nrecords +1; /* Add 1 because of header */
    ncols=*nfields;
    /* Allocate 3-dimensional array nrows-by-ncols-by-nchars */
    input_array = (char ***) malloc (nrows*sizeof(c har **));
    for (i=0; i<nrows; i=i+1) {
    input_array[i] = (char **) malloc(ncols*si zeof(char *));
    for (j=0; j<ncols; j=j+1) {
    input_array[i][j] = (char *) malloc(*nchars* sizeof(char));
    }
    }

    /* Initialize array contents to NULL */
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    for (k=0; k<*nchars; k=k+1) {
    input_array[i][j][k]='\0';
    }
    }
    }

    /* Write field titles to array */
    for (i=0; i<*nfields; i=i+1) {
    j=0;
    while (dbf[(i*32)+32+j] !=NULL) {
    input_array[0][i][j]=dbf[(i*32)+32+j];
    j=j+1;
    }
    }

    /* Write all data fields to array */
    for (i=0; i<*nrecords; i=i+1) {
    c=1; /* Ignore Record Delete Flag */
    for (j=0; j<*nfields; j=j+1) {
    for (k=0; k<fieldlen[j]; k=k+1) {
    input_array[i+1][j][k]=dbf[headlen+(i*recl en)+c];
    c=c+1;
    }
    }
    }

    /* Trim off any trailing spaces, tabs, or newlines */
    for (i=0; i<*nrecords; i=i+1) {
    for (j=0; j<*nfields; j=j+1) {
    for (k=fieldlen[j]-1; k>=0; k=k-1) {
    if (input_array[i][j][k] !=' ' && input_array[i][j][k] !='\t'
    && input_array[i][j][k] !='\n') {
    break;
    }
    }
    input_array[i][j][k+1]='\0';
    }
    }

    /* De-allocate memory
    free (dbf);
    free (fieldlen);
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    free((void *)input_array[i][j]);
    }
    }
    free ((void *)input_array); */

    return (EXIT_SUCCESS);
    }

    *************** *************** ***************
    In the following version I attempt to treat array "input"
    and "input_arra y" inthe same manner as I treat nrecords,
    nfields, and nchars.

    *************** *************** ***************

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>

    /*int readfile (char *, int, int, int, char ***); */

    int main ()
    {
    int i,j,nrecords,nf ields,nchars;
    char filename[100];
    char ***input;

    strcpy (filename, "input.dbf" );

    readfile (filename,&nrec ords,&nfields,& nchars,&input);
    printf ("\n%u %u %u", nrecords,nfield s,nchars);
    printf (" %s",filename) ;

    for (i=0; i<nrecords; i=i+1) {
    for (j=0; j<nfields; j=j+1) {
    printf ("%s ",input[i][j]);
    }
    printf ("\n");
    }
    exit(1);

    }





    int readfile (filename,nreco rds,nfields,nch ars,input_array )
    int *nrecords, *nfields, *nchars;
    char filename[];
    char ****input_array ;
    {
    int i,j,k,c,b1,b2,b 3,b4,headlen,re clen;
    int nrows, ncols,max;
    int *dbf,*fieldlen;
    FILE *fi;

    /* Attempt to open .dbf file */
    fi = fopen (filename, "rb");
    if (fi==NULL) {
    printf ("Can't open .dbf file.\n");
    exit (EXIT_FAILURE);
    }

    /* Count number of bytes in .dbf file */
    i=0;
    while ((b1=fgetc(fi)) !=EOF) {
    i=i+1;
    }
    fclose (fi);

    /* Allocate array for file contents */
    dbf = (int *)malloc(i*size of(int));

    /* Read .dbf file into array dbf */
    i=0;
    fi = fopen (filename, "rb");
    while ((dbf[i]=fgetc(fi)) !=EOF) {
    i=i+1;
    }
    fclose (fi);

    /* Number of records (4 bytes) */
    *nrecords=(dbf[7]*256*256*256)+( dbf[6]*256*256)+(dbf[5]*256)+dbf[4];

    /* Length of header (2 bytes) */
    headlen=(dbf[9]*256)+dbf[8];

    /* Length of each record (2 bytes) */
    reclen=(dbf[11]*256)+dbf[10];

    /* Count number of fields in each record */
    *nfields=0;
    j=32;
    while (dbf[j]!=13) {
    j=j+32;
    *nfields=*nfiel ds+1;
    }

    /* Allocate array for field lengths */
    fieldlen = (int *)malloc(*nfiel ds*sizeof(int)) ;

    /* Populate array of field lengths (1 byte each) */
    *nchars=0;
    for (i=0; i<*nfields; i=i+1) {
    fieldlen[i]=dbf[48+(i*32)];
    if (fieldlen[i]>*nchars) {
    *nchars=fieldle n[i];
    }
    }

    nrows=*nrecords +1; /* Add 1 because of header */
    ncols=*nfields;
    /* Allocate 3-dimensional array nrows-by-ncols-by-nchars */
    *input_array = (char ***) malloc (nrows*sizeof(c har **));
    for (i=0; i<nrows; i=i+1) {
    *input_array[i] = (char **) malloc(ncols*si zeof(char *));
    for (j=0; j<ncols; j=j+1) {
    *input_array[i][j] = (char *) malloc(*nchars* sizeof(char));
    }
    }

    /* Initialize array contents to NULL */
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    for (k=0; k<*nchars; k=k+1) {
    *input_array[i][j][k]='\0';
    }
    }
    }

    /* Write field titles to array */
    for (i=0; i<*nfields; i=i+1) {
    j=0;
    while (dbf[(i*32)+32+j] !=NULL) {
    *input_array[0][i][j]=dbf[(i*32)+32+j];
    j=j+1;
    }
    }

    /* Write all data fields to array */
    for (i=0; i<*nrecords; i=i+1) {
    c=1; /* Ignore Record Delete Flag */
    for (j=0; j<*nfields; j=j+1) {
    for (k=0; k<fieldlen[j]; k=k+1) {
    *input_array[i+1][j][k]=dbf[headlen+(i*recl en)+c];
    c=c+1;
    }
    }
    }

    /* Trim off any trailing spaces, tabs, or newlines */
    for (i=0; i<*nrecords; i=i+1) {
    for (j=0; j<*nfields; j=j+1) {
    for (k=fieldlen[j]-1; k>=0; k=k-1) {
    if (*input_array[i][j][k] !=' ' && *input_array[i][j][k] !='\t'
    && *input_array[i][j][k] !='\n') {
    break;
    }
    }
    *input_array[i][j][k+1]='\0';
    }
    }

    /* De-allocate memory
    free (dbf);
    free (fieldlen);
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    free((void *)input_array[i][j]);
    }
    }
    free ((void *)input_array); */

    return (EXIT_SUCCESS);
    }

  • Szabolcs Borsanyi

    #2
    Re: Need help with multi-dimensional arrays and functions

    On Sat, May 24, 2008 at 02:26:55PM -0400, Paul David Buchan wrote:
    Hello,
    >
    I'm attempting to write a program to read in database files (.dbf).
    When I do it all as a single procedure in main, everything works.
    However, what I really want, is to pass the database filename to a function,
    and have it pass back an array containing the database contents, and some
    [snip]

    Let's get to your code
    *************** *************** ***************
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    >
    /*int readfile (char *, int, int, int, char ***); */
    Why is this a comment? Using a function prototype is nothing evil.
    int main ()
    {
    int i,j,nrecords,nf ields,nchars;
    char filename[100];
    char ***input;
    >
    strcpy (filename, "input.dbf" );
    >
    readfile (filename,&nrec ords,&nfields,& nchars,input);
    Uups, the 'input' variable inside of readfile() receives an uninitialised
    value, and has no chance to return the initialised value to main().
    Your second version does not have this problem. Why don't you pass the 'input'
    pointer as a return value of the function?
    printf ("\n%u %u %u", nrecords,nfield s,nchars);
    Are you sure about the %u ?
    printf (" %s",filename) ;
    >
    for (i=0; i<nrecords; i=i+1) {
    for (j=0; j<nfields; j=j+1) {
    printf ("%s ",input[i][j]);
    }
    printf ("\n");
    }
    }
    Let's see the second attempt
    *************** *************** ***************
    In the following version I attempt to treat array "input"
    and "input_arra y" inthe same manner as I treat nrecords,
    nfields, and nchars.
    >
    *************** *************** ***************
    >
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    >
    /*int readfile (char *, int, int, int, char ***); */
    Ok, with deleting /* and */
    >
    int main ()
    {
    int i,j,nrecords,nf ields,nchars;
    char filename[100];
    char ***input;
    >
    strcpy (filename, "input.dbf" );
    >
    readfile (filename,&nrec ords,&nfields,& nchars,&input);
    printf ("\n%u %u %u", nrecords,nfield s,nchars);
    %u is for unsigned ints. Yours are plain ints, so better use %d.
    printf (" %s",filename) ;
    >
    for (i=0; i<nrecords; i=i+1) {
    I wonder why you keep writing i=i+1, most people do i++, but your version
    is equally correct.
    for (j=0; j<nfields; j=j+1) {
    printf ("%s ",input[i][j]);
    }
    printf ("\n");
    }
    exit(1);
    Does 1 mean a kind of failure? Normally exit(0) is used for
    the successful termination, but from the main(), a 'return 0' will do.
    >
    }
    >
    And now comes your lengthy function
    >
    int readfile (filename,nreco rds,nfields,nch ars,input_array )
    int *nrecords, *nfields, *nchars;
    char filename[];
    char ****input_array ;
    This is the ancient way of defining a function.
    {
    int i,j,k,c,b1,b2,b 3,b4,headlen,re clen;
    int nrows, ncols,max;
    int *dbf,*fieldlen;
    FILE *fi;
    >
    /* Attempt to open .dbf file */
    fi = fopen (filename, "rb");
    if (fi==NULL) {
    printf ("Can't open .dbf file.\n");
    exit (EXIT_FAILURE);
    }
    >
    /* Count number of bytes in .dbf file */
    i=0;
    while ((b1=fgetc(fi)) !=EOF) {
    i=i+1;
    }
    fclose (fi);
    >
    /* Allocate array for file contents */
    dbf = (int *)malloc(i*size of(int));
    The (int*) is superfluous and can be misleading.
    sizeof(*dbf) is slightly easier to maintain than sizeof(int)
    /* Read .dbf file into array dbf */
    i=0;
    fi = fopen (filename, "rb");
    You are reopening a file. Are you doing so just to get again to its
    begining? How about a rewind() (or fseek())?. A reopening should
    again involve some error checking...

    [snip some code]
    nrows=*nrecords +1; /* Add 1 because of header */
    ncols=*nfields;
    /* Allocate 3-dimensional array nrows-by-ncols-by-nchars */
    *input_array = (char ***) malloc (nrows*sizeof(c har **));
    these casts in front of malloc are quite annoying. A C++ compiler
    will sure require this, but well, this is C, so let's save those
    keystrokes.
    for (i=0; i<nrows; i=i+1) {
    *input_array[i] = (char **) malloc(ncols*si zeof(char *));
    Uups!
    *input_array[i] is *(input_array[i]) that is input_array[i][0]
    I what you really wanted is (*input_array)[i]=...
    The catch is the operator precedence.
    for (j=0; j<ncols; j=j+1) {
    *input_array[i][j] = (char *) malloc(*nchars* sizeof(char));
    Again: (*input_array)[i][j] will be better
    }
    }
    >
    /* Initialize array contents to NULL */
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    for (k=0; k<*nchars; k=k+1) {
    *input_array[i][j][k]='\0';
    Yet again: (*input_array)[i][j][k]=0 will be better.
    Notice that '\0' is the four character long-hand for 0 (both are ints).

    [snip], you'll have to correct the same error in a few
    /* De-allocate memory
    free (dbf);
    free (fieldlen);
    for (i=0; i<nrows; i=i+1) {
    for (j=0; j<ncols; j=j+1) {
    free((void *)input_array[i][j]);
    ???????
    Are you releasing the memory before returning from the function?
    Where is the caller (the main()) supposed to find the data?
    And please do not cast the pointers to (void*), this conversion takes
    place automatially.
    }
    }
    free ((void *)input_array); */
    ???????
    Are you planning to call free() on the the callers memory? Not very
    polite. These lines stayed in your code from the times when everything
    was in the main()
    >
    return (EXIT_SUCCESS);
    This too.
    }
    By the way, you do not need to call free() on the malloced memory. This
    is all done for you when your program exits. The friendly way of
    the deallocation, if you need one, is to accompany your read routine with
    a deallocator function.

    Szabolcs

    Comment

    • Paul David Buchan

      #3
      Re: Need help with multi-dimensional arrays and functions

      Thanks Szabolcs!

      Looks like a lot of good information there.
      It's going to take me some time to understand it all.
      I've been FORTRAN programming for years, so C is hard for me.

      I really appreciate your comments.

      Dave

      Comment

      • Paul David Buchan

        #4
        Re: Need help with multi-dimensional arrays and functions

        Hi guys,

        Ben, You're right! I actually had a brief moment of clarity
        when I was thinking of leaving everything in the dbf array,
        but I somehow got onto the track I did.

        Regardless, I totally missed the fact that I didn't terminate the strings.
        And now that you mention is, there may be a screw-up in my trimming
        routine. I need to check the bounds on that. I haven't looked yet.

        Thanks for the input.

        Dave

        Comment

        • Paul David Buchan

          #5
          Re: Need help with multi-dimensional arrays and functions

          Szabolcs,

          I'm pleased to report that I implemented your recommendations ,
          and now it works perfectly!

          Thanks again,

          Dave

          Comment

          Working...