returning a pointer

Feb 23, 2011 at 2:17am
Hey guys I am having trouble returning a pointer within a function. I am creating a program that breaks down a string into tokens. For example if I would have a string "Hey guys what" it would break it down to

ptr[0] = "Hey"
ptr[1] = "guys"
ptr[2] = "what"

and in order for my program to work i have to return 0 (so i pretty much loose all data is what im guessing). So when i try to return the original pointer, i get an error in compiliation. Any ideas?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
////////////////////////////////////////////////////////////////

//argtok partions a char string and breaks them up into tokens//

////////////////////////////////////////////////////////////////




char  ** argtok(char *str0){

     

     int x;

     //count the number of characters

     x = 1;

    

      while (*str0 != '\0'){

      str0++;

      x++;

                            }

     

     //reset pointer

     str0 = str0 - x + 1;

     

     //allocate the appropiate memory

     char *ptr;

     ptr = (char *)malloc(x*1);

     

     if (ptr==NULL){

        printf("Out of memory sorry!");

        return 0;

        }

     //copy cotents into new allocated memory

     int i = 0;

      for(; *str0 != '\0'; str0++, ptr++)

       *ptr = *str0; 

         

         *ptr = '\0';

         

     //reset pointer    

     ptr = ptr - x + 1;

         

     //replace white space with null character and count number of tokens with i

     for(; *ptr != '\0'; ptr++){

           if (*ptr == ' '){

            *ptr = '\0';

             i++;              

                           }

           }

     //reset pointer 

     ptr = ptr - x + 1;            

                        

       

      

    //create array of pointers and assign them to appropiate token

     int a, j = 0;

     char *ptr1[i];

     char *ptr2;

     ptr2 = ptr1[i];

      ptr1[0] = ptr;

      a=0;

        while(a != x){

          if (*ptr == '\0'){

              j++   ;

              ptr1[j] = ptr+1; 

                            }

              a++;

              ptr++; 

                      }

     

      

    //print the results and free each pointer

     printf("\nHere are your token(s)!\n\n");   

       int k; 

         for (k = 0; k<=i; ++k){

        

         printf("%s\n", ptr1[k]);

         }

    //reset pointer 

    // ptr = ptr - x ; 

    //free pointer                         

        // return  ptr;             

            

        

        // return 0; 

       

     }
Last edited on Feb 23, 2011 at 3:31am
Feb 23, 2011 at 3:09am
Please format that code, it's impossible to read without formatting and with all those blank lines. You can edit your post and use the code format tag.

You're doing something really dangerous, you're using the string pointer passed in as a variable, str0, changing it and resetting it, hoping that you can deduce what value it was before being changed.

You've allocated a string ptr that gets the same adjusted treatment. From then I loose touch with what you're doing. I lost interest as it can't be right.

Returning a pointer is easy, you declare the function to return a pointer of the right type and simply return one of the right type.

In your case, you allocate an array from the heap and return that, but it's really the wrong type and probably filled in incorrectly. The array hold pointers to strings, so the array type must be char**.
Feb 23, 2011 at 3:39am
Fixed, Thanks for the fast reply, should I allocate a string instead of a pointer for the code below?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
     //allocate the appropiate memory

     char *ptr;

     ptr = (char *)malloc(x*1);

     

     if (ptr==NULL){

        printf("Out of memory sorry!");

        return 0;

        }

Feb 23, 2011 at 3:49am
Also how can I verify that I am returning a pointer of the same type?
Feb 23, 2011 at 8:38am
how can I verify that I am returning a pointer of the same type?
The compiler does that for you.

The syntax is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char** parse(char* str)
{
    char** str;
    size_t n = 4;  /* some number of elements */
    str = (char**)calloc(n + 1, sizeof(char*));
    if (str)
    {
        for (size_t i = 0; i < n; ++i)
        {
            /* assign some value to str[i] */
        }
    }

    return str;
}
Feb 24, 2011 at 2:31am
Thank you so much... I will work on this guy tomorrow!
Feb 26, 2011 at 6:11pm
I had another question, if I have a function whose input argument is a string, can i modify that input arguement? So what I wanted to do was modify the input string, so every white space would have the null character "\0" and return it with a pointer to a pointer
Feb 26, 2011 at 9:38pm
When you modify the data pointed to by a char* argument, the changes will be seen by the caller. That is, whatever you do to the string that is pointed by the char* argument is committed to the string supplied by the caller. For example:
1
2
3
4
5
6
7
8
9
10
#include <iostream>

void f(char* s) { s[0] = 'b'; }

int main()
{
  char s[] = "abc";
  f(s);
  std::cout << s << std::endl; //prints bbc
}


Regards
Feb 26, 2011 at 10:10pm
I must have been doing something wrong because I could not edit the input argument, thanks zimeonz. Here is my final code. Should I make changes to my code to allocate memory over the input argument instead of copying it to another memory location? Also since this is a pointer to an array of pointers, how do I extract the parsed strings in my main code? I only get the first parsed string. I added a loop in the function to see if it created the array of pointers which it does.

