【问题标题】:Is there a way to create copies of variables so that the original variables don't change in a function?有没有办法创建变量的副本,以便原始变量不会在函数中改变?
【发布时间】:2020-12-23 18:45:54
【问题描述】:

我目前正在处理 CS50 中的替换问题,并且我的变量在代码中的行为方式存在问题。 基本上我得到了两个变量plain_text 和key_2。我的问题是这两个变量,尽管我正在创建它们的副本以便它们不会改变,但仍然被分配了它们的副本所获得的值。现在我知道它在某种程度上与变量的范围有关,但我只是不知道如何正确地做到这一点,所以变量的副本会改变而不是原件。

#include <stdio.h>
#include <math.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>

string substitute_strings (string plain_text,string key);

int main (int argc, string argv[])
{
    for (int i = 0; i < argc; i++)
    {

        if (argc == 1)
        {
            printf ("Enter key!\n");
            return 1;
        }
        for (int j = 0; j < strlen (argv[1]); j++)
        {
            if ((argv[1][j] >= 'a' && argv[1][j] <= 'z') || (argv[1][j] >= 'A' && argv[1][j] <= 'Z'))
            {
                int var = 0;
            }
            else
            {
                printf ("Key can contain only alphabet!");
                return 1;

            }

             }
              if (strlen(argv[1]) != 26)
        {

            printf ("Key must contain 26 characters!\n");
            return 1;

        }

        for (int c = 0; c < strlen (argv[1]); c++)
        {
            for (int b = c + 1; b < strlen (argv[1]);b++)
            {
                if (argv[1][c] == argv[1][b])
                {
                    printf ("Character cannot repeat twice in key!");
                    return 1;
                }
            }
        }
                                    }

string key = argv[1];

string plain_text = get_string ("Enter messege here: \n");



printf("ciphertext: %s\n",substitute_strings (string plain_text , string key));
return 0;
}


**string substitute_strings (string plain,string key_2)**


    {

        string alphabet_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        string alphabet_lower = "abcdefghijklmnopqrstuvwxyz";

      
        string plain_copy = plain;
        

        for (int m = 0; m < strlen (plain_copy); m++)
    {
        if (plain_copy[m] >= 'A' && plain_copy[m] <= 'Z')
        {
            int exit_loop = 0;
            for (int n = 0; exit_loop < 1;n++)
            {
                if (alphabet_upper[n] == plain_copy[m])
                {
                    plain_copy[m] = key_2[n];
                    exit_loop++;
                }
            }
        }

        else if (plain_copy[m] >= 'a' && plain_copy[m] <= 'z')
        {
            int h = 0;
            string key_2_copy = key_2;
            
            while (h < strlen (key_2))
            {
            key_2_copy[h] = tolower(key_2_copy[h]);
            h++;

            }

            int exit_loop_2 = 0;
            int p = 0;
            while (exit_loop_2 < 1)
            {
                if (alphabet_lower [p] == plain_copy [m])
                {
                    plain_copy[m] = key_2_copy [p];
                    exit_loop_2++;
                }
                p++;}





    }
    }


return plain_copy;


}

