Hi~
I've been stuck with this exercise for quite a few days. It's in a book I'm currently self-studying, C++ primer plus. It is as follow:

/*
    Write a program that uses an array of char and a loop to read one word at a time until
    the word done is entered. The program should then report the number of words entered
    (not counting done). A sample run could look like this:
    Enter words (to stop, type the word done):
    anteater birthday category dumpster
    envy finagle geometry done for sure
    You entered a total of 7 words.
    You should include the cstring header file and use the strcmp() function to make the
    comparison test.
    */

Since the exercise is in Chapter 5 - Loops and Relational Expressions, and Branching Statements and Logical Operators is Chapter 6, I can't use if statements here.
The following code is how I did it, but it has problems. The idea is to identify a word from the original array, place it in another array to compare with the "done" string, and increment the NumberOfWords variable accordingly.
There is no compile-time errors, but runtime errors there are >"< :
- I typed in "test string done", then pressed enter. The program halted, and VS2010 says: Unhandled exception at 0x011315f6 in C++PrimerPlusFrame.exe: 0xC0000005: Access violation reading location 0x00430000. and in the source code editor there is a yellow arrow poiting at line 46;
- I typed in "word test done", then pressed Enter, the program reported "The text has 2 words" correctly. But when I pressed "Enter" again, VS2010 says "Run-Time Check Failure #2 - Stack around the variable 'Analyzer' was corrupted.", and a yellow arrow points in the last line, the closing brace '}' of main(), of the source code.

Can anyone tell me what was going wrong here? And what do those two messages mean, access violation and stack corruption? And what is a stack, anyway? What do the two arrows point at? I am only a beginner, and I have only little experience with VS2010. Please, shed some light on me ^^"

Here is the source of the whole thing:

/*
    Write a program that uses an array of char and a loop to read one word at a time until
    the word done is entered. The program should then report the number of words entered
    (not counting done). A sample run could look like this:
    Enter words (to stop, type the word done):
    anteater birthday category dumpster
    envy finagle geometry done for sure
    You entered a total of 7 words.
    You should include the cstring header file and use the strcmp() function to make the
    comparison test.
    */
     
    #include <iostream>
    #include <cstring>
     
    int main()
    {
    using std::cin; using std::cout; using std::endl;
     
    const unsigned int LIMIT = 1001; //The limit of analyzable words
    char WordsToAnalyze[LIMIT] = {0}; //The array to store input text
    char Analyzer[LIMIT] = {0}; //The temporary array used for analyzing
    unsigned int NumberOfWords = 0; //The variable to keep track of number of words
     
    //Intro
    cout << "Welcome to WordCounter, char Edition!\n"
    << "Please enter the words, and use 'done' as the end-marker.\n"
    << "(maximum " << LIMIT - 1 << " characters.)" << endl;
    cin.getline(WordsToAnalyze,LIMIT);
     
    // <--- START WORD COUNTING --->
    /*
    - "done" as sentinel word.
    - StringPointer: set at 0, serves as the marker of the processed character in WordsToAnalyze;
    - LOOP: - Set CharPointer at 0, serves as the marker for Analyzer. Reset Analyzer.
    - LOOP: Add character by character to Analyzer until a space in encountered;
    - Increment NumberOfWords and StringPointer (to push it over the space);
    - Check if Analyzer is "done";
    - Repeat loop
    - Decrement NumberOfWords (because "done" must not be counted).
    */
    int StringPointer = 0; int CharPointer = 0;
    do
    {
    CharPointer = 0; //Reset CharPointer and Analyzer
    for ( WordsToAnalyze; WordsToAnalyze[StringPointer] != ' '; ++StringPointer)
    {
    Analyzer[CharPointer] = WordsToAnalyze[StringPointer];
    ++CharPointer;
    }
    ++NumberOfWords;
    ++StringPointer;
    Analyzer[CharPointer] = '\0';
    } while ( strcmp(Analyzer,"done") != 0 );
    NumberOfWords--;
    //Analyzer[0] = '\0'; WordsToAnalyze[0] = '\0';
    // <!--- END WORD COUNTING --->
     
    //REPORT
    cout << "That text has " << NumberOfWords << " words.";
    cin.clear(); cin.get();
    return 0;
    }

A little off-topic: A few days earlier I posted this problem in the C forum, and someone pointed out that this should be in the C++ forum instead, but I have no idea on how to 'move' a thread. The previous thread is here. Need a mod be contacted to do so?

