2

I have this code in func I am getting pointer to array of pointers. The purpose of this function is to write strings to two allocated spaces for chars. Allocated in main. I am getting segFault at this line

memcpy(*c[1],"hi",sizeof("hi"));

this is full code

void func(char (**c)[])
{
  memcpy(*c[0],"hello",sizeof("hel"));
  memcpy(*c[1],"hi",sizeof("hi"));
}

int main()
{
  char (*arr)[2]=malloc(sizeof(char[2][10]));
  func(&arr);
  printf("%s\n",arr[0]);
  printf("%s\n", arr[1]);
  return 0;
}

I have allocated the array of pointers using

char (*arr)[2]=malloc(sizeof(char[2][10]));

now to pass the address of two allocated string spaces I am calling like this

func(&arr);

the point of passing the address of array of pointers variable arr so I can see the changes to string spaces in main(...) , CHANGES THAT MADE IN FUNC

but this line causing trouble

memcpy(*c[1],"hi",sizeof("hi"));

segFault

Can anyone please inform me what I am doing wrong this answer is also related to array of pointers. https://stackoverflow.com/a/69551741/4808760

Output I am expecting is

hell
hi

Valgrind dump

valgrind --leak-check=full -s ./a.out 
==7397== Memcheck, a memory error detector
==7397== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7397== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==7397== Command: ./a.out
==7397== 
==7397== Invalid write of size 2
==7397==    at 0x4849F23: memcpy@GLIBC_2.2.5 (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==7397==    by 0x1091F2: func (test.c:8)
==7397==    by 0x10922A: main (test.c:18)
==7397==  Address 0x54698dcdde89a700 is not stack'd, malloc'd or (recently) free'd
==7397== 
==7397== 
==7397== Process terminating with default action of signal 11 (SIGSEGV)
==7397==  General Protection Fault
==7397==    at 0x4849F23: memcpy@GLIBC_2.2.5 (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==7397==    by 0x1091F2: func (test.c:8)
==7397==    by 0x10922A: main (test.c:18)
==7397== 
==7397== HEAP SUMMARY:
==7397==     in use at exit: 30 bytes in 1 blocks
==7397==   total heap usage: 1 allocs, 0 frees, 30 bytes allocated
==7397== 
==7397== LEAK SUMMARY:
==7397==    definitely lost: 0 bytes in 0 blocks
==7397==    indirectly lost: 0 bytes in 0 blocks
==7397==      possibly lost: 0 bytes in 0 blocks
==7397==    still reachable: 30 bytes in 1 blocks
==7397==         suppressed: 0 bytes in 0 blocks
==7397== Reachable blocks (those to which a pointer was found) are not shown.
==7397== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==7397== 
==7397== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==7397== 
==7397== 1 errors in context 1 of 1:
==7397== Invalid write of size 2
==7397==    at 0x4849F23: memcpy@GLIBC_2.2.5 (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==7397==    by 0x1091F2: func (test.c:8)
==7397==    by 0x10922A: main (test.c:18)
==7397==  Address 0x54698dcdde89a700 is not stack'd, malloc'd or (recently) free'd
==7397== 
==7397== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
user786
  • 3,902
  • 4
  • 40
  • 72
  • You have at least this problem: with `memcpy(*c[0],"hello",sizeof("hel"))`, the ccopied string won't be null terminated. But anyway, what are you trying to achieve here? – Jabberwocky Oct 14 '21 at 07:03
  • @Jabberwocky `But anyway, what are you trying to achieve here?` I have allocated spaces that I like to pass to a function. All the processing will be done in the func. I know the sizes In main so I don't want to allocate spaces and do processing in main both.i want allocation in main and processing in func. If u got what I mean in the question. Then do u think this is overkill use of pointers or what? And it should have been the sizeof("hello") so I will correct it. But what may be the problem are u getting the question? – user786 Oct 14 '21 at 07:18
  • Please [edit] and show what output you expect. – Jabberwocky Oct 14 '21 at 07:37

4 Answers4

2

One way would be:

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

void func(char **c)
{
    c[0] = malloc(strlen("hel") + 1);
    c[1] = malloc(strlen("hi") + 1);
    strcpy(c[0], "hel");
    strcpy(c[1], "hi");
}

int main()
{
  char *arr[2];
  func(arr);
  printf("%s\n",arr[0]);
  printf("%s\n", arr[1]);
  return 0;
}

Here, arr is an array of two pointers to char. When we pass an array to a function, it decays to a pointer to the first element. Hence, func() accepts a pointer to a pointer to a char. c[0] and c[1] will hold the addresses returned by malloc() to those offsets inside c. Then, strcpy() just copies over the passed string to the allocated space. When this function returns, main() finds that data has already been placed inside arr[0] and arr[1].

Optionally, you could also call strdup() in func(). Also, do not forget to call free().

babon
  • 3,615
  • 2
  • 20
  • 20
  • What is arr. Is it array of pointers in ur answer – user786 Oct 14 '21 at 07:19
  • @user786 I have changed the local variable name inside `func()`. HTH. I have mentioned what `arr` is in my answer. – babon Oct 14 '21 at 07:26
  • strdup: `// A copy of source is created dynamically// and pointer to copy is returned.` does source and return of strdup has same address or different? – user786 Oct 14 '21 at 07:37
  • 1
    @user786 `x = strdup(y)` is the same as `x = malloc(strlen(y) + 1); strcpy(x, y);` – Jabberwocky Oct 14 '21 at 07:38
  • I accept your answer because it shows another way to create some array of variable sized elements. thanks for answer – user786 Oct 14 '21 at 07:39
  • 1
    And if you have `strdup()`, it allocates just like `mailloc()`, so you must **validate** its return the same as you would the return from `malloc()`. – David C. Rankin Oct 14 '21 at 07:55
  • @DavidC.Rankin can u please tell is `memcpy(c[1]+2,"k",1);` is valid with no problem if I do it in func in this answer – user786 Oct 14 '21 at 08:03
  • The problem with `memcpy(c[1]+2,"k",1);` is you will overwrite the `'\0'` character following `"hi"` leaving you with `"hik"` that is no longer a C-string (it's simply 3-stored characters). Recall, you only allocate 3-bytes with `c[1] = malloc(strlen("hi") + 1);`, so if you call `memcpy(c[1]+2,"k",1);` thereafter, you can no longer treat `c[1]` as holding a valid string. The `memcpy()` is equivalent to `c[1][2] = 'k';`. So there really is no need for a `memcpy()` of 1-byte -- ever. Also `strlen("hi") + 1` is the same as `sizeof "hi"`. – David C. Rankin Oct 14 '21 at 08:07
  • @DavidC.Rankin What if I allocated more spaces for c[1]? – user786 Oct 14 '21 at 08:48
  • `malloc(strlen("hel") + 1);` should be `malloc(sizeof("hel"));`. – Lundin Oct 14 '21 at 09:47
  • @Lundin yes, it could be so. – babon Oct 14 '21 at 10:57
  • @user786 - sure, that is the whole key to managing memory in C. Know what you need, and always ensure you account for every byte. If you don't know what you need DON'T SKIMP ON ALLOCATION SIZE. You can always come back and do a final `realloc()` to shrink the allocated size to exactly the number of bytes needed. Here allocate `c[1] = malloc(sizeof "hi" + 1);` (validate) and then `memcpy(c[1]+2,"k",sizeof "k");`. Understand why `sizeof "k"` returns the size of the array `{'k', '\0'}`. See [C11 Standard - 6.3.2.1(p3)](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3) (c17 removes `_Alignof`) – David C. Rankin Oct 14 '21 at 16:58
2

In this statement

char (*arr)[2]=malloc(sizeof(char[2][10]));

arr is a pointer to an array of 2 characters. Whereas, you are allocating it memory of 2 array of 10 characters. It should be

char (*arr)[10] = malloc(sizeof(char[2][10]));
           ^^^^

The type of &arr is char (**)[10], so the type of func() function parameter should be char (**)[10]

void func(char (**c)[10])
                    ^^^^

In func() function, you should access first and second array like (*c)[0] and (*c)[1] respectively.

Output I am expecting is

hell

You are giving count of characters, to copy, to memcpy() as sizeof("hel"). Do you want output hell or hel? I believe (or assume), you want first 3 characters of string literal "hello" to copy to (*c)[0].

Note that sizeof("hel") will give output 4 because it includes null terminator character as well. So, memcpy() will copy 4 characters from string literal "hello". If you want to copy first 3 character from string "hello" then you should pass the count as sizeof("hel") - 1.

Also, you should append the null terminator character after copying characters using memcpy() as memcpy() does not append terminating null character automatically. You can do something like this:

  memcpy((*c)[0],"hello",sizeof("hel") - 1);
  (*c)[0][sizeof("hel") - 1] = '\0';    // append null terminator character 

Last, follow good programming practice, always check the malloc() return and make sure to free() the dynamically allocated memory once you done with it.

Putting these altogether, you can do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(char (**c)[10]) {
    memcpy((*c)[0], "hello", sizeof("hel") - 1);
    (*c)[0][sizeof("hel") - 1] = '\0';
    memcpy((*c)[1], "hi", sizeof("hi") - 1);
    (*c)[1][sizeof("hi") - 1] = '\0';
}

int main(void) {
    char (*arr)[10] = malloc(sizeof(char[2][10]));
    if (arr == NULL) {
        fprintf (stderr, "Failed to allocate memory\n");
        exit(EXIT_FAILURE);
    }

    func(&arr);
    printf("%s\n", arr[0]);
    printf("%s\n", arr[1]);

    free(arr);
    return 0;
}

Output:

# ./a.out
hel
hi

the point of passing the address of array of pointers variable arr so I can see the changes to string spaces in main(...)

Since, you are allocating the memory to arr pointer in main() function before calling func() function, you don't need to pass the address of arr to reflect the changes to string space in main() function after returning from func() function. Simply pass the arr to func() function. When you pass arr, that means, you are passing the pointer to allocated memory to func() function, hence, whatever will be written to that memory in func() will reflect in main(). You also need to change the type of func() parameter and make it same as type of arr which is char (*)[10] and access the first and second array like c[0] and c[1] respectively. You can do

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(char (*c)[10]) {
    memcpy(c[0], "hello", sizeof("hel") - 1);
    c[0][sizeof("hel") - 1] = '\0';
    memcpy(c[1], "hi", sizeof("hi") - 1);
    c[1][sizeof("hi") - 1] = '\0';
}

int main(void) {
    char (*arr)[10] = malloc(sizeof(char[2][10]));
    if (arr == NULL) {
        fprintf (stderr, "Failed to allocate memory\n");
        exit(EXIT_FAILURE);
    }

    func(arr);
    printf("%s\n", arr[0]);
    printf("%s\n", arr[1]);

    free(arr);
    return 0;
}
H.S.
  • 11,654
  • 2
  • 15
  • 32
2

To address what's actually wrong with your code:

  • You don't include the relevant headers.
  • char (*arr)[2] is wrong. You want this to point at the first item of a char [2][10]. Thats an array of 2 items, where each item is an array of 10 characters. Therefore the pointer should be char (*arr)[10].
  • func(&arr); doesn't make sense, there's no reason to pass a pointer by reference unless you intend to change the pointer itself (like when doing malloc inside a function).
  • char (**c)[] is nonsense. This is a pointer to an array of pointers to incomplete array type. But you can't pass an incomplete array type as parameter to a function. This should be char str[2][10], which as parameter "decays" into the equivalent char (*str)[10].
  • You can't use memcpy on substrings, because it doesn't append nul termination. If you use memcpy, then you must manually add a \0 at the end. Note that malloc (unlike calloc) doesn't zero-init the allocated memory.
  • You forgot to call free(). Yes, the OS will deallocate the memory but you should still call free() explicitly, since that increases the chance of exposing heap corruption/pointer/memory leak bugs early on at the design stage.

Corrected code:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

void func(char str[2][10])
{
  strcpy(str[0],"hello");
  strcpy(str[1],"hi");
}

int main (void)
{
  char (*arr)[10]=malloc(sizeof(char[2][10]));
  func(arr);
  printf("%s\n",arr[0]);
  printf("%s\n", arr[1]);
  free(arr);
  return 0;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Can I use `void func(char **str)` instead of `void func(char str[2][10])` with ur main function? Can u please tell. thanks for ur answer revised so many things that I was kind of forgot – user786 Oct 14 '21 at 13:21
  • 1
    @user786 `char**` is used when you have an array of char pointers, each pointer pointer pointing at a string of individual length. If that is what you want, you should use `char**`. In case you are dealing with 2D arrays/fixed size strings, then `char [2][10]` is the correct form. Or perhaps more flexibly: `void func (size_t rows, size_t cols, char str[rows][cols])`. You _cannot_ use a `char**` to point at a 2D array of `char`. – Lundin Oct 14 '21 at 13:24
0

Thanks for the answer I got from babon.

I can also allocate the spaces in main or if I am in need to creating spaces in one line then this would be it char (*arr)[2]=malloc(sizeof(char[2][2])) but I did not get the answer how to pass and assign data to that in either main or called function in linked answer. just told me how to allocate it

void func(char **c)
{
  memcpy(c[0],"hello",sizeof("hel"));
  memcpy(c[1],"hi",sizeof("hi"));
//  memcpy(*c[2],"hell",sizeof("hell"));
//  c[0]="hello";
  //c[1]="hi";
}

int main()
{
  char *arr[2];
  arr[0]=malloc(sizeof(char[2][10]));
  arr[1]=malloc(sizeof(char[2][10]));

  func(arr);
  printf("%s\n",arr[0]);
  printf("%s\n", arr[1]);

  return 0;
}
user786
  • 3,902
  • 4
  • 40
  • 72