有没有一种方法可以创建变量的副本,以使原始变量在函数中不发生变化?

时间:2020-09-04 13:25:31

标签: c function scope cs50

我目前正在处理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;


}

2 个答案:

答案 0 :(得分:4)

在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);

答案 1 :(得分:2)

CS50库在这里很容易出错,因为它严重误解了C语言处理字符串的方式。简而言之,string typedef名称是一个谎言,因为它的别名是不是字符串。感到舒适,这将需要一段时间。

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

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

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

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 *对象。

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

CS50库在后台执行各种魔术操作,使您与I / O和内存管理的繁琐细节隔离开来。 char函数提示用户输入,动态地 分配内存来存储输入的字符串,并向该动态缓冲区返回 pointer

get_string

CS50库引入了typedef名称char *str = malloc( SOME_SIZE ); // get string from input and save to this buffer return str; 作为类型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都指向< em>相同的内存:

plain_copy

您对缓冲区所做的任何更改都会同时反映在 +---+ plain_copy: | | -------+ +---+ | ... | +---+ | plain: | | ----+ | +---+ | | ... | | +---+ | | |'f'| <---+--+ +---+ |'o'| +---+ |'o'| +---+ | 0 | +---+ plain中。

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

在您的plain_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 | +---+ plain现在指向两个不同的字符串,因此对一个字符串的更改不会影响另一个字符串。

相关问题