1

The following code below runs without a seg fault

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

struct node {
    char *data;
    struct node *next;
};

int main(void)
{   
    struct node *head = malloc(sizeof(struct node));
    
    head->data = "test";
    printf("data: %s\n", head->data);

    return 0;
}

when I switch the code to so

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

struct node {
    char *data;
    struct node *next;
};

int main(void)
{   
    struct node *head = malloc(sizeof(struct node));
    
    strncpy(head->data, "test", 512);
    printf("data: %s\n", head->data);

    return 0;
}

I receive a seg fault and am forced to switch my node property data to be of type char data[512]. Why is this required? I thought arrays are inherently pointers, so this behavior is not making sense to me.

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

struct node {
    char data[512];
    struct node *next;
};

int main(void)
{   
    struct node *head = malloc(sizeof(struct node));
    
    strncpy(head->data, "test", 512);
    printf("data: %s\n", head->data);

    return 0;
}

I expected that both pointers and arrays could be assigned string values in the same way.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 3
    `I thought arrays are inherently pointers` They are not. – tkausl Jan 23 '23 at 20:06
  • You haven't allocated memory for the string in version 2 – Ted Lyngmo Jan 23 '23 at 20:07
  • Use `strdup` rather than `strncpy` – stark Jan 23 '23 at 20:08
  • `strncpy(head->data, "test", 512);` Why `512`? There isn't any memory allocation. – Weather Vane Jan 23 '23 at 20:11
  • Think about the difference between `p = &v;` vs `*p = v;` – user253751 Jan 23 '23 at 20:24
  • *I thought arrays are inherently pointers* No. Pointers are **variables** that **hold an address** - and the address in a pointer may or may not be valid. Arrays are **chunks of memory** that **have an address**. Your home has an address, and if someone puts that address on a letter, your post office will deliver it to you. If someone puts "North Pole" as the address, Santa isn't going to get it... – Andrew Henle Jan 23 '23 at 22:19

4 Answers4

3

In this statement

head->data = "test";

the string literal having the array type char[5] is implicitly converted to pointer to its first element and this pointer is assigned to the pointer head->data.

In this statement

strncpy(head->data, "test", 512);

you are using an uninitialized pointer head->data and trying to copy the whole string literal to the memory pointed to by that pointer with an indeterminate value. That invokes undefined behavior.

I thought arrays are inherently pointers, so this behavior is not making sense to me.

Arrays are not pointers. They can be implicitly converted to pointers to their first elements in most situations but this does not mean that arrays are pointers.

Consider the simple demonstration program below.

#include <stdio.h>

int main( void )
{
    char data[512];
    char *p = data;

    printf( "sizeof( data ) = %zu\n", sizeof( data ) );
    printf( "sizeof( p ) = %zu\n", sizeof( p ) );
}

Its output might look like

sizeof( data ) = 512
sizeof( p ) = 8
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

In the second snippet (the one that crashes), you allocate memory for the node struct, which includes the pointer data. However, this pointer is never initialized, and it points to some arbitrary memory address (or just NULL), meaning that writing to it is undefined behavior, and indeed likely to just segfault.

For it to point to a valid memory address, you'll have to explicitly allocate it:

struct node *head = malloc(sizeof(struct node));
head->data = malloc(sizeof(char) * 512 /* or some other size, of course */);
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • Thank you, does this method of initialization provide any benefits rather than char data[512] in the struct? I suppose dynamic size possible on the struct? – c questions Jan 23 '23 at 20:13
  • @cquestions indeed, you can choose a more informed size, and even have different sizes for different nodes. – Mureinik Jan 23 '23 at 20:19
0

Why is this required? I thought arrays are inherently pointers, so this behavior is not making sense to me.

Pointers are just pointers and the only memory they occupy is the memory required to store an address. If you want a pointer to point at dynamically allocated memory, you need to allocate it yourself.

Example:

struct node {
    char *data;
    struct node *next;
};

struct node *create_node(const char *str) {
    struct node *nn = malloc(sizeof *nn);
    if(nn) {
        nn->data = strdup(str); // allocate strlen(str)+1 bytes and copy the string
        if(nn->data) {          // strdup was successful
            nn->next = NULL;
        } else {                // strdup failed
            free(nn);
            nn = NULL;
        }
    }
    return nn;
}

void destroy_node(struct node *n) {
    free(n->data);
    free(n);
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

I thought arrays are inherently pointers

Array Vs pointer:

An array might decay to a pointer in expressions and function parameters, and array accesses might be rewritten by the compiler as pointer accesses, but they are not interchangeable. An array name is an address, and a pointer is the address of an address.


Why is this required?

Declaring a pointer only allocates memory for the pointer.

You can initialise a pointer in two ways:

  • Either with a string literal:

    ptr = "abcd";
    

    Originally, ptr was indeterminate. But this changes what it was pointing to such that it now points to a string literal. The compiler will store the address of the first element of the string literal in ptr.

    If you were to do the same with an
    array:

    char s[] = "String";
    

    and then try to change its value:

    s = "String2"; 
    
    /* error: assignment to expression 
    with array type */
    
    

    It wouldn't compile, because arrays, unlike pointers, are not modifiable lvalues. You can't change its value.

  • Or allocate memory for it and then
    write to it.

    errno = 0;
    
    ptr = malloc (size);
    if (!ptr) {
        errno = ENOMEM;
        perror ("malloc ()");
        deal with error here...
    }
    

    Now you can use strcpy () to copy to it.

Harith
  • 4,663
  • 1
  • 5
  • 20