Reputation: 745
So this is strange- below is my code to create a tokenizer object and the main method:
struct TokenizerT_ {
char * sep;
char * toks;
};
TokenizerT *TKCreate(char *separators, char *ts) {
if (ts==NULL) {
return NULL;
}
int lin = (int) strlen(separators);
char yr[lin];
yr[0] = *separators;
int lim = 1;
int h = 1;
for(h=1; h<strlen(separators); h++){
char tmp = *(separators+h);
int z=0;
for (z=0; z<lim; z++) {
if (tmp==yr[z]) {
z=-1;
break;
}
}
if(z>-1){
yr[h] = tmp;
lim++;
}
}
TokenizerT inu = {yr, ts};
printf("%s\n", inu.sep);
return &inu;
}
int main(int argc, char **argv) {
char * arr = argv[1];
char * y = argv[2];
TokenizerT jer = *TKCreate(arr, y);
printf("%s\n", jer.sep);
printf("%s\n", jer.toks);
return 0;
}
If I run the program with arguments "tes" and "testing", I get the following results: tes tes testing
However, if I run the program with the printf statement in TKCreate commented out, I get:
testing.
The printf("%s\n", jer.sep) stops working and I've done nothing to the code except commenting out that printf statement in TKCreate. Why is that happening?
Upvotes: 0
Views: 110
Reputation: 2182
You need to allocate memory for an object that is to be returned from a function. This puts the object on the heap (permanent memory) vice on the stack (memory that is recycled by the program as soon as the current function ends).
You can do that by making these changes in your TKCreate function:
TokenizerT *TKCreate(char *separators, char *ts) {
...
# change 'char yr[lin];' to:
char* yr = (char*)malloc(lin * sizeof(char));
....
TokenizerT* inu = (TokenizerT*)malloc(sizeof(TokenizerT));
inu->sep = yr;
int->yoks = ts;
printf("%s\n", inu->sep);
return inu;
}
You can also clean up your main() function by only sharing one copy of the object. You could call free() on any allocate memory once you are done with it:
int main(int argc, char **argv) {
char * arr = argv[1];
char * y = argv[2];
TokenizerT jer* = TKCreate(arr, y);
printf("%s\n", jer->sep);
printf("%s\n", jer->toks);
free(jer);
return 0;
}
Upvotes: 0
Reputation: 613531
There are a number of problems here:
inu
is a local variable. You return the address of it. When you attempt to read from that address in main
, you are accessing a variable that is no longer in scope. That is undefined behaviour.sep
field of inu
is assigned yr
. Now, yr
is a local variable, a character array, which decays to a pointer. Again, accessing that once the function has returned means accessing a variable whose scope has ended. Again, undefined behaviour.I suggest the following changes:
malloc
.On top of that I suspect that you have other problems. It appears to me that yr
is not null-terminated, and some elements may not be initialized at all. Is that intentional? Perhaps you actually meant to initialise yr
to be equal to separators
when you wrote yr[0] = *separators
. Note that your code simply assigns a single character. To allocate yr
dynamically, and initialise it to be equal to separators
you can write:
yr := malloc(strlen(separators)+1);
strcpy(yr, separators);
Why do you call strlen(separators)
multiple times? You appear to call it 1+strlen(separators)
times. You should call it once only.
Upvotes: 3
Reputation: 54373
In C you can never return a pointer to a local function variable. Well, you can, but it won't work. Even if it seems to work, it is only waiting for a good opportunity to not work.
So you absolutely cannot do this:
TokenizerT inu = {yr, ts};
return &inu;
What you need to do is either pass in a pointer to the object you want to build, or use malloc
to allocate a new one. If you use malloc
you will need to remember to free
the object later. And because of Windows and its multiple C runtimes it is a very good idea to free
your objects inside a function just for that. Because if you were to have the code in a library, and malloc
in the Debug runtime and free
in the Release runtime, bad things happen.
So for the malloc example:
struct box* create_box(int x, int y) {
struct box* b = malloc(sizeof(*b));
if(!b) abort();
b->x = x;
b->y = y;
return b;
}
void destroy_box(struct box* x) {
free(x);
}
And for passing an object in:
void init_box(struct box* b, int x, int y) {
b->x = x;
b->y = y;
}
void f() {
struct box b;
init_box(&b, 10, 20);
}
Upvotes: 2