1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

conflict of types between an array of char and char**

Discussion in 'C' started by michal, Aug 14, 2015.

  1. michal

    michal New Member

    Joined:
    Sep 12, 2014
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    Hello to every one, I need help, I want to make a dynamic array of char* returned by a function. I need to save the name of files contained inside a folder to a dynamic array of char*.
    My IDE is dev-c++, my favorite, my language is C.
    I had made this:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include<io.h>
    
    unsigned short int FileCount(char *);
    char ** testFunc(); //the fuction that return the dynamic array of char*
    
    int main(int argc, char *argv[])
    {
      int i;
      int cantFiles = FileCount("*.*");  
      //char** ptrArray1 = testFunc("*.*",cantFiles);
      char** ptrArray1 = (char**)malloc(sizeof(char)*cantFiles);
      ptrArray1 = testFunc("*.*",cantFiles);
      //strcpy(ptrArray1,testFunc("*.*"));
    //for(i=0;i<3;i++)
    {
      //printf("%s\n",ptrArray1[i]); // <----- conflict types
    }
      system("PAUSE");	
      return 0;
    }
    //----------------------------------------------------------------------
    unsigned short int FileCount(char *ruta) // ruta is path in spanish
    {
    	int done,p =0,cantFiles =0;
        struct _finddata_t a;
        intptr_t myHandle = _findfirst(ruta,&a);
        while(done != -1)
        {
        	p = strlen(a.name);
            if(p>2) 
    	 cantFiles++; //cant of files inside folder to know the size of mi dynamic char* array   	
            
            done = _findnext(myHandle,&a);
        }
        _findclose(done);
        _findclose(myHandle);
        return cantFiles;
    }
    //-----------------------------------------------------------------------
    char ** testFunc(char *ruta,int cantFiles)  
    {
     int done=0,p =0,i=0;
    
     //cantFiles = FileCount("*.*");
    char** ptrArray = (char**)malloc(sizeof(char)*cantFiles);
       
        struct _finddata_t a;
        intptr_t myHandle = _findfirst(ruta,&a);
        while(done != -1)
        {
        	p = strlen(a.name);
            if((p>2){ // to eliminate the dots ". and .. in the list of file names"
             ptrArray[i] = a.name;   // <----- conflict types
              //strcpy(ptrArray[i],a.name);  // <----- conflict types
               //printf("%s\n",a.name);  // <----- this works
               i++;     	
               }
            done = _findnext(myHandle,&a);
        }
        _findclose(done);
        _findclose(myHandle);
        return ptrArray;
    }
    
    thanks to every one
     
    Last edited by a moderator: Aug 15, 2015
  2. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,012
    Likes Received:
    203
    Trophy Points:
    0
    Occupation:
    Senior Support Engineer
    Location:
    England
    A char** is not quite the same as an array of char*, although it's the same level of indirection. What you need to do first is allocate an array of char*, the size of which will be determined by cantFiles. Then allocate the appropriate amount of memory for each of those array entries (they could all be the same size, or they could be different), thus making this an array of pointers to individual character arrays. Then copy each string into each of those character arrays.

    Your first allocation would therefore be this instead:
    Code:
    char** ptrArray1 = (char**)malloc(sizeof(char*)*cantFiles); // one extra *
    
    ptrArray1 would then be an array of char*, which at this stage are all undefined.

    To avoid memory leaks you need to think carefully about each malloc, where it is going, how you are going to keep track of it, and where you are going to free it. A useful method (not just while you're learning) is to initialise all pointers to zero, then whenever you write into a pointer, make sure it is zero, and if it isn't then you know there's something in there. When you free a pointer, immediately set it back to zero to maintain this functionality. Your code does leak memory at the moment; see if you can find it!

    And for my last hint, to eliminate . and .., just do a string compare for each of them. If you have a test file named "a" then comparing the length with 2 will rule this one out as well.
     
    shabbir likes this.
  3. michal

    michal New Member

    Joined:
    Sep 12, 2014
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    Thanks to xpi0t0s for his answer but the code still not working, may be for my bad english, i can't understand the solution for my code, that him told me.

    But in C++, YEAH, it's work. This is the code for every one, that need to do the same thing, I mean, to count the number of files in a directory, and then, show them, all this with a dynamic array of strings.
    Thanks to every one.

    But I be stilling question my self, why with malloc and with a simple asignation of arrays, the code don't work???

    My code in C++, and with wx-DevC++
    Code:
    #include <cstdlib>
    #include <iostream>
    #include <io.h>
    #include <string.h>
    
    using namespace std;
    
    unsigned short int FileCount(string);
    string *testFunc(string path,int cantFiles); //the fuction that return the dynamic array of strings
    
    int main(int argc, char *argv[])
    {   
        unsigned short int i,cantFiles = FileCount("F:\\*.*");
        cout<<"Number of files: " <<cantFiles<< endl<<endl;
        
        string *ptrArray2 = new string [cantFiles];
        ptrArray2 = testFunc("F:\\*.*",cantFiles); //all ok
        
        cout<<"Files inside of root folder:"<<endl<<endl;
        for(i=0;i<cantFiles;i++)
         cout<<ptrArray2[i]<<endl; // <----- all ok
        
        cout<<endl<<endl;
        delete ptrArray2;
        cout << "Press the enter key to continue ...";
        cin.get();
        return EXIT_SUCCESS;
    }
    //------------------------------------------------------------------------------
    unsigned short int FileCount(string path) 
    {
    	int done,p =0,cantFiles =0;
        struct _finddata_t a;
        intptr_t myHandle = _findfirst(path.c_str(),&a);
        while(done != -1)
        {
        	p = strlen(a.name);
            if((p>2)&&(a.attrib ==_A_ARCH)) // only archives
    	 cantFiles++; //number of files inside folder to know the size of mi dynamic string* array   	
            
            done = _findnext(myHandle,&a);
        }
        _findclose(done);
        _findclose(myHandle);
        return cantFiles;
    }
    //------------------------------------------------------------------------------
    string *testFunc(string path,int cantFiles)
    { int done=0,p =0,i=0;
      string *ptrArray = new string [cantFiles];
      struct _finddata_t a;
      intptr_t myHandle = _findfirst(path.c_str(),&a);
      while(done != -1)
        {
         p = strlen(a.name);
         if((p>2)&&(a.attrib == _A_ARCH)){// only archives
                  ptrArray[i] = a.name;
                  i++; 
                 }
         done = _findnext(myHandle,&a);        
        }
        _findclose(done);
        _findclose(myHandle);
        return ptrArray;
      delete ptrArray; // clear memory
    }
     
    Last edited by a moderator: Sep 8, 2015
  4. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,012
    Likes Received:
    203
    Trophy Points:
    0
    Occupation:
    Senior Support Engineer
    Location:
    England
    The new code works because you've changed from char* to string, where the semantics are different.

    In your original code you were allocating one char array then setting the ptrArray1 to point to it. But you were then trying to use it as an array of arrays. Do you see the difference? It's fine to use a single array as a collection of char buffers but you really have to know what you are doing (you have to compute the offsets yourself instead of having the compiler do it automatically for you).

    But if your new code works and you are happy with it, I'd say stick with it. Strings are easier to use than char arrays.
     
  5. michal

    michal New Member

    Joined:
    Sep 12, 2014
    Messages:
    11
    Likes Received:
    0
    Trophy Points:
    0
    The new code works, that's right, but, I'm trying to understand, why the other one don't. I'm thinking that i'm do the same thing. The new code "with strings" is similar to the old one "with char*".

    I'm believe that a char* is a pointer to char but is a string too an it can be processed as such.

    Normally the function must return an array of char*, and for me, that is an strings array. The problem happens when I try to assign the obtained array from function, to another similar array in main(). Even I tried to do it with a static array, like this:

    #include <stdio.h>

    char **myfunc();

    int main()
    { int i;
    char *string_new_array[4];

    string_new_array = myfunc(); //<-- this don't work
    strcpy(string_new_array,myfunc()); //<--- this don't work

    for(i=0;i<4;i++)
    printf("%s\n",string_new_array);



    }
    //---------------
    char **myfunc(){//I want to print de string "Hellow Word" four times
    int i;
    char mystring[13]="Hellow World";//<---static array
    char *string_array[4];

    for(i=0;i<4;i++)
    string_array = mystring; //<-- that is correct???

    return string_array; // [Warning] function returns address of local variable [enabled by default]. Why the compiler say that???
    }
     
  6. xpi0t0s

    xpi0t0s Mentor

    Joined:
    Aug 6, 2004
    Messages:
    3,012
    Likes Received:
    203
    Trophy Points:
    0
    Occupation:
    Senior Support Engineer
    Location:
    England
    I've already said why it doesn't work. It's because you are allocating ***ONE*** (1, uno, eine, un) array of char instead of an ARRAY OF ARRAYS OF CHAR. Moreover, that array is only "cantFiles" characters long.

    Look at the code:
    Code:
    char** ptrArray1 = (char**)malloc(sizeof(char)*cantFiles);
    
    Suppose there are 10 files in the directory. Then cantFiles will be 10, and the above will be equivalent to:
    Code:
    char ptrArray1[10];
    
    although it won't be exactly equivalent, because actually you've defined ptrArray1 as a char**, not a char*. The important bit is the [10], i.e. 10 characters, so you have enough space for "this56789" and no more.

    What you need to do instead is to allocate an array of cantFiles char* pointers, i.e., as I showed you:
    Code:
    char **ptrArray1=(char**)malloc(sizeof(char*)*cantFiles); // note the extra *
    
    then as a second step you have to allocate the space for those pointers:
    Code:
    for (i=0; i<cantFiles; i++)
      ptrArray1[i]=malloc( --- the number of bytes for each individual filename you want to allocate -- );
    
    because when you allocate space for a pointer, all you get is that pointer and you DO NOT GET THE SPACE ALLOCATED FOR WHAT THAT POINTER IS POINTING AT. You have to do that as a separate allocation.

    The reason this works with a string array is that the secondary allocation WHICH YOU MUST DO MANUALLY FOR CHAR* is effectively done automatically, which is why you don't need to write the code for it.


    >>return string_array; // [Warning] function returns address of local variable [enabled by default]. Why the compiler say that???

    The reason the compiler says that is because you are returning invalid data to the calling function. It's very risky doing that, because the next function could potentially overwrite the data you're relying on. Best to pass string_array into the function instead of trying to allocate it within the code.

    >>that is correct???

    I would say no, not because this line of code is actually wrong, but because of the overall wrong thing you are doing, i.e. creating a variable in a function and returning the pointer to that variable to the caller. That is not something you should ever do, because the stack is invalid after your function returns (it is fine until you call something else, like printf for example, so only if you really know what you're doing you can do that, but given your overall confusion with strings, leave advanced stuff like that till later.
     
  7. lovesamrat

    lovesamrat New Member

    Joined:
    Sep 19, 2015
    Messages:
    3
    Likes Received:
    0
    Trophy Points:
    0
    Nice Information. Thank You for sharing with us the very useful and informative information.
     

Share This Page