0

I thought I understood the answer to this question but I don't. I understand the first result but I still don't know how to do the copy correctly. I tried the following code:

// TstStrArr.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <string.h>
#include <malloc.h>


int main()
{
    char ** StrPtrArr;
    char    InpBuf0[] = "TstFld0";
    char    InpBuf1[] = "TstFld1";

    StrPtrArr = (char **)malloc(2 * sizeof(char *));

    StrPtrArr[0] = (char *)malloc(10 + 1);
    printf("inpbuf=%s   sizeof=%2d   ", InpBuf0, sizeof(StrPtrArr[0]));
    strncpy_s(StrPtrArr[0], sizeof(StrPtrArr[0]), InpBuf0, _TRUNCATE);
    printf("strptrarr=%s\n", StrPtrArr[0]);

    StrPtrArr[1] = (char *)malloc(10 + 1);
    printf("inpbuf=%s   sizeof=%2d   ", InpBuf1, sizeof(*StrPtrArr[1]));
    strncpy_s(*StrPtrArr[1], sizeof(*StrPtrArr[1]), InpBuf1, _TRUNCATE);    //  error here
    printf("*strptrarr=%s\n", StrPtrArr[1]);

    free(StrPtrArr[0]);
    free(StrPtrArr[1]);
    free(StrPtrArr);


    return 0;
}

The result I got was:

inpbuf=TstFld0   sizeof= 4   strptrarr=Tst
inpbuf=TstFld1   sizeof= 1   

and the following error:

Exception thrown: write access violation.
destination_it was 0xFFFFFFCD.

The result I thought I'd get was either of the following:

inpbuf=TstFld1   sizeof=11   *strptrarr=TstFld1
inpbuf=TstFld1   sizeof= 1   *strptrarr=T

I understand the first copy copied the input buffer to the 4 byte pointer which was incorrect. I thought the second copy would copy the input buffer to the value of the dereferenced pointer of a size of 11 but it didn't. I'm guessing the copy was to the first character of the string in the array. I don't understand memory enough to know the significance of the address 0xFFFFFFCD but I guess it's in read-only memory thus causing the error.

What is the correct way to do the copy?

(I don't think it matters, but I'm using VS 2015 Community Edition Update 3.)

Community
  • 1
  • 1
J. Toran
  • 157
  • 2
  • 2
  • 14
  • sizeof will not give you string length. – koper89 Dec 13 '16 at 12:02
  • Thank you. That's true. I believe the second parm of MS version of `strncpy` needs to be the `sizeof` the receiver variable. – J. Toran Dec 13 '16 at 12:06
  • What is `_TRUNCATE`? It does not look like you use `strncpy_s` correctly. See the [specification](http://port70.net/~nsz/c/c11/n1570.html#K.3.7.1.4) – too honest for this site Dec 13 '16 at 12:24
  • Thank you, Olaf. I checked the specification and `_truncate` is used when the number of characters to copy is greater than the `sizeof` the receiver variable. When I specified the `strlen(of the input buffer)` I got a "buffer too small" error. – J. Toran Dec 13 '16 at 12:29

3 Answers3

1

Why

  strncpy_s(*StrPtrArr[1], sizeof(*StrPtrArr[1]), InpBuf1, _TRUNCATE);  

?

*StrPtrArr[1] should be StrPtrArr[1] because StrPtrArr is of type char** and you need char* here.

and sizeof(*StrPtrArr[1]) - is quite strange.... actually sizeof(StrPtrArr[1]) also cannot provide correct value.

You should remember size of allocated memory and then use it like:

 size_t arrSize = 10 + 1;
 StrPtrArr[1] = (char *)malloc(arrSize);
 . . .
 strncpy_s(StrPtrArr[1], arrSize, InpBuf1, _TRUNCATE);  
VolAnd
  • 6,367
  • 3
  • 25
  • 43
  • Thank you. I tried the change, but got the same error on the copy. I was hoping the sizeof(*StrPtrArr) would be 11. – J. Toran Dec 13 '16 at 12:03
  • Try strlen instead of sizeof. – koper89 Dec 13 '16 at 12:04
  • @koper89 Good suggestion, but it is better to use `strlen(StrPtrArr[1]) + 1` to copy `'\0'` also – VolAnd Dec 13 '16 at 12:05
  • I tried the following code with the hard-coded length of 11: `strncpy_s(*StrPtrArr[1], 11, InpBuf1, _TRUNCATE);`. I hate to say but I still get the error on that line, too. – J. Toran Dec 13 '16 at 12:10
  • Command should be `strncpy_s(StrPtrArr[1], arrSize, InpBuf1, _TRUNCATE); ` : `*StrPtrArr[1]` -> `StrPtrArr[1]` (no * needed) – VolAnd Dec 13 '16 at 12:26
  • This is the correct answer. Thank you so much @VolAnd! – J. Toran Dec 13 '16 at 12:34
1

The problem is that you are using sizeof when deciding how many characters to copy. However, you allocated a fixed number of characters which is not known to sizeof operator: sizeof StrPtrArr[0] is equal to the size of char pointer on your system (four bytes, judging from the output), not 10 + 1. Hence, you need to specify that same number again in the call to secure string copy.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Thank you. Is this what you mean: `strncpy_s(*StrPtrArr[1], 8, InpBuf1, _TRUNCATE);`? I still got the same error as above. – J. Toran Dec 13 '16 at 12:13
  • @J.Toran 8 should be 11, and there is no asterisk in front of StrPtrArr[1]. – Sergey Kalinichenko Dec 13 '16 at 12:29
  • Thank you @dasblinkenlight. I believe this is the same correction that VolAnd showed above. Unfortunately I could only specifiy one correct answer. – J. Toran Dec 13 '16 at 12:36
1

It isn't as complicated as people seem to think.

char* array = calloc( n, sizeof(array[0]) ); // allocate array of pointers

// assign a dynamically allocated pointer:
size_t size = strlen(str) + 1;
array[i] = malloc(size);
memcpy(array[i], str, size);

I intentionally used calloc during allocation, since that sets all pointers to NULL. This gives the advantage that you can harmlessly call free() on the pointer, even before it is assigned to point at a string.

This in turn means that you can easily (re)assign a new string to an index at any time, in the following way:

void str_assign (char** dst, const char* src)
{
  size_t size = strlen(src) + 1;
  free(*dst);
  *dst = malloc(size);
  if(*dst != NULL)
  {
    memcpy(*dst, src, size);
  }
}

...
str_assign(&array[i], "something");
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thank you @Lundin. This is also a correct answer. I'm still somewhat of a beginner and hadn't used `memcpy` before, but I will keep it in mind. – J. Toran Dec 13 '16 at 12:40
  • @J.Toran `memcpy` is a general copy function for any kind of memory. It does the same thing as `strcpy_s`, although `strcpy_s` also checks if the source string has reached the end. Since this code already knew how long the source string was by the call to `strlen`, I could use `memcpy` instead of `strcpy_s` - `memcpy` is ever so slightly faster. – Lundin Dec 13 '16 at 12:51
  • if 'n' is known, say 1024, would this be equivalent to your calloc? `char* array[1024] = {NULL};` – nmz787 May 19 '17 at 17:15