Line 46, the first portion of the for loop statement doesn't do anything, you could move part of line 42 into that position and have for(int StringPointer = 0;WordsToAnalyze[StringPointer] != ' ';++StringPointer) but that StringPointer variable will be destroyed after the loop completes since it's declared locally, so leave 42 right where it is and put for(;WordsToAnalyze[StringPointer] != ' ';++StringPointer) The primary problem is that there is no space after the word "done" so your loop keeps running. Let your condition account for reaching a '\0'.

You should also check in 46 whether you have hit 1000 characters, since if you have already done so, incrementing again will go to array position 1001 which doesn't exist and line 53 will cause an error.

You want cin.ignore(); instead of cin.clear(); . clear is for when you are reading into a stream and you've encountered something unexpected, which makes the stream go "bad."

.

Some random advice:
I have used the CPP Primer Plus in the past and really, really like it. The examples are crystal clear and well-explained. Someone pointed out something important about it, that it starts out as teaching you C and then working your way "up" to C++. This is considered by some to be an older method, not a bad one, but one that doesn't touch on the powerful aspects of C++ right away. I would suggest a book like "Accelerated C++" by Koenig and Moo because it takes such an approach. I say that because it's good, and as an added bonus, the author participates in this forum.


Need a mod be contacted to do so?

Yes. Anytime you need something like that hit the "Flag Bad Post" button underneath your name on the left hand side of any post. It doesn't count against you, it just puts your concern into a moderator queue.

Edited 6 Years Ago by jonsca: n/a

Rather, let your loop check for reaching the end of the words too, rather than '\0' as I said above, as that would halt it prematurely. Play around with the conditions of your for loop.

Rather, let your loop check for reaching the end of the words too, rather than '\0' as I said above, as that would halt it prematurely. Play around with the conditions of your for loop.

O.O Thanks for pointing those out! I'm beginning to see the problems, and I'll fix it. But still, what are stack, corrupted stack, and access violation? Can you explain what do cin.clear and cin.ignore mean? I put cin.clear there thinking it will 'clean up' or 'flush' or 'reset' or... well, you know what i mean ^^", the input queue. Is cin.ignore used for those purposes?

Again, thanks for the quick reply!

Skim through http://en.wikipedia.org/wiki/Call_stack (there's a lot of info there but it gives you an idea of where the terminology is coming from. If you try to access an index of an array that is greater than arraysize - 1, you run the risk of trying to access an address that belongs to an adjacent piece of data, which can corrupt the memory.

Like I had mentioned before, if you're having your user enter integers and they enter the letter a, it befuddles the input stream, the stream "fails" and stops taking any more data until you .clear() it.

Using .ignore() will allow you to take in a certain number of characters that the system will throw away. Sometimes when entering data, you get a stray '\n' (newline) in the buffer. If the next command is a cin.get() then the newline will be taken in and the program will effectively skip the pause in your program.

Skim through http://en.wikipedia.org/wiki/Call_stack (there's a lot of info there but it gives you an idea of where the terminology is coming from. If you try to access an index of an array that is greater than arraysize - 1, you run the risk of trying to access an address that belongs to an adjacent piece of data, which can corrupt the memory.

Like I had mentioned before, if you're having your user enter integers and they enter the letter a, it befuddles the input stream, the stream "fails" and stops taking any more data until you .clear() it.

Using .ignore() will allow you to take in a certain number of characters that the system will throw away. Sometimes when entering data, you get a stray '\n' (newline) in the buffer. If the next command is a cin.get() then the newline will be taken in and the program will effectively skip the pause in your program.

I'm not very sure about stacks, but well, I'll let it go. Anyway:
Is there another way to delete all elements of an array, or 'reset' it? That was what I was trying to do with line 53...
Why did entering "word test done" successfully reports 'The text has 2 words'? Why did it worked?
What did you mean by 'check for reaching the end of words'? Isn't checking for spaces do the job?

Exams are coming soon, so I won't be working on this very often until the next two weeks are done with. I'll think of it in my breaks. Thanks for your time!

I'm not very sure about stacks

You can certainly ask more questions. I was just giving you a general source. It's not something I would worry about for now, though.

'check for reaching the end of words'

If you are putting in a space when you type done it's all taken care of, but if you don't you should add one in within your program to make sure it will stop (otherwise it runs to the end of the array (1001) no matter how many characters you put in).

You can certainly ask more questions. I was just giving you a general source. It's not something I would worry about for now, though.


If you are putting in a space when you type done it's all taken care of, but if you don't you should add one in within your program to make sure it will stop (otherwise it runs to the end of the array (1001) no matter how many characters you put in).

I've tried again today, this time using dynamic arrays but still it doesn't work >"< It's that 'access violation' error again, on line 22. Something is wrong with the condition and i don't know what it is.

#include <iostream>
#include <cstring>

