开发者

Loop efficiency - C++

Beginners question, on loop efficiency. I've started programming in C++ (my first language) and have been using 'Principles and Practice Using C++' by Bjarne Stroustrup. I've been making my way through the earlier chapters and have just been introduced to the concept of loops.

The first exercise regarding loops asks of me the following: The character 'b' is char('a'+1), 'c' is char('a'+2), etc. Use a loop to write out a table of characters with their corresponding integer values:

a 97, b 98, ..., z 122

Although, I used uppercase, I created the following:

int number = 64; //integer value for @ sign, character before A
char letter = number;//converts integer to char value
int i = 0;

while (i<=25){
    cou开发者_StackOverflow中文版t << ++letter << "\t" << ++number << endl;
    ++i;
    }

Should I aim for only having 'i' be present in a loop or is it simply not possible when converting between types? I can't really think of any other way the above can be done apart from having the character value being converted to it's integer counterpart(i.e. opposite of current method) or simply not having the conversion at all and have letter store '@'.


Following on from jk you could even use the letter itself in the loop (letter <= 'z'). I'd also use a for loop but that's just me.

for( char letter = 'a'; letter <= 'z'; ++letter )
    std::cout << letter << "\t" << static_cast<int>( letter ) << std::endl;


You should aim for clarity first and you try to micro-optimize instead. You could better rewrite that as a for loop:

const int offsetToA = 65;
const int numberOfCharacters = 26;
for( int i = 0; i < numberOfCharacters; ++i ) {
    const int characterValue = i + offsetToA;
    cout << static_cast<char>( characterValue  ) << characterValue << endl;
}

and you can convert between different types - that's called casting (the static_cast construct in the code above).


That's not a bad way to do it, but you can do it with only one loop variable like this:

char letter = 65;

while(letter <= 65+25){
  printf("%c\t%d\n", letter, letter);
  ++letter;
}


there is nothing particularly inefficient about the way you are doing it but it certainly is possible to just convert between chars and ints (a char is an integer type). this would mean you only need to store 1 counter rather than the 3 (i, letter + number) you curently have

also, for looping from a fixed start to end a 'for' loop is perhaps more idiomatic (though its possible you havent met this yet!)


If you are concerned about the efficiency of your loop, I would urge you to try this:

Get this code compiled and running under an IDE, such as Visual Studio, and set a break point at the beginning. When you get there, switch to the disassembly view (instruction view) and start hitting the F11 (single-step) key, and keep a mental count of how many times you are hitting it.

You will see that it enters the loop, compares i against 25, and then starts doing the code for the cout line. That involves incrementing letter, and then going into the << routine for cout. It does a number of things in there, possibly going deeper into subroutines, etc., and finally comes back out, returning an object. Then it pushes "\t" as an argument and passes it to that object, and goes back in and does all the stuff it did before. Then it takes number, increments it, and passes it to the cout::<< routine that accepts an integer, calls a function to convert it to a string (which involves a loop), then does all the stuff it did before to loop that string into the output buffer and return.

Tired? You're not done yet. The endl has to be output, and when that happens, not only does it put "\n" in the buffer, but it calls the system routine to flush that buffer to the file or console where you are sending the I/O. You probably can't F11 into that, but rest assured it takes lots of cycles and doesn't return until the I/O is done.

By now, your F11-count should be in the vicinity of several thousand, more or less.

Finally, you come out and get to the ++i statement, which takes 1 or 2 instructions, and jumps back to the top of the loop to start the next iteration.

NOW, are you still worried about the efficiency of the loop?


There's an easier way to make this point, and it's just as instructive. Wrap an infinite loop around your entire code so it runs forever. While it's running, hit the "pause" button in the IDE, and look at the call stack. (This is called a "stackshot".) If you do this several times you get a good idea of how it spends time. Here's an example:

