简体   繁体   中英

How to return an array of character pointers from a function back to main?

I'm trying to store a list of files with a .txt extension into an array from my current working directory. I've gotten as far as getting the array together, now I just need to return it. I'm new to C and I'm not used to the pointer situation. How can I return an array of char pointers? I have the array created, and its allocated based on how many Text files I find in the directory.

I'm getting two errors when I try to compile which leads me to believe that my understanding of pointers is not what I thought it was. I also have been reading that my array will be destroyed when returning from the function, because it is on the stack. I'm not sure how to fix that. Any help or criticism would be welcome.

    // prototypes
    char* getLine();
    void replaceWord();
    char * getTxtFilesInDir(char*);
    bool isTxt(char *);

    int main(int argc, char **argv) {

       // Check to see if correct number of arguments is given
       if (argc < 4) {
          printf("ERROR: Not enough parameters supplied. Please supply a find,"
                "replace, and prefix parameter and try again.\n");
          return -1;
       }

       // Initialize variables
       char *find, *replace, *prefix;
       find=argv[1];
       replace=argv[2];
       prefix=argv[3];
       char cd[1024];

       // Get the Current working directory to grab files
       if (getcwd(cd, sizeof(cd)) != NULL) {
          printf("Current working dir is: %s\n", cd);
       } else {
          perror("getcwd() error");
       }

       // Get the values of the arguments
       printf("Value of find: %s\nValue of replace: %s\nValue of prefix: %s\n",
             find, replace, prefix);

       // create an array of the files that are valid
       char* files = getTxtFilesInDir(cd);

       return 0;

    }
char*  getTxtFilesInDir(char* directory) {
   DIR *d;
   struct dirent *dir;
   d = opendir(directory);
   int n=0, i=0;

   // get the number of text files in the directory
   if (d) {
      while((dir = readdir(d)) != NULL) {
         if (isTxt(dir->d_name)) {
            n++;
         }
      }
   }

   rewinddir(d);
   // Create the array of text files depending on the size
   static char* txtFiles[n];

   // add the text files to the array
   if (d) {
      printf("Found: \n");
      while ((dir = readdir(d)) != NULL)
      {
         if (isTxt(dir->d_name)) {
            printf("%s\n", dir->d_name);
            txtFiles[i]= (char*) malloc (strlen(dir->d_name)+1);
            strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
            i++;

         }
      }
      closedir(d);
   }
   return txtFiles;
}

bool isTxt(char *file) {
   size_t len = strlen(file);
   return len > 4 && strcmp(file + len -4, ".txt") == 0;
}

If you want that getTxtFilesInDir returns an array of strings, it should return a char** , not a char* .

Also you don't need to declare the variable as static . You declare a variable in a function as static , when you want that the variable remains the same for all calls of the function. In this case this is probably not what you want to do.

Without modifying too much of your initial code, you can first allocate memory for an arrray of strings, and then resize it when you've got a new entry. In this example I do that at once, because realloc(NULL, somesize) is the same as doing malloc(somesize) . That's why it's important to initialize *tmp = 0 and txtFiles = NULL , so this trick works. You should also pass a pointer to an size_t where you store the number of entries:

char **getTxtFilesInDir(const char* directory, size_t *len) {
    if(directory == NULL || len == NULL)
        return NULL;

    ...

    *len = 0;
    char **txtFiles = NULL, **tmp;
    char *str;

    if (d) {
      printf("Found: \n");
      while ((dir = readdir(d)) != NULL)
      {
         if (isTxt(dir->d_name))
         {
             tmp = realloc(txtFiles, (len+1) * sizeof *textFiles);
             if(tmp == NULL)
                 return txtFiles; // return what you've got so far


             str = malloc(strlen(dir->d_name) + 1);
             if(str == NULL)
             {
                 if(txtFiles == NULL) // first time, free everything
                 {
                     free(tmp);
                     return NULL;
                 }

                 return tmp; // return all you've got so far
             }

             strcpy(str, dir->d_name); // no need of strcnpy, you've allocated
                                       // enough memory
             txtFiles = tmp;
             txtFiles[(*len)++] = tmp;
         }
      }
      closedir(d);

      return txtFiles;
}

The important bits here is 1. how you expand the memory with realloc . Then it allocates memory for the string using malloc . I do that before txtFiles = tmp;, so that I don't have to write to many if(...==NULL) . If something goes wrong along the way, the function returns all the file names it already has stored.

Now in main you do:

int main(int argc, char **argv)
{
    ...

    size_t len = 0;
    char **files = getTxtFilesInDir(cd, &len);

    if(file == NULL)
    {
        fprintf(stderr, "No files found\n");
        return 1;
    }

    for(size_t i = 0; i < len; ++i)
        printf("file: %s\n", files[i]);

    // freeing memory
    for(size_t i = 0; i < len; ++i)
        free(files[i]);

    free(files);

    return 0;
}

I also want to comment on your orginal way of copying:

strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));

strncpy is great when you want to limit the number of bytes to be copied. In your case you've already allocated enough memory, so there is no need to. And when you use strncpy , the limit should be bound to the number of bytes available to the destination, not the source, otherwise you might copy more bytes than it should. In your case you won't get a valid c-string, because you are limiting to copy to the lenth of dir->d_name , so the '\\0' -terminating won't be copied.

man strcpy

#include <string.h> char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n);

[...]

The strncpy() function is similar, except that at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src , the string placed in dest will not be null-terminated.

When you use strncpy you must make sure that the copy is a valid string by setting the '\\0' -terminating byte yourself. Because you've allocated strlen(dir->d_name)+1 bytes for the string:

strncpy(txtFiles[i], dir->d_name, strlen(dir->d_name));
textFiles[i][strlen(dir->d_name)] = 0;

Also the exit value of a program is a unsigned value, it goes from 0 to 255. In main you should return 1 instead of -1 on error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM