在CS50库中使用字符串

问题描述 投票:0回答:2

大家好我有一个关于将字符串传递给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;
}

第一个例子反映了text和newtext字符串上第一个字母的变化,但第二个例子打印出文本不变,newtext打印为“Bla”谢谢!

c string cs50
2个回答
3
投票

这需要一段时间。

让我们从基础开始。在C中,字符串是包括0值终止符的字符值序列。 IOW,字符串"hello"表示为序列{'h', 'e', 'l', 'l', 'o', 0}。字符串存储在char(或wchar_t)的数组中,用于“宽”字符串,我们在此不再赘述。这包括像"Bla"这样的字符串文字 - 它们存储在char的数组中,以便它们在程序的生命周期内可用。

在大多数情况下,“T的N元素数组”类型的表达式将被转换(“衰变”)为“指向T的指针”类型的表达式,所以大部分时间我们处理字符串时我们都是实际上处理char *类型的表达式。但是,这并不意味着char *类型的表达式是一个字符串 - 一个char *可能指向一个字符串的第一个字符,或者它可能指向一个不是字符串的序列中的第一个字符(没有终结符),或者它可能指向不属于较大序列的单个字符。

char *也可能指向由malloccallocrealloc分配的动态分配缓冲区的开头。

需要注意的另一点是[]下标运算符是根据指针算法定义的 - 表达式a[i]定义为*(a + i) - 给定地址值a(从如上所述的数组类型转换),偏移i元素(不是字节)解决结果的地址和取消引用。

另一个需要注意的重要事项是,=未定义为将一个数组的内容复制到另一个数组。实际上,数组表达式不能成为=运算符的目标。

CS50 string类型实际上是typedef类型的char *(别名)。 get_string()函数在幕后执行许多魔术,为字符串内容动态分配和管理内存,并使C中的字符串处理看起来比实际更高。我和其他几个人认为这是教导C的一种不好的方式,至少在字符串方面。不要误会我的意思,这是一个非常有用的工具,只是一旦你没有cs50.h可用并且必须开始自己的字符串处理,你就会在海上待一段时间。

那么,所有废话与你的代码有什么关系呢?具体来说就行了

s = "Bla";

发生的事情是,不是将字符串文字"Bla"的内容复制到s指向的内存,而是将字符串文字的地址写入s,覆盖先前的指针值。您不能使用=运算符将一个字符串的内容复制到另一个字符串;相反,你将不得不使用像strcpy这样的库函数:

strcpy( s, "Bla" );

s[0] = A按预期工作的原因是因为下标运算符[]是用指针算法定义的。表达式a[i]被评估为*(a + i) - 给定地址a(指针,或者如上所述对指针“衰减”的数组表达式),从该地址偏移i元素(不是字节!)并取消引用结果。所以s[0]指向你读到的字符串的第一个元素。


2
投票

没有代码示例,这很难正确回答。我会制作一个,但它可能与你正在做的不一致。

我们来看看这个C函数:

char* edit_string(char *s) {
    if(s) {
        size_t len = strlen(s);
        if(len > 4) {
            s[4] = 'X';
        }
    }
    return s;
}

该函数将接受指向字符数组的指针,如果指针不是NULL且零终止数组超过4个字符,它将用索引4替换第五个字符“X”。 C中没有引用。它们总是被称为指针。它们是相同的,你可以使用解引用运算符*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 = "string"之后指向字符数组"string"

© www.soinside.com 2019 - 2024. All rights reserved.