NTDLL! 7c90e514()
KERNEL32! 7c81cbfe()
KERNEL32! 7c81cc75()
KERNEL32! 7c81cc89()
MSVCRTD! 1021bed3()
MSVCRTD! 1021bd59()
MSVCRTD! 10218833()
MSVCRTD! 1023a500()
std::_Fputc() line 42 + 18 bytes
std::basic_filebuf<char,std::char_traits<char> >::overflow() line 108 + 25 bytes
std::basic_streambuf<char,std::char_traits<char> >::sputc() line 85 + 94 bytes
std::ostreambuf_iterator<char,std::char_traits<char> >::operator=() line 304 + 24 bytes
std::num_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::_Putc() line 633 + 32 bytes
std::num_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::_Iput() line 615 + 25 bytes
std::num_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::do_put() line 481 + 71 bytes
std::num_put<char,std::ostreambuf_iterator<char,std::char_traits<char> > >::put() line 444 + 44 bytes
std::basic_ostream<char,std::char_traits<char> >::operator<<() line 115 + 114 bytes
main() line 43 + 96 bytes
mainCRTStartup() line 338 + 17 bytes

I did this a bunch of times, and not ONCE did it stop in the code for the outer i<=25 loop. So optimizing that loop is like someone's great metaphor: "getting a haircut to lose weight".


Since no one else mentioned it: Having a fixed amount of iterations, this is also a candidate for post-condition iteration with do..while.

char letter = 'a';
do {
    std::cout << letter << "\t" << static_cast<int>( letter ) << std::endl;
} while ( ++letter <= 'z' );

However, as shown in Patrick's answer the for idiom is often shorter (in number of lines in this case).


You can promote char to int...

//characters and their corresponding integer values
#include"../../std_lib_facilities.h"

int main()
{
    char a = 'a';
    while(a<='z'){
        cout<<a<<'\t'<<a*1<<'\n'; //a*1 => char operand promoted to integer!
        ++a;
    }

    cout<<endl;
}


Incrementing three separate variables is probably a little confusing. Here's a possibility:

for (int i = 0; i != 26; ++i)
{
    int chr = 'a' + i;
    std::cout << static_cast<char>(chr) << ":\t" << chr << std::endl;
}

Note that using a for loop keeps all the logic of setting up, testing and incrementing the loop variable in one place.


At this point, I wouldn't worry about micro-optimizations such as an efficient way to write a small loop like this. What you have allows a for loop to do the job nicely, but if you are more comfortable with while, you should use that. But I am not sure if that is your question.

I don't think you have understood the question properly. You are writing the code, knowing that 'A' is 65. The whole point of the exercise is to print the value of 'A' to 'Z' on your system, without knowing what value they have.

Now, to get an integer value for a character c, you can do: static_cast<int>(c). I believe that is what you're asking.

I haven't written any code because it should be more fun for you to do so.

Question for the experts: In C, I know that 'a'...'z' need not have continuous values (same for 'A'...'Z'). Is the same true for C++? I would think so, but then it seems highly unlikely that Stroustrup's book assumes that.


thanks for the help.. all i wrote down was

int main() 
{
char letter = 96;
int number = letter;
int i = 0;

while(i <26)
{
cout <<++letter <<":" <<++numbers <<"  ";
++i;
}

works great...and pretty simple to understand now.


I've tried this and worked fine:

char a = 'a';
int i = a; //represent char a as an int
while (a <= 'z') {
cout << a << '\t' << i << '\n';
++a;
++i;
}


Programming Principles and Practice using C++ (2nd Edition) | Bjarne Stroustrup

Chapter 4 - Computation (Try this #3 - Character Loop)

The character 'b' is char('a'+1), 'c' is char('a'+2), etc. Use a loop to write out a table of characters with their corresponding integer values: a 97 b 98 . . . z 122

This is how I solved the problem (from 10 years ago :D)

I am a freshmen btw, so I just started reading this book now... just want to input my solution

#include <iostream>

using namespace std;

int main()
{
    int i = 0; 
    while (i < 26) {
        cout << char('a' + i) << '\t' << int(97 + i) << '\n';
        ++i;        
    }       
}

I solved it by analyzing first the problem which is knowing the char value of 'a' which is 97 up to 'z'. According to this ASCII table

https://www.ascii-code.com/#:~:text=ASCII%20printable%20characters%20%28character%20code%2032-127%29%20Codes%2032-127,digits%2C%20punctuation%20marks%2C%20and%20a%20few%20miscellaneous%20symbols.

Now, we have a clearer understanding on how to solve the said problem.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