int main()
{
	using std::cin; using std::cout; using std::endl;

	const unsigned int LIMIT = 1001;
	char inputText[LIMIT] = {0};
	char compareObject[LIMIT] = {0};
	unsigned int originalPointer = 0; unsigned int dynamicPointer = 0; unsigned int numberOfWords = 0;

	cout << "Enter words for me to count!\n"
		<< "I will stop counting when I read the word 'done',\n"
		<< "and I will only deal with the maximum of " << (LIMIT - 1) << " characters!" << endl;
	cin.getline(inputText,LIMIT);

	do
	{
		dynamicPointer = 0;
		char* wordAnalyzer = new char[LIMIT];
		while ( inputText[originalPointer] != ' ' )
		{
			wordAnalyzer[dynamicPointer] = inputText[originalPointer];
			++dynamicPointer; ++originalPointer;
		}
		++numberOfWords; ++originalPointer;
		strcpy(compareObject,wordAnalyzer);
		delete [] wordAnalyzer;
	} while ( strcmp(compareObject,"done") != 0, strcmp(compareObject,"done\0") != 0 );
	numberOfWords--;

	cout << "After counting, I see that you've entered a total of " << numberOfWords << ".";

	cin.ignore(); cin.get();
	return 0;
}

jonsca, you have used this book once, right? Did you do this exercise? If you did, how did you solve it? It seems like my solution is quite bumpy... do you have another approach to suggest?

The statement after the comma on line 30 does nothing, but you're onto the right idea. You need to add a null terminator onto wordAnalyzer. Then you can skip the strcpy and compare it directly. I don't think that strcpy will append a null character if the originating string doesn't have one, but I'm not intimately familiar with the <cstring> functions (I know for a fact that it does copy over the null character if one is present).

The only difference between your code and mine was that I appended the null character and did not go through the strcpy step.

Honestly, I think if you've come away with some appreciation for the character array as a basis for C-strings (and later you can compare them to std::strings) and understand why they are manipulated the way that they are, then you're probably fine.

Edited 5 Years Ago by jonsca: n/a

The statement after the comma on line 30 does nothing, but you're onto the right idea. You need to add a null terminator onto wordAnalyzer. Then you can skip the strcpy and compare it directly. I don't think that strcpy will append a null character if the originating string doesn't have one, but I'm not intimately familiar with the <cstring> functions (I know for a fact that it does copy over the null character if one is present).

The only difference between your code and mine was that I appended the null character and did not go through the strcpy step.

Honestly, I think if you've come away with some appreciation for the character array as a basis for C-strings (and later you can compare them to std::strings) and understand why they are manipulated the way that they are, then you're probably fine.

Do you mean like this?

do
	{
		dynamicPointer = 0;
		char* wordAnalyzer = new char[LIMIT];
		while ( inputText[originalPointer] != ' ' )
		{
			wordAnalyzer[dynamicPointer] = inputText[originalPointer];
			++dynamicPointer; ++originalPointer;
		}
		++numberOfWords; ++originalPointer;
		strcat(wordAnalyzer,"\0");
		//strcpy(compareObject,wordAnalyzer);
		delete [] wordAnalyzer;
	} while ( strcmp(compareObject,"done") != 0/*, strcmp(compareObject,"done\0") != 0*/ );
	numberOfWords--;

There is still this 'access violation' error pointing at the while ( inputText[originalPointer] != ' ' ) line, though >"<

I hope dealing with std::strings will be easier. These C stuff is a mess. I wouldn't have spent so much effort on them, but I want to finish what I started =.=

Then originalPointer is getting too large. What is in the array inputText? Not what you think is in it, but what actually is the value of every single element in the array? Print it out value by value, as integers, and make sure the last word in the array is set up properly for your testing loop.

Actually, another thing that I did was append a space after the last word of the input buffer, as if you type "done" and it's not followed by a space, the loop will run over the end, as WaltP has pointed out. I had brought this up a while back, but I think it got lost in the shuffle.

You can scan along your buffer and find the last letter and drop the space in.

I'm sorry for dragging this issue for too long, but I am getting confused >"< I tweaked the code a little more:

do
	{
		dynamicPointer = 0;
		char* wordAnalyzer = new char[LIMIT];
		while ( inputText[originalPointer] != ' ' )
		{
			wordAnalyzer[dynamicPointer] = inputText[originalPointer];
			wordAnalyzer[dynamicPointer + 1] = ' '; wordAnalyzer[dynamicPointer + 2] = '\0';
			++dynamicPointer; ++originalPointer;
		}
		++numberOfWords; ++originalPointer;
		//strcat(wordAnalyzer," \0");
		//strcpy(compareObject,wordAnalyzer);
		delete [] wordAnalyzer;
	} while ( strcmp(compareObject,"done") != 0/*, strcmp(compareObject,"done\0") != 0*/ );
	numberOfWords--;

