【问题标题】:Remembering a variable for next use记住一个变量以备下次使用
【发布时间】:2014-02-15 18:32:07
【问题描述】:

我在记住变量以供下次使用并将其打印出来时遇到问题。我会详细解释它,以便了解我在我的程序中要做什么。

我有一个人在大小为a b 的矩形上行走。我输入起始位置x y 和人的起始方向(North = y+1, South = y-1, East = x+1, West = x-1 // 在我的代码中它是 S,J,V,Z )。所以我的输入看起来像这样:

5 6 // a b 3 3 S // x y s(代表起始方向-北)

现在,我输入移动次数d 为应该移动的人生成。 我输入数字 4,它可以由 3 个字母生成:D、L、P(前进,左转 90 度,右转 90 度)。

4 // d PLDL // 移动

现在,这个人应该按照这些动作走路。所以如果人的位置和起始方向是3 3 S,它应该右转(我的方向是东,但位置相同),然后左转(方向又是北,相同的位置),然后向前(现在我移动y+1,我的方向仍然是北),最后一步是左转(西方向)。所以mi最终位置和方向(输出)是:

3 4 Z

希望你能理解。如果有不清楚的地方,请在评论中提问。

我现在得到奇怪的输出,不真实的数字。我无法弄清楚如何将变量放在一起以及解决它的条件。我的代码首先采用起始方向和位置,但稍后当我生成移动时,它应该根据生成的字符串更改为最终输出。可悲的是,它没有按我的预期工作。你有什么建议吗?我的问题有点广泛,但我希望我们能一起解决。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <string.h>
#include <vector>

using namespace std;
int n; // pocet uloh
int a; // rozmer obdlznika a
int b; // rozmer obdlznika b
int i;


static const char alpha[] = {'D', 'L', 'P'};
char genRandom()
{

    return alpha[rand() % strlen(alpha)];
}
// end of generator

// funkcia na pohyb

void pohyb (){
    int x[i];
    int y[i];
    string sD = ""; // starting direction
    string mD = ""; // middle direction (stored one for next use)
    string eD = ""; // ending direction to print out in output
    string d = ""; // number of generated directions eg. d=6 ==> PDDLPPD


    for (int i=0; i < d.size(); i++){

            if (sD[i] == 'S'){
                if(d[i] == 'D'){
                    y[i] = (y[i]+1);
                }else if(d[i] == 'L'){
                    mD[i] == 'Z'; 
                }else if(d[i] == 'P'){
                    mD[i] == 'V'; 
                }
            }else if (sD[i] == 'J'){
                if(d[i] == 'D'){    
                    y[i] = (y[i]-1);
                }else if(d[i] == 'L'){
                    mD[i] == 'V'; 
                }else if(d[i] == 'P'){
                    mD[i] == 'Z'; 
                }    
            }else if (sD[i] == 'V'){
                if(d[i] == 'D'){
                    x[i] = (x[i]+1);
                }else if(d[i] == 'L'){
                    mD[i] == 'S'; 
                }else if(d[i] == 'P'){
                    mD[i] == 'J'; 
                }    
            }else if (sD[i] == 'Z'){
                if(d[i] == 'D'){
                    x[i] = (x[i]-1);
                }else if(d[i] == 'L'){
                    mD[i] == 'J'; 
                }else if(d[i] == 'P'){
                    mD[i] == 'S'; 
                }    
            } // koniec if podmienky
    eD = mD[i];
    } // koniec for loopu
    // vystup
    for ( i = 0 ; i < n ; i++ )
    {

        if(!((x[i]>=0)&&(x[i]<=a) & (y[i]>=0)&&(y[i]<=b))){
            cout << x[i] << ' ' << y[i] << ' ' << eD[i] << ' ' << "SPADOL" << endl;
        }else{
            cout << x[i] << ' ' << y[i] << ' ' << eD[i] << endl;
        }

    } 

} // koniec funkcie pohyb



int main() {

    cin >> n;
    vector<int> x(n); // x position
    vector<int> y(n); // y position
    vector<int> d(n); // zombie directions generation ex. DPLDDP 
    vector<string> sD(n); // starting direction
    vector<string> eD(n); // ending direction

    while(!((n >= 1)&&(n <=15000)))
    {
        cout << "max 15000" << flush;
        cin >> n;
    }


    cin >> a >> b;

    while(!((a >= 1)&&(a <=100) & (b >= 1)&&(b <= 100)&&(a!=b)))
    {
        cout << "chyba max 100 alebo a!=b" << endl;
        cin >> a >> b;
    }


    for (i = 0; i < n; i++)
    {    
        cout << "Uloha " << i+1 << ":" << endl;
        cin >> x[i];
        cin >> y[i];
        cin >> sD[i];

        while(!((x[i]>=0)&&(x[i]<=a))) {
            cout << "Try Again x: " << flush;
            cin >> x[i];}
            while(!((y[i]>=0)&&(y[i]<=b))) {
                cout << "Try Again y: " << flush;
                cin >> y[i];}

                cin >> d[i];
                while(!((d[i]>=1)&& (d[i]<=200))) {
                    cout << "Try Again d: " << flush;
                    cin >> d[i];}


                    for (int counter=0; counter<d[i]; counter++)
                    {
                        cout << genRandom();
                    }
                    cout << endl;

    }    // koniec for

    pohyb();
system("pause");

}

示例输入:

3
3 5 
2 2 S 
8
DPLDLPDD
2 4 Z
7
PDDPDPD
2 1 J
8
PPDLDDDD

和输出

2 5 S SPADOL // spadol means his location is out of the rectangle
3 4 J
0 2 Z SPADOL

【问题讨论】:

  • 您应该使用switch() 而不是if,这个y[I] = (y[I]+1)y[I] ++y += 1 相同,变量sD、mD、eD 应该是char 而不是@ 987654340@如果你只想记住一个符号
  • 您应该提供一个示例输入、其预期和实际输出。
  • @Quest 好的,我该如何更改其余代码,因为我得到[Error] invalid types char[int]' 为数组下标` 为== S 等...当我更改 sD , mD, eD 转字符
  • @febeks 这是一个小问题,您可以在您的代码工作后解决。你读过我回答的第 2 项吗?
  • @febeks 在我的答案中添加了一个编辑,解释了如何将变量而不是它的值传递给函数。

标签: c++ variables memory if-statement


【解决方案1】:

我将给你一些解释,而不是修复你的代码,这些解释应该可以帮助你理解并自己修复它。

首先,让我调整一下您对变量是什么的理解。在编程语言中,有一些值需要存储。一旦我们存储了一个值,我们就需要能够再次检索它,因此我们需要一种方法来描述它的存储位置。

int i = 5;

这告诉编译器创建一个int 值类型的实例,为其分配值5,并将其命名为i

但是,C++ 是一种作用域语言。这意味着任何给定名称的可见性都有限制。

int x() {
    int i;
}

int y() {
    i = 5; // ERROR: I not declared in this scope.
}

在上面的代码中,我们在一个作用域(x 的函数体)中声明了i,但随后尝试在另一个作用域中使用它。

C++ 作用域通常用'{ ... }' 来区分,例如以下是有效的:

#include <iostream>

int i = 0; // globally visible 'i'.

void f() { std::cout << "from f i = " << i << '\n'; }

int main() { // <-- function body scope
    int i = 1;
    { // inner scope
        int i = 2; // new variable, called 'i', but only inside this scope.
        { // <-- another inner scope
            i = 3;
            f();
        }
    } // scope ended, the second 'i' has no gone away.
    std::cout << "from end of main i = " << i << '\n';

    return 0;
}

上面的程序打印“0”,然后打印“1”。

C++ 允许我们做一些叫做“阴影”的事情——我们可以在 不同 范围内为 不同 变量使用相同的名称。

作用域也会影响变量的“生命周期”(参见http://ideone.com/fXPlB7),但我不打算对此进行讨论。

让我更清楚地展示其含义 - 变量具有相似的名称但不是同一个变量:

int i = 5;

void f(float i)
{
    std::cout << "in f, i is " << i << '\n';
}

int main()
{
    char i[] = "Hello";

    f(3.141);

    std::cout << "in main, i is " << i << '\n';

    return 0;
}

这个程序打印什么?

确保您理解这一点:i 没有改变,而是 i 在给定范围内引用了哪个变量

在您的函数pohyb 中,您有以下两行代码:

string d = ""; // number of generated directions eg. d=6 ==> PDDLPPD

for (int i=0; i < d.size(); i++){

这声明了一个新变量,并在此范围内导致名称 d 引用它。 d 是一个空字符串。

下一行遍历d 中的所有值。空字符串中有多少个值? 0. 所以,for循环行是这样说的:

int i = 0;
is i < 0?

0 不是

您的下一个问题是 C++ 中字符串(C-string)和字符数组之间的区别。

C++ 基于 C,它没有“字符串”的第一类定义。相反,C 有一个约定:“字符串是由 0 个或多个字符组成的数组,后跟一个零值字符”。

char empty[1] = { 0 }; // valid, empty string. it has 1 element, the 'nul'.
char a[] = { 'a', 0 }; // represents "a", size is 2 chars, 'a' and '\0'
char hello[] = { 'h', 'e', 'l', 'l', 'o', 0 }; // size 6, 5 letters and a nul
char Hello[] = "hello"; // short-cut for writing the above
char ten[] = { '1', '0', 0 }; // '0' and 0 are not the same
char hundred[] = { '1', '0', '\0' }; // '\0' == 0
char ouch[4] = "ouch"; // ERROR: the string is actually 5 chars.

所有处理“字符串”(不要与 std::strings 混淆)的 C 函数都按照这个原则运行——判断长度的唯一方法是对字符进行计数,直到值为零.

出于您的目的,您实际上想要一个字符数组,但这不会自动将它们变成一个字符串。

您的代码使用 strlen 来查找 char 数组中的元素数 - 这是不正确的,并且对您的应用程序有潜在危险。紧跟在 alpha 的 3 个有效元素之后的字节可以是任何值,因此 strlen 可能返回 3 或返回非常大的值。

你真正想要的是C关键字sizeof

sizeof(X) 是在编译时确定事物的大小。当 X 是一个完全限定的数组时,它返回 X 的字节大小。请注意,这意味着您只能在全局或局部范围内的数组上使用它:when you pass arrays to functions they are passed by pointer

#include <iostream>

char hello[] = "hello"; // has size 6: 'h', 'e', 'l', 'l', 'o', 0

void f(char x[])
{
    std::cout << "f(x), sizeof x = " << sizeof(x) << '\n';
}

void g()
{
    char x[] = "world";
    std::cout << "g() sizeof x = " << sizeof(x) << '\n';
}

void h()
{
    int x[] = { 1, 2, 3, 4, 5, 6, 7 };
    std::cout << "h() sizeof x = " << sizeof(x) << ", but sizeof(x[0]) = " << sizeof(x[0]) << '\n';
}

int main()
{
    std::cout << "main() sizeof hello = " << sizeof(hello) << '\n';
    f();
    g();
    h();
    return 0;
}

您期望输出是什么?如果您想了解,请粘贴到ideone

对于您的代码,char 数组的使用看起来是正确的,因此您想使用sizeof 来确定数组中有多少个字符。请记住,sizeof 以字节为单位返回大小,正式的正确写法是:

size_t index = size_t(rand()) % (sizeof(alpha) / sizeof(*alpha))];
return alpha[index];

这将获取 alpha 的总大小并将其除以 alpha 指向/包含的类型(一个字符)的大小。这些值在编译时是已知的,因此编译器将执行此计算并发出等效于:

return alpha[rand() % (3 / 1)];

或者只是

return alpha[rand() % 3];

alpha 中有 3 个元素,但 C/C++ 数组的索引为 0,因此模数将为我们提供一个值 [0,3),即 0、1 或 2。

最后,您担心使用 if 语句。对于复杂的逻辑,有时最好的办法是将它们写出来并手动运行它们。您可能想熟悉一下 switch 关键字,它接受一个变量并将其与潜在值进行匹配:

#include <iostream>
#include <string>

int main()
{
    std::string input;

    while (std::cin.good()) {
        std::cout << "Direction? (n/s/e/w/q): ";
        std::getline(std::cin, input);

        // if input is empty, input[0] would be undefined behavior.
        if (input.empty())
            continue;

        switch (input[0]) // check the first character only
        {
            // input[0] is of type char, so we can express our values
            // a character literals. we could also write the ascii values,
            // e.g. for 'n' we could put "case 110:"
            case 'n':
                std::cout << "You have entered a dark room.\n";
                break; // escape the switch, not the loop.

            case 'e':
            case 's': // no break, 'e' falls thru
            case 'w': // still no break, 'e' and 's' fall thru
                std::cout << "You can't go that way.\n";
                break;

            case 'q':
                std::cout << "bye!\n";
                return 0;
                break;

            default:
                std::cout << "I asked you to type n, s, e, w or q, but you typed " << input << ".\n";
                break;
        }
    }

    return 0;
}

http://ideone.com/s4xana

---- 编辑----

关于“记住”作用域之间的值。在函数体和嵌套范围内,这会自动发生:

int main() {
    int i = 1;
    { // inner scope
         std::cout << "inner scope\n";
         { // another inner scope
             if (i == 1) {
                 // this is a scope
                 std::cout << "i = " << i << '\n'; // prints 1
             }
         }
    }
}

但是在函数和模块之间,你需要让它们成为函数参数。

#include <iostream>

int f(int i, int j, int k) {
    std::cout << "f() i = " << i << ", j = " << j << ", k = " << k << '\n';
    i = 10;
    j = 100;
    k = 300;
}

int main() {
    int j = 42;
    f(j, j, j);
    std::cout << "in main: j = " << j << '\n';
    return 0;
}

这打印什么?记住:变量是局部作用域的。仅仅因为它们与不同范围内的另一个变量具有相同的名称并不能使它们相互连接。

想想下面这样的代码,警告:伪代码:

define f - takes int as f::i, int as f::j, int as f::k
    "f() i = ", f::i, ", j = ", f::j, ", k = ", f::k, '\n';
    f::i = 10;
    f::j = 100;
    f::k = 300;
end f

define main
    declare main::j as int
    let main::j be 42
    call f with f::i = 42, f::j = 42 f::k = 42
    "in main: j = " << main::j << '\n';
end main

现在它可能更有意义 - 即使我们在 f 中更改了 j,但与我们在 main 中看到的 j 不同。

如何克服这个问题:

C++ 提供了两种方法。旧的“c”方法是传递变量的地址,称为通过指针传递。指针可能会变得很麻烦,并且经常使新程序员感到困惑,因此我将向您展示 C++ 机制:参考。

正如您在上面看到的,当您调用带有参数的函数时,C++ 会创建一个新的局部范围变量并将输入变量的值复制到其中:

void f(int n)
{
    n += 2;
}

f(5);

这里我们看到'5' 不是一个变量,而是一个硬编码的值。否则 'f' 不可能工作 - 在整个程序中,'5' 会变成 7。

当我们想说“调用 f 并对我的 LOCAL 变量进行操作”时,我们使用引用。

void f(int& n)
{
    n += 2;
}

int main()
{
    int x = 23;
    f(x);
    // x is now 25
}

很容易认为引用只是一个别名,但这不是它们的实现方式。引用是传递现有变量在内存中的位置的一种巧妙方法,但不够聪明,无法意识到该变量正在消失或在内存中重新定位。

std::vector<int> v;
v.push_back(5);
int& first = v[0]; // reference to the first element of v at the moment,.
std::cout << "first = " << first << '\n'; // prints 5.
v.reserve(2000); // causes 'v' to relocate in memory
v[0] = 25;
std::cout << "first = " << first << '\n'; // may crash or print 5, but not 25.

关于引用要记住的另一件事是,一旦它们连接到某物,就无法更改连接:

int a = 5, b = 6;
int& r = a;
std::cout << r;
r = b;
std::cout << r;
std::cout << a;

这会打印:566,而不是 565,因为 int&amp; r = a 使 r 引用了 a。当我们说r = b 时,因为r 现在是a 的引用,我们实际上说的是a = b

---- 编辑 2 ----

C 和 C++ 有一个修饰符关键字,const,这是一个约定你承诺不修改任何东西。如果您想编写一个通过引用接受复杂对象的函数(以避免复制字符串等,这很昂贵),但您不想更改它,可以使用 const 修饰符:

#include <iostream>
#include <string>

void writeln(const std::string& str)
{
    std::cout << str << '\n';
}

int main()
{
    std::string greeting = "hello";
    writeln(greeting);
}

另外,关于“&”的注释。不管你写string&amp; str 还是string &amp;str,编译器都没有关系,它们的意思是一样的。 &amp; 是指“引用”还是“地址”(用于指针)或“和”(用于逻辑)取决于上下文。

【讨论】:

  • @kfstone 哇...你写这篇文章多久了? +1 ...当你这样做时:int a = 5; int *pA = a; 然后a = 7; pA == 7 或不?
  • @Quest 如果你写 int a = 5; int *pa = &amp;a; a = 7; int b = *pa; 然后取消引用 pa (*pa) 将给出 7 而不是 5,是的。
【解决方案2】:

注意:这些是在您发布示例输入和输出之前编写的。

  1. 当您将alpha 定义为时

    static const char alpha[] = {'D', 'L', 'P'};
    

    它实际上是一个由三个元素组成的 char 数组。但是strlen() 是一个函数,用于计算遇到的第一个\0 (NUL) 字符之前的字符数。因此,genRandom() 中的 strlen() 将无法按预期工作(我猜它会返回一个随机的大数。)您应该将 alpha 定义为

    static const char alpha[] = "DLP";
    

    将隐式第四个元素 \0 添加到 alpha

  2. 在您的pohyb() 中,您定义string 变量sDmDd,初始值为空。它们与您的main() 中的vector&lt;int&gt; dvector&lt;string&gt; sD, mD 没有任何关系。所以你所有的i &lt; d.size()sD[i] == 'S'd[i] == 'D'...都不会像你期望的那样工作。您应该将main() 中的sDmDd 作为参数传递给pohyb()
    更新:好的,我会更具体。因为pohyb() 中的string dmain() 中的vector&lt;int&gt; d 无关,并且在整个pohyb() 中保持为空,所以for (int i=0; i &lt; d.size(); i++) 甚至不会运行一次。因为pohyb() 中的int x[i], y[i]main() 中的vector&lt;int&gt; x, y 无关,并且包含未初始化的(= 随机)i 元素(这里i 在调用pohyb() 时恰好等于n)你看到奇怪的(“SPADOL”)输出。请先学习如何在 C++ 中将参数传递给函数。在你学会它之前,你将一事无成。

我没有检查这两个是否就是你所需要的。它们只是我迄今为止发现的明显错误。您可能需要对程序结构进行根本性的修改。

【讨论】:

  • you should define alpha as static const char alpha[] - 。他的第一直觉是把它变成一个只有 3 个字符的数组,这是正确的。他应该做的是使用 sizeof:alpha[rand() % (sizeof(alpha) / sizeof(alpha[0]))]sizeof(alpha) 将返回 alpha 的字节大小,sizeof(alpha[0]) 将返回 alpha 元素的大小,将 size-in-bytes 划分为 number-of-elements。由于 alpha 是一个字符数组,这将为他提供数组中的字符数。由于数组是基于 0 的,这将为他提供他正在寻找的正确随机化。
  • @kfsone 好吧,只是(至少)有两种方法可以修复他的genRandom();一个是你提议的,另一个是我提议的。他们都不应该被认为是错误的。作为旁注,值得注意的是,sizeof(char) 保证为 1。
  • 随机生成不是我的问题,我在改变 vaues x y s 时遇到了问题......正如我所描述的那样
  • @nodakai 它们不是 c 字符串,它们是字符数组,因此在它们上使用字符串工具是不正确的,因为添加 nul 字节并使它们类似于字符串。
  • @febeks 随机生成是您的问题之一。在 3 字节字符数组上使用 strlen(alpha) 不会返回合理的值。如果rand() % strlen(alpha) 的计算结果为9827341,那么alpha[9827341] 的计算结果是什么?
猜你喜欢
  • 2023-03-31
  • 1970-01-01
  • 2013-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-10
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多