Here is my test bench
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main(){
    
     char line[100];  //test bench is for 100 character string (argtok can take any length)
     char *ptr, **d_ptr;
     
     //read input from user and drop a pointer to the beginning of the string
     ptr = line;
     read_term(line);

     //run argtok to partition strings 
     d_ptr = argtok(ptr);
     
     
     printf("%s", *d_ptr);
     
     getchar();
     return 0;
    
    
    
    
}



Here is my function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
char  ** argtok(char *str0){

     int x=1;

     //count the number of characters
      while (*str0 != '\0'){
            str0++;
            x++;
                            }
     //reset pointer
     str0 = str0 - x + 1;

     //allocate the appropiate memory
     char *ptr, **d_ptr;
     ptr = (char *)malloc(x*1);
     d_ptr = &ptr;
     
       if (ptr==NULL){

         printf("Out of memory sorry!");
         return 0;
                   }

     //copy cotents into new allocated memory
      int i = 0;
      for(; *str0 != '\0'; str0++, ptr++)
             *ptr = *str0; 

             *ptr = '\0';
     //reset pointer    
     ptr = ptr - x + 1;

     //replace white space with null character and count number of tokens with i
     for(; *ptr != '\0'; ptr++){

           if (*ptr == ' '){

            *ptr = '\0';
             i++;              

                           }

                               }

     //reset pointer
     ptr = ptr - x + 1;            

    //create array of pointers and assign them to appropiate token
     int a, j = 0;
     char *ptr1[i];
          ptr1[0] = ptr;
          a=0;

            while(a != x){
                if (*ptr == '\0'){
                j++   ;
                ptr1[j] = ptr+1; 

                                 }
                a++;
                ptr++; 
                         }

    //print results
     printf("\nHere are your token(s)!\n\n");   
         int k; 
         for (k = 0; k<=i; ++k){
            printf("%s\n", ptr1[k]);
                               }
    //reset pointer
     ptr = ptr - x ; 
         return d_ptr; 

     }


And here is the function to read from terminal.

1
2
3
4
5
6
7
8
9
10
11
12
13
 void read_term (char buffer[])
{
     char read;
     int i = 0;
     
     do{
         read = getchar();
         buffer[i] = read;
         i++;
         }
         while (read != '\n');
         buffer[i-1] = '\0';
     }
Last edited on Feb 26, 2011 at 10:11pm
Feb 26, 2011 at 11:17pm
bionio wrote:
Should I make changes to my code to allocate memory over the input argument instead of copying it to another memory location?
I don't understand exactly. You can not reliably allocate new memory over some existing memory. (You can try with realloc, but you may not succeed.)

If you mean, should you overwrite your input, instead of allocating new buffer, you can pick any. It is like asking whether you should have + operation or += operation. Both approaches have certain uses and are broadly speaking equivalent. It is not that important in your case.

By the way, you separate words at spaces, but how do you handle consecutive spaces? Are you supposed to handle them?

Conceptual stuff aside, I think there is a leak in your code. You allocate the memory whose address you assign to ptr, but never free it. You also return pointer to ptr, but ptr is a local variable and its lifetime expires at the closing brace of strtok. So, you will return a dangling pointer. (It will probably work ok with a simple test. Those bugs manifest in complex situations.)

Indeed, it will be easier if you overwrite the input instead of copying it over to a new buffer. However, if you want to return some collection of things that you have created inside the routine, you can't do it so simply.

One way to solve this problem is to use vector instead of using arrays. They are not the same breed:
http://www.cplusplus.com/reference/stl/vector/

Another way to solve such leaks is to use auto_ptr:
http://www.cplusplus.com/reference/std/memory/auto_ptr/

And another way is to expect the caller to allocate all buffers that you need, suitably large, and pass them to your function. Those whose size you can not anticipate must be allocated with enough space in reserve to hold the maximum quantity possible. This is the C style approach.

Regards
Feb 27, 2011 at 1:50am
Thanks for the info! Ok all I have to worry about is a single white space. I also have to use array of pointers (class problem). This function will be used to build a simple shell in the future. It also has to be done in C not C++ (technically I am in the wrong forum lol). But I understand now that ptr will be gone once the function ends. Another question, since I have to return an array of pointers, Do I have first create the array and then allocate? Like this

1
2
3
4
5
6
char **ptr[5];
ptr = (char**)malloc(sizeof(char*));

//rest of code here//

free ptr;


So thanks again with the info

Last edited on Feb 27, 2011 at 1:55am
Feb 27, 2011 at 2:35am
Do I have first create the array and then allocate?
No, you don't :) With malloc you can do this:
1
2
3
4
5
6
char **ptr;
ptr = (char**)malloc(5 * sizeof(char*));

//rest of code here that modifies ptr[0] ... ptr[4]//

free(ptr);


