您好,我有一个关于将字符串传递给C语言中的函数的问题。我正在使用CS50库,我知道他们将字符串作为char数组(指向数组开头的char指针)进行传递,因此通过参考。我的函数正在接收数组作为参数,它返回数组。例如,当我更改函数中数组元素之一时,此更改将按我期望的那样反映到原始字符串。但是,如果我将新字符串分配给参数,则函数将返回另一个字符串,并且原始字符串不变。您能解释一下这种行为背后的机制吗?
#include <stdlib.h>
#include <cs50.h>
#include <stdio.h>
string test(string s);
int main(void)
{
string text = get_string("Text: ");
string new_text = test(text);
printf("newtext: %s\n %s\n", text, new_text);
printf("\n");
return 0;
}
string test(string s)
{
//s[0] = 'A';
s = "Bla";
return s;
}
第一个示例反映了文本字符串和newtext字符串上第一个字母的变化,但是第二个示例未更改地打印了文本,将newtext打印为“ Bla” 谢谢!
答案 0 :(得分:3)
这需要一段时间。
让我们从基础开始。在C语言中,字符串是包含0值终止符的字符值序列。 IOW,字符串 "hello"
表示为序列{'h', 'e', 'l', 'l', 'o', 0}
。字符串存储在char
的数组中(对于宽字符串,则存储在wchar_t
的数组中,在此不再赘述)。其中包括像"Bla"
这样的字符串文字-它们存储在char
数组中,以便在程序的整个生命周期内都可用。
在大多数情况下,类型为T
的N元素数组的表达式将被转换(“衰变”)为类型为“指向{{1}的指针”的表达式。 }”,因此在大多数情况下,当我们处理字符串时,实际上是在处理T
类型的表达式。但是,这并不意味着char *
类型的表达式是一个字符串-char *
可以指向字符串的第一个字符,也可以指向第一个字符。不是字符串(无终止符)的序列中的字符,或者它可能指向不属于较大序列的单个字符。
char *
也可能指向由char *
,malloc
或calloc
分配的动态分配的缓冲区的开始。
要注意的另一件事是realloc
下标运算符是根据指针算术定义的-表达式[]
的定义为a[i]
-给定地址值*(a + i)
(从如上所述的数组类型转换),从该地址偏移a
个元素(不是字节)并取消引用结果。
要注意的另一重要事项是i
未被定义为将一个数组的内容复制到另一个数组。实际上,数组表达式 不能成为=
运算符的目标。
CS50 =
类型实际上是类型string
的{{1}}(别名)。 typedef
函数在幕后执行了许多魔术操作,以动态分配和管理字符串内容的内存,并使C语言中的字符串处理看起来比实际情况高得多。我和其他一些人认为,这至少在字符串方面是一种不好的C语言教学方法。不要误会我的意思,它是一个非常有用的实用程序,只是一旦您没有cs50.h并必须开始进行自己的字符串处理,您就可以在海了一段时间。
那么,所有的废话与您的代码有什么关系?具体来说,
char *
正在发生的事情是,不是将字符串文字get_string()
的内容复制到s = "Bla";
指向的内存中,而是将字符串文字的 address 写入其中"Bla"
,覆盖先前的指针值。您不能使用s
运算符将一个字符串的 contents 复制到另一字符串;相反,您必须使用s
之类的库函数:
=
strcpy
正常工作的原因是因为下标运算符strcpy( s, "Bla" );
是根据指针算术定义的。表达式s[0] = A
的计算结果为[]
-给定地址a[i]
(如上所述,指针或“已衰减”到指针的数组表达式),偏移量{{1 }}元素(不是字节!),然后取消引用结果。因此*(a + i)
指向您读入的字符串的第一个元素。
答案 1 :(得分:2)
没有代码示例,这很难正确回答。我会做一个,但可能与您的工作不符。
让我们使用这个C函数:
char* edit_string(char *s) {
if(s) {
size_t len = strlen(s);
if(len > 4) {
s[4] = 'X';
}
}
return s;
}
该函数将接受 pointer 到 character数组,并且如果指针不是NULL且以零结尾的数组长于4个字符,它将替换索引4的第五个字符,带有“ X”。 C中没有引用。它们始终称为 pointers 。它们是同一回事,您可以使用 dereference 运算符*p
或类似p[0]
的数组语法访问指向的值。
现在,此功能:
char* edit_string(char *s) {
if(s) {
size_t len = strlen(s);
if(len > 4) {
char *new_s = malloc(len+1);
strcpy(new_s, s);
new_s[4] = 'X';
return new_s;
}
}
s = malloc(1);
s[0] = '\0';
return s;
}
该函数将指针返回到新分配的原始字符数组副本或新分配的空字符串。 (这样做,调用者可以始终将其打印出来并在结果上调用free
。)
它不会更改原始字符数组,因为new_s
没有指向原始字符数组。
现在您也可以这样做:
const char* edit_string(char *s) {
if(s) {
size_t len = strlen(s);
if(len > 4) {
return "string was longer than 4";
}
}
s = "string was not longer than 4";
return s;
}
请注意,我将返回类型更改为const char*
,因为像"string was longer than 4"
这样的字符串文字是常量。尝试对其进行修改将使程序崩溃。
在函数中对s
进行赋值不会更改 s 指向的字符数组。指针s
指向或引用原始字符数组,然后在s = "string"
之后指向字符数组"string"
。