简体   繁体   中英

strncpy vs direct assignment seg fault

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.

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

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 */);

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);
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM