0

I am a little new to using multi-dimensional arrays, but I think after extensive research, I was starting to feel super confident I could work with them correctly. Apparently, I guess I don't quite know them completely lol. In this case, I am trying to read in all the test files from directory ("path" in this case), insert each "d_name" into its own row of a 2-dimensional char array. I am able to print each string element (filename, in this case) successfully when I use the "[]" array-type notation. However, after I pass a pointer for this 2-dimensional char array into a "readFiles" function, I am able to successfully print out the 1st string of this array, but when I attempt to move the pointer in order to point at the next row (the 2nd string, or filename), I receive unexpected garbage results, eventually ending in a seg fault. So something is wrong with the way I am iterating through an array with multiple rows like this. I REALLY do not wish to print character by character here, I just want to print out 1 string (filename) at a time by simply moving the pointer down 1 row after a string element is printed. Could somebody please help if you can ? I really really appreciate your time on the weekend like this!

my program (just a giant function for now)...

#include <stdio.h> 
#include <dirent.h> 
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include<unistd.h> 
#include <stdbool.h>
#include <errno.h>
#include <sys/types.h>

void alphabetcountmulthreads( char *path )
{


    void readFiles( char **allfiles, int total_rows )
    {

        printf( "The pointer passed into the function is at %p and its first element is...\n", (void *)*allfiles );

        for ( int k = 0; k < total_rows; ++k )
        {
            printf( "%s\n", *( allfiles + k ) );
            printf( "...which was found at...%p\n", (void *)*(allfiles + k) );
            printf( "...because the pointer was added by %d (string should be in row %d )\n", k, k );
            
        }

    }



    DIR *dir;
    struct dirent *in_file;

    char fileList[100][30];
    char *filename_reader = &fileList[0][0];

    int filled_rows = 0;
    int file_count = 0;


    dir = opendir( path );      // open the data directory

    if( dir == NULL )
    {
        printf( "Unable to read directory!" );
        exit( 1 );
    }

    while( ( in_file = readdir( dir ) ) != NULL )
    {
        FILE *entry_file;
        char *check_filename;   // for checking if file ends EXACTLY with ".txt"
        int c;
        
        check_filename = strrchr( (in_file->d_name), '.' );     // move to last "."

        if ( !strcmp ( in_file->d_name, "." ) || !strcmp ( in_file->d_name, ".." ) )
        {
            continue;
        }

        if ( check_filename )
        {
            if ( strcmp( ( check_filename ) , ".txt" ) )    // does it end in ".txt" ?
            {
                continue;
            }
        
            file_count++;
            
            int filename_length = strlen( (in_file->d_name) );            

            // path = "../data"
            printf( "%s...%d c's long\n", in_file->d_name, filename_length );
            strcpy( fileList[filled_rows], (in_file->d_name) );

            printf( "The string, %s\n", fileList[filled_rows] );
            printf( "had its first character stored at...%p\n", &fileList[filled_rows][0] );

            filled_rows++;
        }

    }


    closedir( dir );


    
    fileList[ (filled_rows - 1 + 1) ][0] = '\0';

    filename_reader = &fileList[0][0];

    char **function_insert1 = &filename_reader;

    printf( "The actual address of the 2D array is %p\n", (void*)&fileList[0][0] );
    printf( "Its pointer's location is at %p\n", filename_reader );
    readFiles( function_insert1, filled_rows );
    
}

and here is my output........

test11.txt...10 c's long
The string, test11.txt
had its first character stored at...0x7fffd2b86180
test10.txt...10 c's long
The string, test10.txt
had its first character stored at...0x7fffd2b8619e
test2.txt...9 c's long
The string, test2.txt
had its first character stored at...0x7fffd2b861bc
test1.txt...9 c's long
The string, test1.txt
had its first character stored at...0x7fffd2b861da
test3.txt...9 c's long
The string, test3.txt
had its first character stored at...0x7fffd2b861f8
test12.txt...10 c's long
The string, test12.txt
had its first character stored at...0x7fffd2b86216
test13.txt...10 c's long
The string, test13.txt
had its first character stored at...0x7fffd2b86234
The actual address of the 2D array is 0x7fffd2b86180
Its pointer's location is at 0x7fffd2b86180
The pointer passed into the function is at 0x7fffd2b86180 and its first element is...
test11.txt
...which was found at...0x7fffd2b86180
...because the pointer was added by 0 (string should be in row 0 )

...which was found at...0x7fffd2b860e0
...because the pointer was added by 1 (string should be in row 1 )

