LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

C strings and returning character arrays

Solved!
Go to solution

This is not strictly a CVI question, but it's vexing me today in CVI, so I thought I'd bring it here.  I've reached the end of what I understand about C "strings" (or better: character arrays).

 

I have a function that formats some input data into a pretty string to then be used somewhere.  It relies on a mutable string. 

 

 

 

const char* ExampleStringifier(double value, int precision)
{
	char outStr[100] = {0};

	sprintf(outStr, "%2.*f", precision, value);

	return outStr;
}

 

 

 

I'm clearly initializing it incorrectly in the function, as I just found a bug where its value on the stack is being replaced when called multiple times by the same calling function.  Examples:

 

 

 

	DebugPrintf("value = %s \n",
					ExampleStringifier(50.0, 14));
	
	DebugPrintf("value = %s \n",
					ExampleStringifier(-9.45460, 3));
	
	DebugPrintf("value = %s, another value = %s \n",
					ExampleStringifier(3.14159265359, 8),
					ExampleStringifier(1.41421356237, 3));
	

 

 

 

This yields the following:

 

value = 50.00000000000000

value = -9.455

value = 1.414, another value = 1.414

 

You can see how when I call the function twice in the same caller, the return gets replaced by the most recent call.

 

As a test of a CVI stringifier function, I tried double calls to TimeStr:

 

 

	DebugPrintf("time = %s, another time = %s \n",
					TimeStr(),
					TimeStr());

 

 

 

Interestingly, if I break the program after the first call, wait several seconds and continue, what I get back is the same time.  So maybe CVI has the same bug?

 

time = 12:56:32, another time = 12:56:32

 

 

 

0 Kudos
Message 1 of 3
(9,992 Views)
Solution
Accepted by topic author ElectroLund

One note on this:

const char* ExampleStringifier(double value, int precision)
{
	char outStr[100] = {0};

	sprintf(outStr, "%2.*f", precision, value);

	return outStr;
}

outStr is no longer valid after the return statement because it lifetime ends there. The 100 bytes of memory thus can be reused for other variables of other functions. So after some runtime this memory will contain other data which quite likely will not be a string.

 

You could declare outStr as static which in turn makes the function not longer re-entrant and does not protect against the other issue:

 

This

DebugPrintf("value = %s, another value = %s \n",
					ExampleStringifier(3.14159265359, 8),
					ExampleStringifier(1.41421356237, 3));

has the issue, that both calls of ExampleStringifier are executed before the "value = %s, another value = %s \n" is built, and since both calls (might) use the same memory for their outStr location the second call overwrites the first.

 

Same happens with the string returned from TimeStr().

 

Side note: the CVI help for gmtime() says this:

Note  Per the C89 specification, this function and the localtime function return a pointer to the same static struct tm structure. Using these functions in conjunction with each other might result in overwriting the previous value of the UTCTime parameter.

So you see, it's not a "C string" issue but the problem of returning a pointer to a local variable (static or not)

 

So, my possible solutions:

1) return dynamic allocated memory which the caller has to free()

2) manage an static array of buffers which elements you rotate through with each call

3) require an external buffer and its size to be supplied to the function, it can return a pointer to it

 

-----------------------
/* Nothing past this point should fail if the code is working as intended */
Message 2 of 3
(9,959 Views)

Using your first suggestion, dynamically allocated memory, I can rewrite the function to:

 

const char* ExampleStringifier(double value, int precision)
{
	char* localStr = (char *) malloc(20);

	sprintf(localStr, "%2.*f", precision, value);

	return localStr;
}

 

Then my caller produces:

 

value = 50.00000000000000
value = -9.455
value = 3.14159265, another value = 1.414

 

So then the only issue is that of freeing.  The convenience and point of calling this function inline with other functions is that there's no need to declare a holder character array first. 

 

DebugPrintf("value = %s, another value = %s \n",
					ExampleStringifier(3.14159265359, 8),
					ExampleStringifier(1.41421356237, 3));

 

In so doing, there's also not a direct way of freeing at the caller level (again, by design for convenience). 

 

I guess I can't have my cake and eat it too. 😞

 

 

0 Kudos
Message 3 of 3
(9,943 Views)