[try Beta version]
Not logged in

 
While Loop Fails?

Apr 23, 2013 at 12:12am
I'm wondering why my program crashes at my while loop when run with a file containing "print hello world". Here's the code including the if statement:

1
2
3
4
5
6
7
8
9
if (tokens[0] == "print") {
                        e = sizeof(tokens) / sizeof(tokens[0]);
                        e--;
                        f = 1;
                        while (f <= e) {
                            cout << tokens[f];
                            f++;
                        }
                    }
Apr 23, 2013 at 3:37am
what type tokens is? Is it array of std::string?
Last edited on Apr 23, 2013 at 4:49am
Apr 23, 2013 at 4:41am
If tokens is a string, how can you possibly compare a single character in that string to the string "print"? It's not like by making the statement if(tokens[0] == "print") the program will parse through every character until it finds "p", immediately check the next one for "i" and so on. That, and you are setting e to be the size of the string divided by the size of a character, which is also invalid. One small tidbit as well- you could easily replace that while statement with a for statement, for simplicity.
Apr 23, 2013 at 12:15pm
tokens is a string array. It contains a few strings: print hello world.
Apr 23, 2013 at 12:23pm
Is tokens a dynamic array?
Apr 23, 2013 at 12:55pm
program crashes at my while loop when run with a file containing "print hello world"

The mention of a file suggests the input to the program comes from an external file, where the size of the data cannot be known at compile-time.

Consequently, this sort of calculation
sizeof(tokens) / sizeof(tokens[0]) based upon what is known at compile time, cannot give any useful result.

Either maintain a separate field which contains a count of how many elements of the array are in use, or more simply, use a vector which can keep track of its own size during use.

lmsmi1 wrote:
tokens is a string array. It contains a few strings: print hello world.

But what sort of string? A C++ std::string, or a plain null-terminated c-string?
Last edited on Apr 23, 2013 at 1:08pm
Apr 23, 2013 at 1:53pm
Here's the full code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include <iostream>
#include <string>
#include <windows.h>
#include <fstream>
#include <vector>
#include <sstream>

using namespace std;

void SetColor(unsigned short color) {
    HANDLE hcon = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hcon, color);
}

BOOL WINAPI SetConsoleIcon(HICON hIcon) {
    typedef BOOL (WINAPI *PSetConsoleIcon)(HICON);
	static PSetConsoleIcon pSetConsoleIcon = NULL;
	if(pSetConsoleIcon == NULL)
		pSetConsoleIcon = (PSetConsoleIcon)GetProcAddress(GetModuleHandle(("kernel32")), "SetConsoleIcon");
	if(pSetConsoleIcon == NULL)
		return FALSE;
	return pSetConsoleIcon(hIcon);
}

int main(int argc, char* argv[])
{
    SetConsoleTitle("OpenCMD v1.0.0");
    HICON hIcon = LoadIcon(GetModuleHandle(0),"MAINICON");
   	SetConsoleIcon(hIcon);

    if (argc<2) {
        cerr << "OpenCMD v1.0.0\n(C) Copyright 2013 HackForums";
        getchar();
    return(0);
    } else {

        string line;
        string cmd;
        string params;
        string token;

        unsigned int a; // first for loop var.
        unsigned int b = 0; //
        unsigned int c = 0; // Spaces in the string to parse
        unsigned int d = 0; // Number of tokens in string array
        unsigned int e = 0; // Extra interger to use with parsing commands from file (Ex. print ...)
        // unsigned int f = 0; // Spare int


        ifstream file;
        file.open(argv[1]); // Args start a 0, then go up by 1. argv[0] is the Program path.
        if(!file.is_open()) {
            file.close();
            cout << "Error opening file.";
        } else {
            while(!file.eof()) { // While the file isn't ended
                b = 0;
                c = 0;
                d = 0;
                getline(file, line); // get the command's whole line
                if (!line.empty()) { // If the line isn't empty...
                    for (a = 0; a < line.length(); a++) { // Tranform string to lowercase
                        tolower(line.at(a));
                    }
                    while(isspace(line.at(b))) {
                        b++;

                        for(; b < line.length(); b++) {
                            if(isspace(line.at(b))) {
                                c++;

                                // Skip over duplicate spaces & if a NULL character is found, we're at the end of the string
                                while(isspace(line.at(b++))) {
                                    if(line.at(b) == '\0') {
                                        c--;
                                    }
                                }
                            }
                        }
                    }
                    c++;
                    string tokens[c]; // initialize the number of tokens to number of spaces + 1
                    stringstream ss(line);
                    while (getline(ss, token, ' ')) { // add tokens to array
                        tokens[d] = token;
                        d++;
                    }
                    if (tokens[0] == "color") { // check first token (0)
                        if (tokens[1] == "-green") { // check second token (1)
                            SetColor(10); // do command for the tokenized string read
                        }
                    } else if (tokens[0] == "pause") {
                        getchar();
                    } else if (tokens[0] == "print") {
                        e = 1;
                        while (e < (sizeof(tokens) / sizeof(tokens[0]))) {
                            cout << tokens[e];
                            e++;
                        }
                    } else {
                        cout << "OpenCMD has read an unknown command: " << tokens[0];
                        getchar();
                    }
                }
            }
            file.close(); // Close File
        }
    }
    return(0);
}


tokens is a C++ string, and I don't know why the while loop won't print whatever is after "print" to the console.
Apr 23, 2013 at 2:17pm
string tokens[c];
Variable Length Arrays are not supported in C++
It should give you a warning. If it doesn't — turn on warnings.
Looks all problem arises from use of VLA and sizeof together, which is undefined behavior actually.

Are you using MSVS?
Last edited on Apr 23, 2013 at 2:18pm
Apr 23, 2013 at 2:20pm
No. I'm using codeblocks 12.11 (as an IDE. Mingw is my compiler). If I can't define the number of elements in an array via an int variable, how do I set the number of elements in tokens to the number of words in the string that was tokenized?
Last edited on Apr 23, 2013 at 2:27pm
Apr 23, 2013 at 2:35pm
Oh, yes, gcc partially supports VLA. I usually compile with -pedantic-errors so it isn't used.
a) You can dynamically allocate your array:
1
2
int* x = new int[c];
x[2] = //.... 
However you will need to save your c variable because it holds your array size which you cannot deduce.
And you need to delete array when you done with it: delete[] x;
b) You can use one of standard collections: std::vector<std::string> will do it for you.
It has a size() member function, support insertion, deletion and other manipulations with it.
Last edited on Apr 23, 2013 at 2:36pm
Apr 23, 2013 at 2:37pm
Is it possible to tokenize and parse a string vector?
Apr 23, 2013 at 2:48pm
Something like this:
1
2
3
4
5
6
            vector<string> tokens;
            stringstream ss(line);
            while (ss >> token)      // add tokens to array
            {
                tokens.push_back(token);
            }


1
2
3
4
5
6
7
            else if (tokens[0] == "print")
            {
                for (int e=1; e < tokens.size(); e++ )
                {
                    cout << tokens[e] << " ";
                }
            }
Apr 23, 2013 at 2:54pm
Dang, I didn't know vectors were that cool :)

Thanks dude, you've shown me how to use a vector right, and have shown me how to print words from a file. You're good at this, aren't you?
Apr 23, 2013 at 2:55pm
Thanks. I have good days and bad days. But I try.
Topic archived. No new replies allowed.