When I entered "word tests done " (a space attached to the end), it says 'access violation', and in the 'Autos' thing in VS2010, originalPointer has the value of 3272! What happened?! Autos also says that in the inputText array "word tests done " is exactly what is stored there, with a space present in the end. The rest are 0's. wordAnalyzer stores nothing.

Then I stop debugging and start again, this time enter "word tst string done stuff", it also says 'access violation', originalPointer this time is 1476, inputText is exactly what is entered. wordAnalyzer stores "stuff".

This is where it gets confusing. What have I done to push the pointers to as far as 3272? All i did was increment it by one using the ++ operator...


I then got my old code in and compile. It's this one:

#include <iostream>
#include <cstring>
     
int main()
{
    using std::cin; using std::cout; using std::endl;
     
    const unsigned int LIMIT = 1001; //The limit of analyzable words
    char WordsToAnalyze[LIMIT] = {0}; //The array to store input text
    char Analyzer[LIMIT] = {0}; //The temporary array used for analyzing
    unsigned int NumberOfWords = 0; //The variable to keep track of number of words
     

    cout << "Welcome to WordCounter, char Edition!\n"
    << "Please enter the words, and use 'done' as the end-marker.\n"
    << "(maximum " << LIMIT - 1 << " characters.)" << endl;
    cin.getline(WordsToAnalyze,LIMIT);
     
    int StringPointer = 0; int CharPointer = 0;
    do
    {
		CharPointer = 0; //Reset CharPointer and Analyzer
		for ( WordsToAnalyze; WordsToAnalyze[StringPointer] != ' '; ++StringPointer)
		{
			Analyzer[CharPointer] = WordsToAnalyze[StringPointer];
			++CharPointer;
		}
		++NumberOfWords;
		++StringPointer;
		Analyzer[CharPointer] = '\0';
    } while ( strcmp(Analyzer,"done") != 0 );
    NumberOfWords--;
    //Analyzer[0] = '\0'; WordsToAnalyze[0] = '\0';

    cout << "That text has " << NumberOfWords << " words.";
    cin.clear(); cin.get();
    return 0;
}

The only time it encounters 'access violation' error is ending input with "done", like "word tests done". Attaching another word after "done" like "word tests done stuff" or manually enter "done " (with space) like "word tests done " yields correct answers, and the program completes successfully.

One last thing. Looking at the example's output:

Enter words (to stop, type the word done):
[B]anteater birthday category dumpster[/B]
[B]envy finagle geometry done for sure[/B]
You entered a total of 7 words.

It seems like the first line "anteater birthday category dumpster" was entered with a return key, then the second line "envy finagle geometry done for sure", then return key. It doesn't seem to process the whole thing at once, but keeps fetching off the input queue until "done" is found...

Perhaps I misunderstood the exercise's requirement after all? ... Oh man...

Start outputting values to the screen.
Watch your loop value as it increases.
Watch your word value as it's tested.
Watch the character being looked at during each loop.
Run through the loop one character at a time (pause at end) and make sure all the values are as expected.

for ( WordsToAnalyze; WordsToAnalyze[StringPointer] != ' '; ++StringPointer) You went back to this again, and the syntax is not correct.

I agree with WaltP, put some debugging code in there to see what's in each of your arrays at any given time (simply print them out character by character).

Don't get mired down by one exercise so much that you give up your quest, though. It never hurts to take a step back and look at the big picture, IMO.

Edited 5 Years Ago by jonsca: n/a

for ( WordsToAnalyze; WordsToAnalyze[StringPointer] != ' '; ++StringPointer) You went back to this again, and the syntax is not correct.

I agree with WaltP, put some debugging code in there to see what's in each of your arrays at any given time (simply print them out character by character).

Don't get mired down by one exercise so much that you give up your quest, though. It never hurts to take a step back and look at the big picture, IMO.

@Jon:Sorry ^^" I opened the old .cpp and copied-pasted it here wihout fixing it. I was just trying to say that the old one somehow works.

@Walt: Hey, putting in some "debugging code" seems like a neat idea <3 !! Just tried putting some cout's in there and now I know what you're talking about :D Now I'll try putting something to pause it each loop instance...

It seems like something is wrong with the condition of the while loop. Hmm. And it has some sense of humor, too. It goes as far as putting smilies in the array. Ugh.

BRAVO~~~!!! It is fixed!

