Question

  • Compiler: GCC 4.7.2 (Debian 4.7.2-5)
  • Platform: Linux 3.2.0 x86 (Debian Wheezy)

Edit: See answer bellow code has been updated and has been tested. It runs correctly again kudos to WhozCraig.

I am writing a simple header file that contains functions that manipulate wide strings dynamically. For example my header includes a copy function that expands the destination buffer if need be. The current function that I am writing copies only a segment of a provided buffer into a provided destination buffer. Originally I used a destination buffer that was more than large enough for my test case and the function had no problems at all. But I wanted to test my realloc() logic so I set the destination buffer's size to 1 so that the function would have to realloc() the destination buffer. But it looks like to me realloc() is changing the values of the contents of the destination buffer. I have tracked the error down to the third realloc() call. Here is the function and the test case that I have been using.

#include <wchar.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>

#define DWS_DEF_SIZ 16

#define DWS_ENOMEM -1
#define DWS_EINVAL -2

int scpydws(wchar_t* *des, size_t *siz, int argc, wchar_t *src, size_t num, ...)
{   
    size_t i = 0;
    size_t j = 0;
    va_list argv;
    wchar_t *tmp = NULL;
    size_t x = 0;

    if(des == NULL) return DWS_EINVAL;
    if(siz == NULL) return DWS_EINVAL;
    if(*siz == 0 && *des != NULL) return DWS_EINVAL;

    if(*des == NULL)
    {
        if(*siz == 0)
        {
            if((*des = malloc(DWS_DEF_SIZ * sizeof(wchar_t))) == NULL) return DWS_ENOMEM;

            *siz = DWS_DEF_SIZ;
        }
        else if((*des = malloc(*siz * sizeof(wchar_t)) == NULL) return DWS_ENOMEM;
    }

    for(va_start(argv, num); argc > 0; argc--, src = va_arg(argv, wchar_t*), num = va_arg(argv, size_t))
    {
        if(src == NULL || src[0] == 0) continue;

        for(j = 0; j < num; j++, i++)
        {
            for(x = 0; x < i; x++) putwchar((*des)[x]);
            wprintf(L" | %i == %i\n", i, *siz - 1);

            if(i == *siz - 1)
            {
                if((tmp = realloc(*des, *siz * 2 * sizeof(wchar_t))) == NULL)
                {
                    va_end(argv);
                    return DWS_ENOMEM;
                }

                *siz *= 2;
                *des = tmp;
            }

            (*des)[i] = src[j];
        }
    }

    (*des)[i] = 0;
    va_end(argv);
    return 0;
}

int main()
{
    int ret = 0;
    size_t siz = 1;
    wchar_t *des = NULL;
    wchar_t src1[] = L"123456789";
    wchar_t src2[] = L"abcdefghijklmnopqrstuvwxyz";
    wchar_t src3[] = L"blahblah";

    //The syntax is fairly straight forward the numbers following the
    //buffers represent how many characters to copy into the destination buffer
    ret = scpydws(&des, &siz, 3, src1, 2, src2, 3, src3, wcslen(src3));
    wprintf(L"ret = %i\n", ret);

    return 0;
}

Here is the output from the test case. Everything works fine until the third realloc() the characters before "|" represent the destination buffer during the functions operation. What follows the "|" represents a comparison between the function's index variable for the destination buffer (integer on the left of the "==") and the destination buffer's last valid index (integer on the right of the "==") if the statement is true than the function would realloc() the destination buffer. So my question is basically where the hell does the 'Y' come from (see bellow)? Also as I side note I compiled the code on a Windows Vista SP3 system with MinGW-GCC (I do not know what MinGW-GCC version) and instead of the random 'Y' character I got a ENOMEM error from realloc(), ironically on the third realloc() call but I do not really care why that happens.

 | 0 == 0
1 | 1 == 1
12 | 2 == 3
12a | 3 == 3
12ab | 4 == 7
12abc | 5 == 7
12abcb | 6 == 7
12abcbl | 7 == 7 Note: The function realloc()s here
12abcYla | 8 == 15 Note: Notice how the 'b' changes to a 'Y'?
12abcYlah | 9 == 15
12abcYlahb | 10 == 15
12abcYlahbl | 11 == 15
12abcYlahbla | 12 == 15
ret = 0
Was it helpful?

Solution

The size arguments for malloc() and realloc() need to be multiplied by sizeof(wchar_t).

I changed this:

*des = malloc(DWS_DEF_SIZ))

to this:

*des = malloc(DWS_DEF_SIZ * sizeof(wchar_t)))

and this:

tmp = realloc(*des, *siz * 2)

to this:

tmp = realloc(*des, *siz * 2 * sizeof(wchar_t))

Thanks to WhozCraig.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top