Why Do I Lose Data In a Char Pointer When Passed Into or Over a Function?

Started by
22 comments, last by ajm113 9 years, 8 months ago

`doLogic' should also take an argument that is the length of `dstBuffer', so it can make sure to not write past the end of the buffer. Notice how `sprint_s' already does that.

I was looking at the problem in isolation (thinking the compiler would know the value of sizeof). But the OP mentioned a library - so you are 100% correct.

Something like this would be ideal:


bool errorVal = doLogic(srcBuffer, sizeof(srcBuffer), dstBuffer, sizeof(dstBuffer));
Advertisement

char* awesome_ini::aw_subStr(const char* line, size_t start, size_t length)
{
char subbuff[AWI_MAX_KEY_NAME_LENGTH];

memset(subbuff, 0, AWI_MAX_KEY_NAME_LENGTH);

size_t p = 0;
for (size_t i = start; i < length+1; i++)
{
subbuff[p] = line[i];
p++;
}

return (char*)subbuff;
}

char subbuff is stack allocated memory, you NEVER return pointers to stack memory, this is because exactly what you are seeing is happening, instead you pass in a buffer that the function should write to(and you should pass in the size of that buffer).


The reason why you don't see this with objects is because when you return an object, you are copying the value from the function, so if i do:

struct Foo{
int x;
};

Foo Func(void){
Foo p;
p.x = 10;
return p;
}

int main(int argc, char **argv){
Foo f = Func();
printf("%d\n", f.x);
return 0;
}
what's happening is that when p is returned from func, the values of p are copied into the values of f, so your copying the values off the stack into another part of the stack that is still valid in the program context(note: this is a very simiplied explanation).

however if we do:
struct Foo{
int x;
};

Foo *Func(void){
Foo p[2];
p[0].x = 10;
p[1].x = 12;
return p;
}

int main(int argc, char **argv){
Foo *f = Func();
printf("%d\n", f[0].x);
return 0;
}
this is undefined behavior, and is very likely to crash the program, this is because you are returning a pointer into stack allocated memory that became invalid the moment you returned from Func. in many cases using the data immediately after would technically still work, but it is undefined and very bad to rely on such behavior.

one of the correct solutions is to do like so:
struct Foo{
int x;
};

bool Func(Foo *p, int psize){
if(psize<2) return false; //failed
p[0].x = 10;
p[1].x = 12;
return true; //succeded!
}

int main(int argc, char **argv){
Foo f[2];
Func(f, 2); // we can pass stack allocated memory into functions, we just should never return such memory!
printf("%d\n", f[0].x);
return 0;
}
edit:

I didn't think I would need a working example since I thought putting comments of the output would be enough.

always try to provide the minimalist working example to demonstrate your problem, you'll get more concise answers that way, then us trying to understand what the hell your doing.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.

`doLogic' should also take an argument that is the length of `dstBuffer', so it can make sure to not write past the end of the buffer. Notice how `sprint_s' already does that.

I was looking at the problem in isolation (thinking the compiler would know the value of sizeof). But the OP mentioned a library - so you are 100% correct.

Something like this would be ideal:


bool errorVal = doLogic(srcBuffer, sizeof(srcBuffer), dstBuffer, sizeof(dstBuffer));

No. Something like that is not ideal.

Use of sizeof in that manner will, in the end, introduce subtle bugs, as you will depend on a behavior that does not exist outside of that function. Specifically, arrays decay to pointers. Just pass in the damn size of the string, and track with proper variables.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

In the context of my original post, it would be fine:


char srcBuffer[MAX_LENGTH];
sprintf_s(srcBuffer, MAX_LENGTH, "Hello World!");

char dstBuffer[MAX_LENGTH];

bool errorVal = doLogic(srcBuffer, sizeof(srcBuffer), dstBuffer, sizeof(dstBuffer)); // return false if dstBuffer is too short etc

But point taken.

Aside from just explaining that returning stack-allocated member is bad mkay, these slides will help you have a better understanding of why it's not supported and why you can see the behavior you get without just getting an outright crash or compiler error:

http://www.slideshare.net/olvemaudal/deep-c

Sean Middleditch – Game Systems Engineer – Join my team!


In the context of my original post, it would be fine

I think you're missing the point of Washu's post. Using sizeof() when you have MAX_LENGTH staring you in the face is just lazy. sizeof() is resolved at compile-time, not run-time. IMHO, using sizeof with a variable as the argument should be avoided whenvever possible, as it inevitably leads to:


char *dstBuffer = new char[MAX_LENGTH];
some_function( ..., sizeof( dstBuffer ) ...); // where MAX_LENGTH is intended, but sizeof(char*) is what it compiles to

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

We're all drifting off topic here, but what about:


char srcBuffer[] = {"hello"};
char dstBuffer[] = {"World"};

bool errorVal = doLogic(srcBuffer, ?, dstBuffer, ?);

In which case you need to use either sizeof or strlen.

Please note I fully agree about the case of


char *dstBuffer = new char[MAX_LENGTH];

But the OP specifically asked about "C", meaning [] or malloc.

We're all drifting off topic here, but what about:

char srcBuffer[] = {"hello"};
char dstBuffer[] = {"World"};

bool errorVal = doLogic(srcBuffer, ?, dstBuffer, ?);
In which case you need to use either sizeof or strlen.

Please note I fully agree about the case of
char *dstBuffer = new char[MAX_LENGTH];

But the OP specifically asked about "C", meaning [] or malloc.


char srcBuffer[] = {"hello"};
size_t srcLen = sizeof(srcBuffer);
char dstBuffer[] = {"World"};
size_t dstLen = sizeof(dstBuffer);

bool errorVal = doLogic(srcBuffer, srcLen, dstBuffer, dstLen);
Oh hey, look, now when I refactor out that doLogic call to another function it doesn't magically start seeing 4 or 8 or whatever the pointer size is randomly. All because I didn't use sizeof in an easly breakable place!

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

"Oh hey, look, now when I refactor out that doLogic call to another function it doesn't magically start seeing 4 or 8 or whatever the pointer size is randomly. All because I didn't use sizeof in an easly breakable place!"

Wow, a lot of assumptions! I suggest you read the OP. The takeaways are really simple: "library" + "C". Not c++.

This topic is closed to new replies.

Advertisement