Most carefull way to concatenate strings in C

半腔热情 提交于 2019-12-11 12:44:16

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!