...which was found at...0xa5b420
...because the pointer was added by 2 (string should be in row 2 )
Segmentation faultSegmentation fault
PluffTed
  • 53
  • 5
  • `char fileList[100][30]; char (*filename_reader)[30] = fileList;` and `fileList[ (filled_rows - 1 + 1) ][0] = '\0';` is just `fileList[filled_rows][0] = 0;` You may want to read [Difference between char *pp and (char*) p?](https://stackoverflow.com/a/60519053/3422102) and [Pointer to pointer of structs indexing out of bounds(?)...](https://stackoverflow.com/a/60639540/3422102) – David C. Rankin Jul 18 '20 at 22:47
  • 2
    Also, always compile with *warnings enabled*, and **do not** accept code until it *compiles without warning*. To enable warnings add `-Wall -Wextra -pedantic` to your `gcc/clang` compile string (also consider adding `-Wshadow` to warn on shadowed variables). For **VS** (`cl.exe` on windows), use `/W3`. All other compilers will have similar options. Read and understand each warning -- then go fix it. That would have identified your pointer mismatch. They will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you. – David C. Rankin Jul 18 '20 at 22:50
  • Additional 2D array pointer help [How to pass a 2D array to a function using single pointer](https://stackoverflow.com/a/62495880/3422102) – David C. Rankin Jul 18 '20 at 22:53
  • Finally found the exact one I was looking for [2D array seg fault in C](https://stackoverflow.com/a/60498812/3422102) Let me know if you have further questions. (your question is more or less a duplicate of this one from the 2D array handling standpoint) – David C. Rankin Jul 18 '20 at 23:12
  • Hey David, I really appreciate the time you've taken to respond here. I did not notice elsewhere that sort of notation you mentioned ("char (*filename_reader)[30] = fileList"), but unfortunately, when I try to adapt the program for this, the same problem persists. – PluffTed Jul 18 '20 at 23:21
  • I just want to be able to move down a whole row at a time...is this not possible? – PluffTed Jul 18 '20 at 23:22
  • 1
    You have other issues, but `fileList` is a 2D array (an array of 1D arrays). On access, `fileList` is converted to a pointer to its first element (the first 1D array). So `char (*filename_reader)[30] = fileList;` declares a *pointer-to-array of* `char[30]`. So `*filename_reader` provides access to the first string, then `filename_reader++` will advance the pointer by 30-characters (to your next 1D array) and `*filename_reader` now provides the 2nd string. Note, since you use an empty-string as a *sentinel*, you would iterate `while(**filename_reader) puts (*filename_reader++);` – David C. Rankin Jul 18 '20 at 23:35
  • 1
    Here is a very minimal example of what you are doing. I uses the 2D array of strings (named `strings`) and a pointer to the first 1D array in `strings` (`pstring`) to iterate over each sting in the 2D array [Pointer-to-Array](https://paste.opensuse.org/38053734). Note, C doesn't allow nested functions, so I don't know how you are compiling with the declaration for `void readFiles()` nested in `void alphabetcountmulthreads()` – David C. Rankin Jul 18 '20 at 23:46
  • 1
    If you are still stuck, edit and provide [A Minimal, Complete, and Verifiable Example (MCVE)](http://stackoverflow.com/help/mcve) and I'm happy to help get you going. – David C. Rankin Jul 19 '20 at 01:00
  • David, thank you so much! Within the first main function, the printing of each element with the char (*filename_reader)[30] using the incrementing pointer is working. BUT, I was really hoping you could give me a suggestion as to how I can pass this same pointer to the readFiles function? Preferably it be a double pointer so that this function can manipulate the value of this pointer? Again thank you so much – PluffTed Jul 19 '20 at 01:46
  • Pointer **type** controls **pointer-arithmetic**. You do not have a *pointer to another pointer*, you have a *pointer to array of* `char[30]`. So you will require `void readFiles( char (*allfiles)[30], int total_rows )` just as you declared `char (*filename_reader)[30] = fileList;` in `main()`. If you pass `char**` (pointer to pointer), then `p++` advances 8-bytes (or 4-bytes on x86) to the **next pointer**. By passing `char (*)[30]`, then `p++` advances 30-bytes to the beginning of the next string (1D array). See [2d to function](https://paste.opensuse.org/13305884) – David C. Rankin Jul 19 '20 at 02:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/218123/discussion-between-pluffted-and-david-c-rankin). – PluffTed Jul 19 '20 at 03:47
  • Awesome David your code in opensuse helped me a lot! You clearly know all of the ins and outs of C and I can only dream of getting to your level of expertise. My function works now. Hope you have a wonderful rest of your day x) – PluffTed Jul 19 '20 at 04:22
  • 1
    Learning C is more a journey than a race. There is a lot to learn, but you are on the right track. Best advise is to *slow down*, enjoy the journey and approach it the same way you approach eating a whale -- one byte at a time.... – David C. Rankin Jul 19 '20 at 04:26

0 Answers0