Regards
Last edited on Feb 27, 2011 at 2:35am
Feb 27, 2011 at 7:01am
Ok I am almost there guys! I am stuck, I had to allocate another piece of memory so doubling what I actually want. I could not figure out how I could add contents into a double pointer (d_ptr) so I created another pointer (ptr) and had d_ptr point to ptr in order for this to work. Any ideas or is this the only way to do it? It works but with the price of doubling the memory. I also noticed I didnt check for null character for ptr...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <stdio.h>
#include <stdlib.h>



int main(){
     char  ** argtok(char *str0);
     void read_term (char buffer[]);
     
     char line[100];  //test bench is for 100 character string (argtok can take any length)
     char *ptr, **d_ptr;
     
     //read input from user and drop a pointer to the beginning of the string
     ptr = line;
     read_term(line);

     //run argtok to partition strings 
     d_ptr = argtok(ptr);
     // check array [1] of pointer
     printf("\n\n%s", d_ptr[1]);
    
     
     getchar();
     return 0;
    
    
    
    
}


//Function to parse string into token
char  ** argtok(char *str0){

     int x=1;

     //count the number of characters
      while (*str0 != '\0'){
            str0++;
            x++;
                            }
     //reset pointer
     str0 = str0 - x + 1;

     //allocate the appropiate memory
     char *ptr, **d_ptr; 
     d_ptr = (char **)malloc(x*sizeof(char*));
     ptr = (char*)malloc(x*sizeof(char));
     
       if (d_ptr==NULL){

         printf("Out of memory sorry!");
         return 0;
                   }

     //copy cotents into new allocated memory ptr    
      int i = 0;
      for(; *str0 != '\0'; str0++, ptr++)
             *ptr = *str0; 

             *ptr = '\0';
     //reset pointer    
     ptr = ptr - x + 1;

     //replace white space with null character and count number of tokens with i
     for(; *ptr != '\0'; ptr++){

           if (*ptr == ' '){

            *ptr = '\0';
             i++;              

                           }

                               }

     //reset pointer
     ptr = ptr - x + 1;           
    //create array of pointers and assign them to appropiate token
     int a, j = 0;
     
        
          a=0;
          d_ptr[0] = ptr;
            while(a != x){
                if (*ptr == '\0'){
                j++   ;
                d_ptr[j] = ptr+1;
                

                                 }
                                 
                a++;
                ptr++; 
                         }
                         
    //reset pointer
    ptr = ptr - x ;
    
    //print results
     printf("\nHere are your token(s)!\n\n");   
         int k; 
         for (k = 0; k<=i; ++k){
            printf("%s\n", d_ptr[k]);
                               }
         
         return d_ptr; 

     }
/////////////////////////////////////////////////////
//read_term reads the input from a user at terminal//
/////////////////////////////////////////////////////     
     void read_term (char buffer[])
{
     char read;
     int i = 0;
     
     do{
         read = getchar();
         buffer[i] = read;
         i++;
         }
         while (read != '\n');
         buffer[i-1] = '\0';
     }
Last edited on Feb 27, 2011 at 7:02am
Feb 27, 2011 at 9:28am
Suppose I have some string, and I want to create array of pointers that point to the a-s in the string. Here is an example of what you could do, just to illustrate the mechanics:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>
#include <cstdlib>
#include <cstring>

int main() {
  char* str = (char*)malloc(sizeof("caravan"));
  strcpy(str, "caravan");
  char** ptrs_to_a = (char**)malloc(3 * sizeof(char*));
  ptrs_to_a[0] = &str[1];
  ptrs_to_a[1] = &str[3];
  ptrs_to_a[2] = &str[5];
  printf("%s\n%s\n%s\n", ptrs_to_a[0], ptrs_to_a[1], ptrs_to_a[2]);
  //aravan
  //avan
  //an
  free(ptrs_to_a);
  free(str);
}
Feb 27, 2011 at 4:14pm
Oh ok well thats exactly what I did then. I created a *ptr that held the modified input argument and created the pointer to pointer **d_ptr to create the array.
Feb 27, 2011 at 6:50pm
This part seems ok. But there is all of the pointer arithmetic. I can't see anything wrong with it, but this stuff requires testing and multiple reviews. For efficiency, you can allocate the char* (d_ptr) array for (i+1), not x elements. But, more importantly, remember that the client has to free the memory:
1
2
free(d_ptr[0]); //free ptr == d_ptr[0]
free(d_ptr);


Regards
Mar 1, 2011 at 5:07am
Well it works but for some reason it doesnt want to go into the execvp command!!! Trying to build a simple shell :(
Mar 1, 2011 at 6:45am
Looking at execvp's specification here: http://linux.about.com/library/cmd/blcmdl3_execvp.htm
Did you make the last element of the argument array null pointer? Because it is required.

Regards
Mar 2, 2011 at 3:15am
No i didnt!!! Hmm I will try it after I am done with some other HW. But it was due last nite lol. What was weird was that if I would create a folder it would give an error in the terminal but it would actually create it lol
Topic archived. No new replies allowed.