【问题讨论】:

  • 当您尝试增加缩进以格式化此问题时,看起来代码的缩进有点混乱。您可以通过在代码块之前的行和之后的行上使用三个“反引号”标记 (`) 来显示没有额外缩进的代码。
  • 这是“为什么用 typedef 隐藏指针是个坏主意”的一个很好的例子。
  • 好消息:strdup() 很可能会成为下一个发布的 C 标准的一部分(参见 C2x draft (PDF document)
  • 贴出的代码无法编译!编译时,始终启用警告,然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -Wconversion -pedantic -std=gnu11)注意:其他编译器使用不同的选项来产生相同的结果。
  • 关于以下陈述; printf("ciphertext: %s\n",substitute_strings (string plain_text , string key)); 调用函数时不包括参数类型。唯一需要参数类型的时间是在原型和函数签名中。因此,声明应该是:printf( "ciphertext: %s\n", substitute_strings ( plain_text , key ) ); 注意水平间距,以便我们人类阅读

标签: c function scope cs50


【解决方案1】:

CS50 库在这里让您失败,因为它严重歪曲​​了 C 中字符串的处理方式。简而言之,string typedef 名称是一个谎言,因为它的别名不是字符串强>。舒服点,这需要一段时间。

首先,一些必要的背景...

C 没有真正的字符串数据类型,它有自己的语义和运算符——在 C 中,字符串只是一个包含 0 值终止符的字符值序列,因此像 "foo" 这样的字符串表示为序列'f', 'o', 'o', 0。字符串存储在字符类型的数组中(charwchar_t 用于“宽”字符编码),但您也可以存储 字符串的字符序列(它们没有0 终止符)在字符类型的数组中。

除非它是 sizeof 或一元 &amp; 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则类型为“T 的 N 元素数组”的表达式将被转换或“衰减”为“指向T”类型的表达式,表达式的值将是字符串第一个元素的地址。除此之外,这意味着当您将数组表达式传递给函数时,函数实际接收的是指向数组第一个元素的指针:

void foo( char *str )
{
   // do something with str
}

int main( void )
{
  char string[] = "hello";  // array size is taken from length of string + 1

  foo( string ); 
  ...
}

我们在内存中得到的是这样的:

        +---+
   str: |   |---------------+
        +---+               |
         ...                |
        +---+               |
string: |'h'| string[0] <---+
        +---+
        |'e'| string[1]
        +---+
        |'l'| string[2]
        +---+
        |'l'| string[3]
        +---+
        |'o'| string[4]
        +---+
        | 0 | string[5]
        +---+

str 不包含字符串本身,它包含存储字符串的缓冲区的地址。对于 所有 数组类型,这种行为是相同的,而不仅仅是包含字符串的数组。

这样做的结果是,当我们处理字符串时,大部分时间我们都在处理char * 类型的表达式。但是char * 不是字符串 - 它可能指向字符串的第一个字符,或者它可能指向不是序列的第一个字符一个字符串(无终止符),或者它可能指向一个不属于更大序列的单个 char 对象。

好的,这就是背景。那么,这一切如何应用于您的代码?

CS50 库在后台执行各种魔术,将您与 I/O 和内存管理的血腥细节隔离开来。 get_string 函数提示用户输入,动态分配内存来存储输入字符串,并返回一个指向该动态缓冲区的指针

char *str = malloc( SOME_SIZE );
// get string from input and save to this buffer
return str;

CS50 库引入了 typedef 名称 string 作为类型 char * 的别名。这意味着string 类型的对象实际上是指针,而不是字符串。

因此,当您将get_string 的结果分配给plain_text 时,您在内存中的内容是这样的(假设"foo" 是输入):

            +---+
plain_text: |   | ----+
            +---+     |
             ...      |
            +---+     |
            |'f'| <---+
            +---+
            |'o'|
            +---+
            |'o'|
            +---+
            | 0 | <-- string terminator
            +---+

所以plain_text 不存储字符串本身,它存储包含字符串的缓冲区的地址。当你将plain_text 传递给substitute_strings 时,它只是接收到这个指针值,而不是字符串的副本。

当你写作时

plain_copy = plain;

您将缓冲区的地址复制到plain_copy,所以plain plain_copy都指向相同的内存 em>:

            +---+
plain_copy: |   | -------+
            +---+        |
             ...         |
            +---+        |
     plain: |   | ----+  |
            +---+     |  |
             ...      |  |
            +---+     |  |
            |'f'| <---+--+
            +---+
            |'o'|
            +---+
            |'o'|
            +---+
            | 0 |
            +---+

您对缓冲区所做的任何更改都会反映在plainplain_copy 中。

那么你如何解决这个问题?

在您的substitute_strings 函数中,您需要分配第二个相同大小的缓冲区并将其地址分配给plain_copy,然后将plain 的内容复制到该新缓冲区中:

string plain_copy = malloc( strlen( plain ) + 1 );
if ( plain_copy )
  strcpy( plain_copy, plain );
else
  // unable to allocate memory for copy, handle as appropriate

// manipulate and return plain_copy as before 

现在你有以下情况:

            +---+
     plain: |   | ----+
            +---+     |
             ...      |
            +---+     |
            |'f'| <---+
            +---+
            |'o'|
            +---+
            |'o'|
            +---+
            | 0 |
            +---+

            +---+
plain_copy: |   | ----+
            +---+     |
             ...      |
            +---+     |
            |'f'| <---+
            +---+
            |'o'|
            +---+
            |'o'|
            +---+
            | 0 |
            +---+

plainplain_copy 现在指向两个不同的字符串,因此更改一个不会影响另一个。

【讨论】:

    【解决方案2】:

    cs50 上下文中的stringchar* 的别名,实际上是复制了指针。

    要复制指向的字符串,您应该分配内存并使用strcpy()

    #include <stdlib.h>
    

    应在代码顶部添加以使用malloc()(和exit())和

    string plain_copy = plain;
    

    应该是

    string plain_copy = malloc(strlen(plain) + 1); /* +1 for terminating null-character */
    if (plain_copy == NULL) { /* check if allocation is successful */
        perror("malloc");
        exit(1);
    }
    strcpy(plain_copy, plain);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 2013-03-06
      • 1970-01-01
      相关资源
      最近更新 更多