Search code examples
cmemorystrcpy

Making a working copy of char array of unkown length


Using MPLABX 5.35 with XC16 v1.36 for a PIC24FJ128GB204. Limited c background, but learning.

I'm trying to write a function that takes a (pointer to a) string in a char array (of unknown size) and edits it in-place to right-justify it and pad it with characters until it has the requested length. As I would like to keep this function for other projects as well, the size of the source array will be unknown, but the user (me) should keep targetLength smaller than the size of the array.

So imagine the array:

char example[20] = "test";

I want to be able to pass it to the function with the desired length (say, 10 chars incl null termination) and the fill character '#' and it should edit the array in-place to "#####TEST"

The code I came up with (which only works for some occasions so isn't usable):

uint16_t fillStringRight(char * source, 
                    uint16_t targetLength, 
                    char filler)
{
    uint16_t sourceLength = strlen(source) + 1;

    if (sourceLength > targetLength){ 
        // source length is already longer than requested
        return 0;
    }

    uint16_t reqFiller = targetLength - sourceLength;

    strcpy(&source[reqFiller], source);
    memset(source, filler, reqFiller -1);
    return 1;
}

However, in this case:

char source[20] = "test";
fillStringRight(source, 6, ' ');

This does not work. If I understand correctly, the source and result overlap, so strcpy will overwrite the null-terminator and thus keep on writing further and further into the memory until the watchdog restarts the PIC.

First question: is my understanding correct?

I suppose I should make a copy of the source string instead of editing it in-place. However, I don't know the length of the string so I can't size the char array that will be used for the copy (unless I make it ridiculously large). I read about malloc to create a variable in the heap with a size that is determined at runtime, but also read against using malloc on a microcontroller. I do not understand why yet, so prefer to steer away from it until I understand better.

Second question: what would be a correct way to do this? I could write a function that works backwards (start with a null terminator at the last position, then work backwards), but that seems like a lot of overhead. Is there not a better way?


Solution

  • From man memmove(3):

    The memory areas may overlap: copying takes place as though the bytes in src are first copied into a temporary array that does not overlap src or dest, and the bytes are then copied from the temporary array to dest.

    In other words, there's a function for what you want :)
    Try memmove(&source[reqFiller], source, sourceLength); A pseudo code equivalent:

    char *tmp = duplicate_string(source);
    copy tmp to &source[reqFiller]; 
    

    Unrelated, but FYI:
    1) strlen() returns size_t, not uint16_t
    2) &a[b] == &*(a +b) == (a + b) so &source[reqFiller] == source + reqFiller