问题
My firmware in one cycle per 10 second is moving string to the output (csv file on SD card). The problem is that string sometimes in non-deterministic way is changing adding values that shouldn't be there or put space in the middle. Is this related to sprintf function or dynamic allocated memory for that string?
void archPolling()
{
double archCountVal[200];
float archDataVal[100];
char *FilStringMeas = malloc(sizeof(char) * 2048);
char *FilArchive = malloc(sizeof(char) * 4096);
vArchEvent eventArch = STATE_POLLING;
unsigned char CiphCRC[5];
FIL FilData;
UINT bw;
int queueSize = 0;
if ( xSemaphoreTake( MutexMeasurment, 200 ) == pdTRUE)
{
eventArch = STATE_COLLECT;
}
if (eventArch == STATE_COLLECT)
{
while (uxQueueMessagesWaiting(xDataQueue) > 0)
{
xQueueReceive(xDataQueue, &archDataVal[queueSize], 0);
queueSize++;
}
xSemaphoreGive(MutexMeasurment);
if (queueSize > 0 && timerFlag == 1)
eventArch = STATE_FORM;
else
eventArch = STATE_POLLING;
}
if (eventArch == STATE_FORM)
{
//portENTER_CRITICAL();
HAL_RTC_GetTime(&RtcHandle, &RTCTimeArch, FORMAT_BIN);
HAL_RTC_GetDate(&RtcHandle, &RTCDateArch, FORMAT_BIN);
sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
sprintf(FilStringMeas, ",");
for (int i = 0; i < queueSize; i++)
{
sprintf(FilStringMeas, "%s%f,", FilStringMeas, archDataVal[i]);
}
strcat(FilArchive, FilStringMeas);
archCRC((BYTE *) FilArchive, strlen(FilArchive), CiphCRC);
strcat(FilArchive, (char *) CiphCRC);
strcat(FilArchive, "\n");
//portEXIT_CRITICAL();
eventArch = STATE_SYNC;
}
if (eventArch == STATE_SYNC)
{
f_open(&FilData, "0:55AD001.csv", FA_OPEN_EXISTING | FA_WRITE);
f_lseek(&FilData, f_size(&FilData));
f_write(&FilData, FilArchive, strlen(FilArchive) * sizeof(char), &bw);
f_close(&FilData);
timerFlag = 0;
eventArch = STATE_POLLING;
}
free(FilStringMeas);
free(FilArchive);
}
Edit: example of wrong output
0,0,1,3.512586,42.960911,,46.487427,24.501009,1.512586,27.498940,40.960911,36.598400,11.039062,9.401555,25.498940,42.487427,20.501009,7.512586,17.401556,36.960911,32.598400,7.039061,5.512586,31.498940,48.487427,16.501009,5.039061,13.401555,29.498940,46.487427,24.501009,1.512586,27.498940,44.487427,36.598400,11.039062,9.401555,38.960911,42.487427,20.501009,7.512586,33.498940,36.960911,32.598400,7.039061,15.401555,31.498940,48.487427,16.501009,3.512586,13.401555,42.960911,38.598400,3.039061,1.512586,27.498940,44.487427,22.501009,11.09062,AAAA
0,0,1,34.471630,6.303817,15,15.528328,45.382984,32.471630,6.617044,4.303817,29.472881,47.696175,16.527170,4.617044,11.528328,41.382984,38.471630,24.527170,0.303817,25.472881,43.696175,36.471630,10.617044,17.528328,37.382984,41.696175,20.527170,8.617044,15.528328,45.382984,32.471630,6.617044,13.528328,29.472881,47.696175,16.527170,2.303817,11.528328,41.382984,38.471630,12.617044,0.303817,25.472881,43.696175,22.527170,10.617044,17.528328,37.382984,34.471630,20.527170,6.303817,31.472881,39.696175,32.471630,6.617044,13.528328,43.382984,496175,AAAA
回答1:
I think the issue Jack Whitman pointed out is the cause for your spurious output: You shouldn't pass the string to print to as an argument to sprintf
. Another potential risk is a buffer overflow, which sprintf
and strcat
don't guard against. (Well, can't guard against, because they don't know how large a buffer is.)
You build a single string and then append it to a file. One solution to your problem would be not to create the intermediary string, but to append the partial formatted strings to the file directly.
Another solution is to keep track of the number of characters written. This information is returned by all variantes of printf
, unless an error occurred, which is signalled by -1
. Appending to the string then works more or less like:
size_t n = 0;
n += sprintf(str + n, ...);
n += sprintf(str + n, ...);
If you use snprintf
instead of sprintf
, you could also guard against buffer overflow.
That is a bit cumbersome. The problem of the s*printf
functions is that they always fill the string from the start and overwrites the data on subsequent calls to the same output buffer, which is a bit unintuitive, given that fprintf
appnds to to file and doesn't overwrite anything written to the same output file earlier.
If you have more cases where you want to build a string from succesive calls to formatted printing routines, you could write a small framework. The example below creates an "appender" from a fixed-sized char
buffer and successively fills it. The result may be truncated to prevent overflow, but will always yield a null-terminated string unless rem
is 0:
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h> // needed for va_list
struct appender {
char *str; // original buffer
size_t rem; // remaining space
size_t n; // (potential) characters written
};
int appprintf(struct appender *app, const char *fmt, ...)
{
va_list args;
char *p = app->rem ? app->str + app->n : NULL;
int n;
va_start(args, fmt);
n = vsnprintf(p, app->rem, fmt, args);
app->rem = (n < app->rem) ? app->rem - n : 0;
app->n += n;
va_end(args);
return n;
}
int main()
{
char buffer[64];
struct appender app = { buffer, sizeof(buffer) };
int i;
for (i = 0; i < 100; i++) {
appprintf(&app, " %d", i);
}
puts(buffer);
return 0;
}
回答2:
I can see you use sprintf(dest, "%s...", dest, ...);
to concatenate your strings. I've just read again manpage for sprintf, and if it is not explicitely forbidden, it not explicitely allowed.
As far as I am concerned, I would never do that. Even if it works, you ask printf
machinery to explicitely copy a (longer and longer) string to itself.
Instead of :
sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
sprintf(FilStringMeas, ",");
for (int i = 0; i < queueSize; i++)
{
sprintf(FilStringMeas, "%s%f,", FilStringMeas, archDataVal[i]);
}
strcat(FilArchive, FilStringMeas);
I would do
sprintf(FilArchive, "%02d-%02d-%02d,%02d:%02d:%02d,1", RTCDateArch.Date, RTCDateArch.Month, RTCDateArch.Year, RTCTimeArch.Hours, RTCTimeArch.Minutes, RTCTimeArch.Seconds);
strcat(FilArchive, ",");
for (int i = 0; i < queueSize; i++)
{
sprintf(FilStringMeas, %f,", archDataVal[i]);
strcat(FilArchive, FilStringMeas);
}
and I would test for buffer size.
来源:https://stackoverflow.com/questions/29966116/most-carefull-way-to-concatenate-strings-in-c