The problem is in the test condition of the main do-while loop. In the loop body, wordAnalyzer is modified from " done " to " done \0 " (added a space and a null terminator), so the conddition strcmp(compareObject,"done") != 0 is always true, so the loop keeps running forever and putting things into it, and yeah, even smilies.

Followed is the complete code, with the do-while loop's condition polished up a bit. Most of the 'debug codes' are now in comments:

/*
Write a program that uses an array of char and a loop to read one word at a time until
the word done is entered. The program should then report the number of words entered
(not counting done). A sample run could look like this:
Enter words (to stop, type the word done):
anteater birthday category dumpster
envy finagle geometry done for sure
You entered a total of 7 words.
You should include the cstring header file and use the strcmp() function to make the
comparison test.
*/

#include <iostream>
#include <cstring>

int main()
{
	using std::cin; using std::cout; using std::endl;

	const unsigned int LIMIT = 1001; //The limit of analyzable characters
	unsigned int numberOfWords = 0;

	cout << "Enter words for me to count!\n"
		<< "I will stop counting when I read the word 'done',\n"
		<< "and I will only deal with the maximum of " << (LIMIT - 1) << " characters!" << endl;
	char inputText[LIMIT] = {0};
	cin.getline(inputText,LIMIT);

	// <--- START WORD COUNTING --->

	char compareObject[LIMIT] = {0}; //The array serves as compare object for the comparison test
	int originalPointer = 0; //The pointer to mark the analyzed character in inputText
	int dynamicPointer = 0; //The pointer to mark the analyzed character in wordAnalyzer
	const char exitKey[6] = "done ";

	//Algorithm description:
	// <-- Start Loop -->
	// - Reset dynamicPointer;
	// - Create a dynamic array wordAnalyzer;
	// - Process inputText character by character, put them in wordAnalyzer plus a space and a \0
	//	 until a space is encountered;
	// - Increment numberOfWords, increment originalPointer to push it over the space;
	// - Copy wordAnalyzer to compareObject and puts wordAnalyzer into oblivion;
	// - Is compareObject "done "? If not, repeat the whole loop.
	// <-- End Loop -->
	//
	// decrease numberOfWords by 1 ('not counting done') and report.

	do
	{
		dynamicPointer = 0;
		char* wordAnalyzer = new char[LIMIT];

		while ( inputText[originalPointer] != ' ' )
		{
			wordAnalyzer[dynamicPointer] = inputText[originalPointer];

			//cout << endl;
			//cout << "dynamicPointer: " << dynamicPointer << endl
			//	<< "originalPointer: " << originalPointer << endl;
			//cout << "wordAnalyzer[" << dynamicPointer << "]: " << wordAnalyzer[dynamicPointer] << endl
			//	<< "inputText[" << originalPointer << "]: " << inputText[originalPointer] << endl;
			//cin.get();

			wordAnalyzer[dynamicPointer + 1] = ' '; wordAnalyzer[dynamicPointer + 2] = '\0';
			++dynamicPointer; ++originalPointer;
		}

		//cout << endl << "while loop done";
		++numberOfWords; ++originalPointer;
		//cout << endl << "numberOfWords: " << numberOfWords << endl;

		strcpy(compareObject,wordAnalyzer);
		//cout << "wordAnalyzer: \"" << wordAnalyzer << "\"" << endl;
		//cout << "compareObject: \"" << compareObject << "\"" << endl;
		//cin.get();

		delete [] wordAnalyzer;
	} while ( strcmp(compareObject,exitKey) != 0 );
	numberOfWords--;
        // <--- END WORD COUNTING --->

	cout << "After counting, I see that you've entered a total of " << numberOfWords << ".";

	cin.ignore(); cin.get();
	return 0;
}

Thanks to Jon and Walt <3 !! Love you guys :D

Edited 5 Years Ago by tdba.316: code polish

After beseeching the heavens for answering me why the book requires the readers (who are no doubt with little experience, or they wouldn't have picked a primer) to solve problems this complicated this early, the answer came to me.

http://www.informit.com/store/product.aspx?isbn=0672326973 <- Check the Downloads tab.

The solution is ridiculously simple =.= :

// pe5-7.cpp -- count words using C-style string
    
#include <iostream>
#include <cstring>     // prototype for strcmp()
const int STR_LIM = 50;
int main()
{
    using namespace std;
    char word[STR_LIM];
    int count = 0;
    
    cout << "Enter words (to stop, type the word done):\n";
    
    while (cin >> word && strcmp("done", word))
        ++count;

    cout << "You entered a total of " << count << " words.\n";
    return 0; 
}

I hope this will be helpful for others in the future =)

This question has already been answered. Start a new discussion instead.