可以在C中按值传递字符串吗?

  • 本文关键字:字符串 按值传递 c
  • 更新时间 :
  • 英文 :


字符串在传递给C语言函数时被解释为指针,例如,func(str)将传递一个指向名为str的内存块的指针。所以默认情况下,字符串在传递给函数时已经是指针了。有没有一种方法可以按值传递字符串,而不必最终操纵str的内容?

首先,什么是字符串?它是一个字符数组,然后是一个。只是为了确保我们都同意。

问题是,你能传递一个字符数组的值吗?答案是肯定的,但只有当它在结构体或联合中:

struct String40Chars {
char str[41];
};
void f(struct String40Chars s) {
// s was passed by value - including s.str
}

但是,由于历史特性,你不能直接传递数组:

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

对于真正想要按值传递数组的人来说,这是不幸的。

幸运的是你通常不需要。在我能想到的每一种情况下,指针都足够好,而且速度更快,因为它不需要复制数组。也许你可以详细说明为什么需要按值传递数组。

注:你说字符串是指针但这是无稽之谈。字符串是数组。你可以有指向数组的指针,但指针不是数组。

在C语言中实现字符串参数值语义的唯一方法是使用一个结构体或联合来包装字符串。

类似:

#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
}

当然,调用者必须通过将字符串(例如使用strcpy)复制到str字段来初始化StrByValue对象。如果你不想使用零终止字符串,你也可以在StrByValue结构体中添加len字段。

其他答案是将字符串存储在堆栈内存中,初始化后不能调整大小,我认为这不是一个长期的跑步者。根据我的说法,字符串应该存储在堆内存中,以便我们以后可以扩展它,把它想象成一个数据结构,当在末尾添加一些内容时,它会扩展。

为了使用糖语法很好地实现这一点,我们需要创建3个函数,即init_string(),append_string()free_string()

  • init_string():初始化指针并赋值len = INIT_STR_LEN
  • append_string():可用于连接两个字符串
  • free_string():释放ptr分配的堆,因此没有内存泄漏

代码:

#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 allocatedn");
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 passedn");
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 allocatedn");
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 passedn");
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;
}

最新更新