Wisnu.wdn
Wisnu.wdn

Reputation: 41

Can you pass a string by value in C?

Strings are interpreted as pointers when you pass them to a function in C, for example func(str) will pass a pointer to the block of memory named str. So by default, strings are already pointers when passing them into a function. Is there a way to pass a string by value where you don't have to end up manipulating the contents of str ?

Upvotes: 3

Views: 1191

Answers (3)

Darth-CodeX
Darth-CodeX

Reputation: 2447

Other answer are storing the string on the stack memory, which cannot be resized after initialization, which I don't think is a long runner. According to me string should be stored on heap memory so that we can expand it later, think something of a data structure that expands when something is appended at the end.

To implement this nicely and with some sugar syntax, we need to create 3 functions namely init_string(), append_string() and free_string().

  • init_string(): initialize the pointer and assign len = INIT_STR_LEN
  • append_string(): can be used for concatenation of two strings
  • free_string(): free the heap allocated ptr, hence no memory leaks

Code:

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

typedef struct
{
    char *ptr;
    size_t len;
} string;

string init_string(const char *s)
{
    string x;
    x.len = strlen(s);
    x.ptr = malloc(x.len + 1);
    if (!x.ptr)
    {
        fprintf(stderr, "err: memory can't be allocated\n");
        exit(EXIT_FAILURE);
    }
    memcpy(x.ptr, s, x.len + 1);
    return x;
}

void append_string(string *s, const char *str)
{
    if (!str || !s)
    {
        fprintf(stderr, "err: null was passed\n");
        exit(EXIT_FAILURE);
    }
    size_t str_l = strlen(str);
    s->ptr = realloc(s->ptr, s->len + str_l + 1);
    if (!s->ptr)
    {
        fprintf(stderr, "err: memory can't be allocated\n");
        exit(EXIT_FAILURE);
    }
    memcpy(s->ptr + s->len, str, str_l + 1);
    s->len += str_l;
}

void free_string(string *s)
{
    if (!s)
    {
        fprintf(stderr, "err: null was passed\n");
        exit(EXIT_FAILURE);
    }
    free(s->ptr);
    s->len = 0;
}

void foo(string a) // got the value of a
{
    // cannot modified permanently
}

void bar(string *a) // got the address of a
{
    // can be modified permanently
}

int main(void)
{
    string str = init_string("Hello world, I'm a c-string.");
    puts(str.ptr);
    append_string(&str, "stackoverflow");
    puts(str.ptr);
    free_string(&str);
    return EXIT_SUCCESS;
}

Upvotes: 0

First, what is a string? It's an array of characters and then a \0. Just to make sure we all agree on that.

So the question is, can you pass an array of characters by value? And the answer is yes, but only if it's inside a struct or union:

struct String40Chars {
    char str[41];
};

void f(struct String40Chars s) {
    // s was passed by value - including s.str
}

However, you can't pass an array directly due to a historical quirk:

void f(char str[41]) {
    // you would think this would work,
    // but actually, the compiler converts it to void f(char *str)
}

That is unfortunate for people who really do want to pass arrays by value.

Luckily you usually don't need to. In every case I can think of, a pointer is good enough, and faster as well since it doesn't need to copy the array. Maybe you can elaborate on why you need to pass an array by value.

P.S. You talk about "strings being pointers" but that is nonsense. Strings are arrays. You can have pointers to arrays but the pointer is not the array.

Upvotes: 7

wohlstad
wohlstad

Reputation: 28594

The only way to achieve value semantics with string arguments in C is to have a struct or union that wraps the string.

Something like:

#define STR_BY_VALUE_MAX_LENGTH 128
typedef struct StrByValue_
{
    char str[STR_BY_VALUE_MAX_LENGTH];
} StrByValue;

void f(StrByValue strByVal)
{
    // Changes to strByVal will not affect the caller
}

Of course the caller will have to initialize the StrByValue object by copying the string (e.g. using strcpy) into the str field. You can also add a len field to the StrByValue struct if you don't want to use zero terminated strings.

Upvotes: 4

Related Questions