在 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 运算符 *pp[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 * 也可能指向由 malloccalloc 动态分配的 缓冲区的开头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] 指向您读入的字符串的第一个元素。