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, 7 months ago

Hello, I'm writing a library using low level C++ methods so my library can easily be built in C. So I'm staying away from C++ standard templates. So vectors and strings are a bit limit. (Plus I think it's fun to have a bit of a challenge. ;) ). I've mostly been using strings in C++ for ease of use, and I decided to stick with char pointers, but I appear to run into odd problems, of which I cant explain.. I'll give a example.


//Issue One

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

const char* bufferP = doLogic(buffer);

//bufferP = "Goodbye World!" after doLogic


//Some random function in my class that has nothing to do with bufferP at all.
doMoreMath();

//Wut? Now bufferP equals random garbage data?
//bufferP = "/////////tx///"

I know this problem can be fixed doing this:


const char* bufferP = doLogin();

char buffer_a[MAX_NAME_LENGTH];
memset(buffer_a, 0, MAX_NAME_LENGTH);
strncpy(buffer_a, bufferP, strlen(bufferP));

But why does this happen? This issue never seems to happen on any other pointers I've created, like objects. Is it necessary I do everything in char arrays? So I don't lose data randomly?

Thank you, Andrew.

Check out my open source code projects/libraries! My Homepage You may learn something.
Advertisement
it's hard to tell since you didn't give an actual working example, but my guess is your returning stack memory from doLogic, and when you call doMoreMath that stack memory is being overwritten, because you never should return stack allocated memory.
Check out https://www.facebook.com/LiquidGames for some great games made by me on the Playstation Mobile market.

What do your doLogic and doMoreMath do? (too many 'do's!)

In particular, what exactly are you returning from doLogic? If you're creating a buffer with local scope in doLogic and returning a pointer to it, you're only ever going to get nonsense back.

ph34r.png

Ah, that would make sense. So I need to make the memory heap then so I can use the variable globally. I didn't think I would need a working example since I thought putting comments of the output would be enough. I really need to get back to the basics again, but at lower level.

@Mark

DoLogic is anything that returns char*. I.E

Example of a substr function I made:


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;
}

That also causes the pointer in the example code to go away after a few lines of code. And do math is simply a function that does anything really...

I.E


void doMath(void)
{
//Do some long calculation
int x = 5 - 2;
printf("%i\n", x);
}
Check out my open source code projects/libraries! My Homepage You may learn something.

return (char*)subbuff;


Crank up the warning level of your compiler until it complains about that line. Returning a pointer to something in the stack is almost universally an error, and your compiler should be able to tell you about it.

If you are going to do without std::string, I suggest you reimplement it, as something like this:
struct String {
  char *data;
  unsigned size;
  unsigned reserved;
};
Then write functions to perform all the operations you need on them.

A better way would be


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

char dstBuffer[MAX_LENGTH]

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

A better way would be


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

char dstBuffer[MAX_LENGTH]

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

That is certainly the "C" way of doing things and it sounds like this is what the OP's goal is.

Yes, a very common C idiom is to avoid allocation but instead to pass a buffer to a pre-allocated (possibly uninitialised) buffer / structure of sufficient size. In some cases, a library will have a function for determining the size of a buffer that needs to be passed when this would not be known.

A better way would be


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

char dstBuffer[MAX_LENGTH]

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


`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.

A better way would be


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

char dstBuffer[MAX_LENGTH]

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

`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.

To be even safer, it should take the length of srcBuffer as well

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.

This topic is closed to new replies.

Advertisement