在 CS50 库中使用字符串
Using a string in CS50 library
大家好,我有一个关于将字符串传递给 C 中的函数的问题。我正在使用 CS50 库,我知道他们将字符串作为 char 数组(指向数组开头的 char 指针)传递,所以传递是通过参考完成。我的函数正在接收数组作为参数,它是 returns 数组。例如,当我更改函数中的数组元素之一时,此更改会如我所料反映到原始字符串中。但是,如果我将新字符串分配给参数,函数 returns 另一个字符串和原始字符串不会改变。你能解释一下这种行为背后的机制吗?
#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;
}
第一个示例反映了 text 和 newtext 字符串的第一个字母的变化,但第二个示例打印出 text 不变,newtext 为 "Bla"
谢谢!
如果没有代码示例,这很难正确回答。我会做一个,但它可能与你正在做的不匹配。
让我们使用这个 C 函数:
char* edit_string(char *s) {
if(s) {
size_t len = strlen(s);
if(len > 4) {
s[4] = 'X';
}
}
return s;
}
该函数将接受指向字符数组的指针,并且如果指针不为空且以零结尾的数组更长超过 4 个字符,它将用 'X' 替换索引 4 处的第五个字符。 C中没有references。它们总是被称为pointers。它们是相同的东西,您可以使用 dereference 运算符 *p
或 p[0]
.[=23 等数组语法来访问指向的值=]
现在,这个函数:
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] = '[=11=]';
return s;
}
该函数 return 是一个 指向 原始字符数组的新分配副本或新分配的空字符串的指针。 (通过这样做,调用者总是可以打印出来并在结果上调用 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;
}
请注意,我将 return 类型更改为 const char*
,因为像 "string was longer than 4"
这样的字符串文字是常量。试图修改它会使程序崩溃。
在函数内对 s
进行赋值 不会更改 s 用来指向 的字符数组。指针 s
指向或引用原始字符数组,然后在 s = "string"
之后指向字符数组 "string"
.
这需要一段时间。
让我们从基础开始。在 C 语言中,string 是一个包含 0 值终止符的字符值序列。 IOW,string "hello"
表示为序列 {'h', 'e', 'l', 'l', 'o', 0}
。字符串存储在 char
的数组中(或 wchar_t
表示 "wide" 字符串,我们这里不讲)。这包括 字符串文字 ,例如 "Bla"
- 它们存储在 char
的数组中,因此它们在程序的整个生命周期内都可用。
在大多数情况下,类型 "N-element array of T
" 的 表达式 将被转换 ("decay") 为类型 "pointer to T
" 的表达式,因此大多数时候,当我们处理字符串时,我们实际上是在处理 char *
类型的表达式。但是,这并不意味着 char *
类型的表达式是 字符串 - char *
可能指向字符串的第一个字符,也可能指向指向序列中 不是 字符串(无终止符)的第一个字符,或者它可能指向不属于更大序列的单个字符。
A char *
也可能指向由 malloc
、calloc
或 动态分配的 缓冲区的开头realloc
。
另外需要注意的是[]
下标运算符是根据指针算术定义的——表达式a[i]
被定义为*(a + i)
——给定一个地址值a
(如上所述从数组类型转换而来),从该地址偏移 i
个元素( 而不是字节 )并取消引用结果。
另一个需要注意的重要事项是 =
未定义为将一个数组的内容复制到另一个数组。事实上,数组表达式 不能 成为 =
运算符的目标。
CS50 string
类型实际上是类型char *
的typedef
(别名)。 get_string()
函数在幕后执行了很多魔术,为字符串内容动态分配和管理内存,并使 C 中的字符串处理看起来比实际情况要高级得多。我和其他几个人认为这是一种糟糕的 C 语言教学方式,至少在字符串方面是这样。不要误会我的意思,这是一个 非常 有用的实用程序,只是一旦您没有 cs50.h 可用并且必须开始进行自己的字符串处理,您'我们要出海一段时间。
那么,这些废话与您的代码有什么关系?具体来说,行
s = "Bla";
发生的事情是,不是将字符串文字 "Bla"
的内容复制到 s
指向的内存,而是字符串的 地址 正在将文字写入 s
,覆盖先前的指针值。您不能使用 =
运算符将一个字符串的 contents 复制到另一个;相反,你必须使用像 strcpy
:
这样的库函数
strcpy( s, "Bla" );
s[0] = A
之所以如您所料,是因为下标运算符 []
是根据指针算术定义的。表达式 a[i]
的计算结果为 *(a + i)
- 给定一个地址 a
(一个指针,或者一个数组表达式 "decayed" 指向一个指针,如上所述),偏移量 i
个元素( 不是字节!)并取消引用结果。所以 s[0]
指向您读入的字符串的第一个元素。
大家好,我有一个关于将字符串传递给 C 中的函数的问题。我正在使用 CS50 库,我知道他们将字符串作为 char 数组(指向数组开头的 char 指针)传递,所以传递是通过参考完成。我的函数正在接收数组作为参数,它是 returns 数组。例如,当我更改函数中的数组元素之一时,此更改会如我所料反映到原始字符串中。但是,如果我将新字符串分配给参数,函数 returns 另一个字符串和原始字符串不会改变。你能解释一下这种行为背后的机制吗?
#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;
}
第一个示例反映了 text 和 newtext 字符串的第一个字母的变化,但第二个示例打印出 text 不变,newtext 为 "Bla" 谢谢!
如果没有代码示例,这很难正确回答。我会做一个,但它可能与你正在做的不匹配。
让我们使用这个 C 函数:
char* edit_string(char *s) {
if(s) {
size_t len = strlen(s);
if(len > 4) {
s[4] = 'X';
}
}
return s;
}
该函数将接受指向字符数组的指针,并且如果指针不为空且以零结尾的数组更长超过 4 个字符,它将用 'X' 替换索引 4 处的第五个字符。 C中没有references。它们总是被称为pointers。它们是相同的东西,您可以使用 dereference 运算符 *p
或 p[0]
.[=23 等数组语法来访问指向的值=]
现在,这个函数:
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] = '[=11=]';
return s;
}
该函数 return 是一个 指向 原始字符数组的新分配副本或新分配的空字符串的指针。 (通过这样做,调用者总是可以打印出来并在结果上调用 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;
}
请注意,我将 return 类型更改为 const char*
,因为像 "string was longer than 4"
这样的字符串文字是常量。试图修改它会使程序崩溃。
在函数内对 s
进行赋值 不会更改 s 用来指向 的字符数组。指针 s
指向或引用原始字符数组,然后在 s = "string"
之后指向字符数组 "string"
.
这需要一段时间。
让我们从基础开始。在 C 语言中,string 是一个包含 0 值终止符的字符值序列。 IOW,string "hello"
表示为序列 {'h', 'e', 'l', 'l', 'o', 0}
。字符串存储在 char
的数组中(或 wchar_t
表示 "wide" 字符串,我们这里不讲)。这包括 字符串文字 ,例如 "Bla"
- 它们存储在 char
的数组中,因此它们在程序的整个生命周期内都可用。
在大多数情况下,类型 "N-element array of T
" 的 表达式 将被转换 ("decay") 为类型 "pointer to T
" 的表达式,因此大多数时候,当我们处理字符串时,我们实际上是在处理 char *
类型的表达式。但是,这并不意味着 char *
类型的表达式是 字符串 - char *
可能指向字符串的第一个字符,也可能指向指向序列中 不是 字符串(无终止符)的第一个字符,或者它可能指向不属于更大序列的单个字符。
A char *
也可能指向由 malloc
、calloc
或 动态分配的 缓冲区的开头realloc
。
另外需要注意的是[]
下标运算符是根据指针算术定义的——表达式a[i]
被定义为*(a + i)
——给定一个地址值a
(如上所述从数组类型转换而来),从该地址偏移 i
个元素( 而不是字节 )并取消引用结果。
另一个需要注意的重要事项是 =
未定义为将一个数组的内容复制到另一个数组。事实上,数组表达式 不能 成为 =
运算符的目标。
CS50 string
类型实际上是类型char *
的typedef
(别名)。 get_string()
函数在幕后执行了很多魔术,为字符串内容动态分配和管理内存,并使 C 中的字符串处理看起来比实际情况要高级得多。我和其他几个人认为这是一种糟糕的 C 语言教学方式,至少在字符串方面是这样。不要误会我的意思,这是一个 非常 有用的实用程序,只是一旦您没有 cs50.h 可用并且必须开始进行自己的字符串处理,您'我们要出海一段时间。
那么,这些废话与您的代码有什么关系?具体来说,行
s = "Bla";
发生的事情是,不是将字符串文字 "Bla"
的内容复制到 s
指向的内存,而是字符串的 地址 正在将文字写入 s
,覆盖先前的指针值。您不能使用 =
运算符将一个字符串的 contents 复制到另一个;相反,你必须使用像 strcpy
:
strcpy( s, "Bla" );
s[0] = A
之所以如您所料,是因为下标运算符 []
是根据指针算术定义的。表达式 a[i]
的计算结果为 *(a + i)
- 给定一个地址 a
(一个指针,或者一个数组表达式 "decayed" 指向一个指针,如上所述),偏移量 i
个元素( 不是字节!)并取消引用结果。所以 s[0]
指向您读入的字符串的第一个元素。