-
A question regarding *s++
Suppose I define char* s, then I call *s++. I guess it should do ++ first and then dereference. But look at a very typical code,
Code:
char* s = "Hello";
char* s2 = new char[100];
char* p = s2;
while(*s)
{
*p++ = *s++;
}
*p = '\0';
Look at the statement *p++ = *s++, obviously p is deferenced first and then it is incremented. Is there anything I missed here? Thanks.
-
Re: A question regarding *s++
It is actually incremented first. However, note that the postfix version of ++ is used, which means that while s itself is incremented, the return value of the operator is the old value of s.
That is,
Code:
char *s = "Hello";
// *s == 'H'
char *t = s++;
// *t == 'H', *s == 'e'
*t = 'h'
// now t points to "hello". Well, it doesn't, since modifying a string literal will probably crash the program, but it would if the Hello array had been in modifiable memory to begin with.
This behavior is the reason why prefix increment, ++s, should be preferred unless you have a specific need for the postfix version. It's not only less confusing, it can in some cases be more efficient.
-
Re: A question regarding *s++
Thanks for your explainations.
Quote:
Originally Posted by
Lindley
It is actually incremented first. However, note that the
postfix version of ++ is used, which means that while s itself is incremented, the return value of the operator is the
old value of s.
That is,
Code:
char *s = "Hello";
// *s == 'H'
char *t = s++;
// *t == 'H', *s == 'e'
*t = 'h'
// now t points to "hello". Well, it doesn't, since modifying a string literal will probably crash the program, but it would if the Hello array had been in modifiable memory to begin with.
This behavior is the reason why prefix increment, ++s, should be preferred unless you have a specific need for the postfix version. It's not only less confusing, it can in some cases be more efficient.
-
Re: A question regarding *s++
Quote:
Originally Posted by
Lindley
This behavior is the reason why prefix increment, ++s, should be preferred unless you have a specific need for the postfix version. It's not only less confusing, it can in some cases be more efficient.
Ok, now that we have a thread about that specific topic, I can ask this... :cool:
From plain old C I'm used to prefer the post-increment version unless it is specifically required otherwise, simply because it looks more familiar to me.
Am I right to assume that it wouldn't make a difference in terms of efficiency in C++ as long as it's about POD types?
It looks reasonable to me that I get a performance penalty if I use post-increment on non-POD types and I somehow process the result of that operation, because it is likely to require creation of a temporary object. But does it really make any difference if I don't use the result of the increment operation in further processing? :ehh:
-
Re: A question regarding *s++
Quote:
Originally Posted by
Eri523
Ok, now that we have a thread about that specific topic, I can ask this... :cool:
From plain old C I'm used to prefer the post-increment version unless it is specifically required otherwise, simply because it looks more familiar to me.
Am I right to assume that it wouldn't make a difference in terms of efficiency in C++ as long as it's about POD types?
It looks reasonable to me that I get a performance penalty if I use post-increment on non-POD types and I somehow process the result of that operation, because it is likely to require creation of a temporary object. But does it really make any difference if I don't use the result of the increment operation in further processing? :ehh:
In a single sentence, "postfix is never faster, and sometimes slower".
Under these conditions, there really is no reason to use postfix unless you need to.
As for your question of taste ("prefering postfix over prefix"), if you start getting experience in C++, or work with people who have a C++ back ground, you will notice that prefix is the preferred way, and postfix will start looking weird.
PS: The compiler may or may not optimize away and performance penalty, but why take the chance?
-
Re: A question regarding *s++
Quote:
Originally Posted by
Eri523
Ok, now that we have a thread about that specific topic, I can ask this... :cool:
From plain old C I'm used to prefer the post-increment version unless it is specifically required otherwise, simply because it looks more familiar to me.
Am I right to assume that it wouldn't make a difference in terms of efficiency in C++ as long as it's about POD types?
It looks reasonable to me that I get a performance penalty if I use post-increment on non-POD types and I somehow process the result of that operation, because it is likely to require creation of a temporary object. But does it really make any difference if I don't use the result of the increment operation in further processing? :ehh:
The main argument for me to not using post-increment on class types isn't the possible lack of efficiency but that the behavior is different to that on POD types. Look at the following code (which was a C sample code in Stroustrup's Annotated C++ Reference)
Code:
while (*p++ = *q++);
When p and q are char arrays the above is a perfect strcpy. When p and q are class types there is no way to let the post-increment on the right-hand happen after the assignment.
If you then see that increment operation on class types mainly is done for containers and their iterators you probably will decide for prefix increment same as me and I promise that it will look 'more familiar' to you within a very short period ;-)
Regards, Alex
-
Re: A question regarding *s++
Quote:
Originally Posted by itsmeandnobodyelse
When p and q are class types there is no way to let the post-increment on the right-hand happen after the assignment.
Sorry, but I do not quite understand the problem that you are presenting. Now, order of evaluation is implementation defined, but here it seems quite clear that the dereference of q must happen before its post-increment. Therefore, how does it matter if the assignment happens before or after the post-increment? With the normally expected semantics, the net effect should be the same, i.e., similiar to "a perfect strcpy".
-
Re: A question regarding *s++
Quote:
Originally Posted by
laserlight
Sorry, but I do not quite understand the problem that you are presenting. Now, order of evaluation is implementation defined, but here it seems quite clear that the dereference of q must happen before its post-increment. Therefore, how does it matter if the assignment happens before or after the post-increment? With the normally expected semantics, the net effect should be the same, i.e., similiar to "a perfect strcpy".
Assume
Code:
char p[20] = { '\0' };
char * q = "Hello";
while (*p++ = *q++);
The above does:
Code:
p[0] = 'H';
p[1] = 'e';
p[2] = 'l';
p[3] = 'l';
p[4] = 'o';
p[5] = '\0';
If you do the same with a string class
Code:
MyString q = "Hello";
while (*p++ = *q++);
which has pointer semantics and overloaded operators for derefence, assignment and post-increment
Code:
p[0] = 'e';
p[1] = 'l';
p[2] = 'l';
p[3] = 'o';
p[4]= '\0';
There is no way to happen the assigmnent *p = *q prior to the post-increment operation of q++.
Regards, Alex
-
Re: A question regarding *s++
itsmeandnobodyelse: please provide the smallest and simplest compilable program that demonstrates what you claim.
EDIT:
This program appears to contradict your claims:
Code:
#include <iostream>
#include <deque>
class X
{
public:
explicit X(int n) : n(n) {}
// Sorry, I'm too lazy to use safe bool idiom,
// and explicit conversion functions might not yet be available to you.
operator bool() const
{
return n != 0;
}
int get() const
{
return n;
}
private:
int n;
};
int main()
{
using namespace std;
deque<X> one;
for (int i = 1; i <= 10; ++i)
{
one.push_back(X(i));
}
one.push_back(X(0));
deque<X> two(one.size(), X(-1));
deque<X>::iterator p = two.begin();
deque<X>::iterator q = one.begin();
while (*p++ = *q++);
for (deque<X>::size_type i = 0, size = two.size(); i < size; ++i)
{
cout << two[i].get() << endl;
}
}
The output I get is:
Code:
1
2
3
4
5
6
7
8
9
10
0
-
Re: A question regarding *s++
Quote:
Originally Posted by
laserlight
Sorry, but I do not quite understand the problem that you are presenting. Now, order of evaluation is implementation defined, but here it seems quite clear that the dereference of q must happen before its post-increment. Therefore, how does it matter if the assignment happens before or after the post-increment? With the normally expected semantics, the net effect should be the same, i.e., similiar to "a perfect strcpy".
I'm being pedantic, but since you rarely make a mistake, I thought I'd chip in here... the part in bold is not correct. The precedence of the post increment and post decrement operators is higher than the precedence of the dereference operator. Therefore for an ISO Standard compliant compiler the post increment must happen before the dereference.;)
-
Re: A question regarding *s++
Quote:
Originally Posted by PredicateNormative
I'm being pedantic, but since you rarely make a mistake, I thought I'd chip in here... the part in bold is not correct. The precedence of the post increment and post decrement operators is higher than the precedence of the dereference operator. Therefore for an ISO Standard compliant compiler the post increment must happen before the dereference.
No worries, we need to be pedantic here. Yes, your observation is correct. Consequently, itsmeandnobodyelse's same assertion is correct. However, itsmeandnobodyelse's assertion about there being a problem is wrong, assuming that the implementation follows normal semantics. The reason for my mistake is that I am thinking of the result of p++ being the original p. Therefore, *p will still dereference the value of the original p, unless for some reason postfix operator++ is implemented to return the modified p, which goes against normal semantics.
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
There is no way to happen the assigmnent *p = *q prior to the post-increment operation of q++.
Regards, Alex
Maybe you are confusing evaluation order?
Regardless of type, when you write:
Both p++ and q++ are evaluated first (but in undefined relative order).
Once this is done, operator= is called on the return values of p++ and q++, ie, the old values of p and q. (making it look like the assignment took place before increment)
There is no special magic or rules that applies only to PODs that do the assignments first, and then increments the values of p or q.
...
Either that, or I seem to not understand what you are saying. Your example (rewritten since incomplete) seems to work correctly:
Code:
#include <iostream>
#include <string>
int main()
{
char pArray[20] = { '\0' };
char* p = pArray;
std::string helloStr = "Hello";
//force the null terminator into string //Undefined behavior, but defined in practice
helloStr.c_str();
std::string::iterator q = helloStr.begin();
while (*p++ = *q++); //note that this reads string.end(), but should point '\0'. May crash in ranged debug
std::cout << pArray << std::endl;
}
Was this your full example?
Regards,
-
Re: A question regarding *s++
Quote:
Originally Posted by
laserlight
No worries, we need to be pedantic here. Yes, your observation is correct. Consequently, itsmeandnobodyelse's same assertion is correct. However, itsmeandnobodyelse's assertion about there being a problem is wrong, assuming that the implementation follows normal semantics. The reason for my mistake is that I am thinking of the result of p++ being the original p. Therefore, *p will still dereference the value of the original p, unless for some reason postfix operator++ is implemented to return the modified p, which goes against normal semantics.
It was only that little bit about precedence I highlighted in bold that I disagreed with, I most certainly agree with everything else that you are saying about itsmeandnobodyelse assertion.
It would seem to me there is something wrong with the post-increment operator implementation in itsmeandnobodyelse's MyString class.
-
Re: A question regarding *s++
Good rule of thumb is unless you absolutely have to have the original value of the value, use preincrement. I really irks me when I see things like this:
Code:
for(i = 0; i < 10; i++)
Grr.
For primitives it doesn't matter, the compiler will be smart enough to fix it for you, but if you do things like that, you get into the habit of it, and habits are hard to break.
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
Assume
Code:
char p[20] = { '\0' };
char * q = "Hello";
while (*p++ = *q++);
I doubt that this would even compile: You are trying to increment an array. ;)
-
Re: A question regarding *s++
Quote:
Originally Posted by
monarch_dodra
Maybe you are confusing evaluation order?
Regardless of type, when you write:
Both p++ and q++ are evaluated first (but in undefined relative order).
Once this is done, operator= is called on the
return values of p++ and q++, ie, the old values of p and q. (making it look like the assignment took place before increment)
There is no special magic or rules that applies only to PODs that do the assignments first,
and then increments the values of p or q.
...
Either that, or I seem to not understand what you are saying. Your example (rewritten since incomplete) seems to work correctly:
Code:
#include <iostream>
#include <string>
int main()
{
char pArray[20] = { '\0' };
char* p = pArray;
std::string helloStr = "Hello";
//force the null terminator into string //Undefined behavior, but defined in practice
helloStr.c_str();
std::string::iterator q = helloStr.begin();
while (*p++ = *q++); //note that this reads string.end(), but should point '\0'. May crash in ranged debug
std::cout << pArray << std::endl;
}
Was this your full example?
Regards,
No, it was from my memories ;-)
Here is the code that compiles:
Code:
#include <iostream>
int main()
{
char * q = "Hello";
char buf[20] = { '\0' };
char * p = buf;
while (*p++ = *q++);
std::cout << buf << std::endl;
return 0;
}
If incrementation would happen before assignment the p[0] would not be set correctly.
I will add the string class example soonly.
Regards, Alex
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
If incrementation would happen before assignment the p[0] would not be set correctly.
Sure it would, given the post-increment semantics which are at the heart of what we're discussing.
-
Re: A question regarding *s++
Here the full sample
Code:
#include<iostream>
class String
{
char * ps;
int siz;
int len;
public:
String(const char * psz)
{
siz = strlen(psz)+1;
ps = new char[siz];
strcpy(ps, psz);
}
~String() { delete []ps; }
const char * c_str() { return ps; }
friend class StringIterator;
};
class StringIterator
{
int pos;
String * s;
public:
StringIterator(String & str) : s(&str), pos(0) {}
char & operator*() { return s->ps[pos]; }
StringIterator& operator++(int)
{
pos++;
return *this;
}
};
int main()
{
char * q = "Hello";
char buf[20] = { '\0' };
char * p = buf;
while (*p++ = *q++);
std::cout << buf << std::endl;
String s("World");
String t(" ");
StringIterator i(s);
StringIterator j(t);
while (*j++ = *i++);
std::cout << t.c_str() << std::endl;
return 0;
}
And output is
Hello
orld
Regards, Alex
P.S.
I don't think the above is a real problem. However, it was that what invoked me to not using post-increment on class types anymore.
-
Re: A question regarding *s++
Quote:
Originally Posted by
Lindley
Sure it would, given the post-increment semantics which are at the heart of what we're discussing.
Quote:
Originally Posted by
itsmeandnobodyelse
StringIterator& operator++(int)
{
pos++;
return *this;
}
The real problem is that you implemented post-increment as pre-increment, breaking standard conventions and assumptions.
I'm pretty sure 4 or 5 people will tell you the same in a 5 minute time span.
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
Here the full sample
A correct implementation of post-increment would be:
Code:
StringIterator operator++(int)
{
StringIterator copy(*this);
pos++;
return copy;
}
This would give the correct result. However, the expense of the copy is the reason why post-increment is sometimes slower.
-
Re: A question regarding *s++
Quote:
Originally Posted by
Lindley
A correct implementation of post-increment would be:
Code:
StringIterator operator++(int)
{
StringIterator copy(*this);
pos++;
return copy;
}
This would give the correct result. However, the expense of the copy is the reason why post-increment is sometimes slower.
Ok, looking at that code makes it clear why the compiler couldn't optimize away the additional overhead (under normal circumstances), even if I don't use the returned temporary object.
But could it do that if the implementation of the operator would be inlined? :rolleyes:
-
Re: A question regarding *s++
Personally, I would replace the following incorrectly implemented post-increment operator:
Code:
StringIterator& operator++(int)
{
pos++;
return *this;
}
with
Code:
//pre-increment: ++i
StringIterator& operator++()
{
++pos;
return *this;
}
//post-increment: i++
StringIterator operator++()
{
StringIterator orig(*this);
++(*this);
return orig;
}
EDIT:
But I would like to ask the question... why are you implementing a string type anyway? Why not use std::string?
-
Re: A question regarding *s++
Quote:
Originally Posted by
monarch_dodra
The real problem is that you implemented post-increment as pre-increment, breaking standard conventions and assumptions.
I'm pretty sure 4 or 5 people will tell you the same in a 5 minute time span.
I thank you very much for pointing out my error. It was a very old issue (maybe 16 years old) and I am very glad that I now know the solution.
Quote:
But I would like to ask the question... why are you implementing a string type anyway? Why not use std::string?
I am using std::string since 2003. The various string classes I made are all older than 10 years where STL wasn't available for all compilers or were not allowed to be used in projects.
Private string classes can have a much better functionality than std::string. Especially for streaming, editable substrings and codeset support. The (only?) disadvantage is that they were not available with the compiler and must be installed (and be allowed) at each new platform.
Regards, Alex
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
The (only?) disadvantage is that they were not available with the compiler and must be installed (and be allowed) at each new platform.
Another disadvantage is that std::string is standard whereas your custom string class is not. A new team member wouldn't have to spend any time learning the 'Alex String Library'. He/she could instead be using a well documented, well tested, well known library. If he/she doesn't already know std::string, he/she could learn it & know that the knowledge will still be relevant if he/she changed companies (unlike effort spent to learn alex::string). When there's a bug they can be 99% sure its not a bug in the string class (although there seems to be no shortage of programmers that blame the compiler). They could post questions on programming forums about how to use std::string but not alex::string.
-
Re: A question regarding *s++
Quote:
Originally Posted by
Martin O
Another disadvantage is that std::string is standard whereas your custom string class is not. A new team member wouldn't have to spend any time learning the 'Alex String Library'. He/she could instead be using a well documented, well tested, well known library. If he/she doesn't already know std::string, he/she could learn it & know that the knowledge will still be relevant if he/she changed companies (unlike effort spent to learn alex::string). When there's a bug they can be 99% sure its not a bug in the string class (although there seems to be no shortage of programmers that blame the compiler). They could post questions on programming forums about how to use std::string but not alex::string.
That isn't so much a problem. Actually I added all standard member functions to my own string class just to meet that requirement. You should also see that the source code of existing STL implementations is without any comments and ugly to hell. So, debugging my own string class is much less frustrating than debugging into the basic_string. Did you ever debug into the 'traits' and didn't get lost somewhere? Same applies to documentation of basic_string which I wouldn't name 'well-documented'. IMO is really bad readable mostly due to the fact that they made it a template class which adds a complexity to the implementation and documentation that is unnecessary for everyone who knows which char type he was using.
Also I don't share the idea that private implementations of a library can't have the same quality than a standard library. Actually I experienced more bugs and shortcomings of the various implementations of STL as it was with my own libraries which didn't change for years and are well-proved in many projects. For example all my containers were thread-safe and have no problems to be passed via dll boundaries. Last is, there was no alternative in the years before C++ standard ;-)
Regards, Alex
P.S.
This wasn't a pleading for self-made libraries or for not using STL. On the contrary, I am a consequent user of those standards wherever it is possible. But I wasn't afraid of making or using my own libraries if it was required by the project or by the customer. And the basic_string class actually is a poor string class.
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
You should also see that the source code of existing STL implementations is without any comments and ugly to hell.
Yes, STL implementations are typically geared towards getting the most out of the compiler they're written for. They are not intended to be readable directly.
Quote:
So, debugging my own string class is much less frustrating than debugging into the basic_string. Did you ever debug into the 'traits' and didn't get lost somewhere?
No, there's no need to debug into basic_string because it doesn't have any significant bugs at this point. If the debugger tries to step into it, you just "Step out" and then "step in" again to make another try at getting where you intended to go.
Any bug which seems to be in basic_string is simply the programmer's misuse or corruption of the object.
Quote:
Same applies to documentation of basic_string which I wouldn't name 'well-documented'.
Eh, it's just fine for everything I need....
http://www.cplusplus.com/reference/string/
Quote:
IMO is really bad readable mostly due to the fact that they made it a template class which adds a complexity to the implementation and documentation that is unnecessary for everyone who knows which char type he was using.
Since basic_string is rarely instantiated with anything other than char or wchar_t, an argument could be made that it didn't have to be a template class. But I still think the template approach is superior to the CString solution, which is to have the observable class internals change depending on a preprocessor flag. That's just a bit nuts IMO.
-
Re: A question regarding *s++
I'm all for std::string, for the above reasons (Standard+already debugged).
IMO, there is nothing wrong with templates. Once you get used to it, it really doesn't make any difference.
Besides, a template form is needed for wstring...
I DO think that std::string is a bit lacking in the interface/usability department.
Sure, you can always get your work done by combining algorithms, search_first_of and erase/remove, but is it so much to ask for a "trim"?
-
Re: A question regarding *s++
Ah, but the great thing about the design of the STL----including, for the most part, std::string----is that it's easily extensible.
That's why we have options like the Boost String Algorithms library. Which does, by the way, include trim().
-
Re: A question regarding *s++
Quote:
Originally Posted by
Lindley
Ah, but the great thing about the design of the STL----including, for the most part, std::string----is that it's easily extensible.
That's why we have options like the
Boost String Algorithms library. Which does, by the way, include trim().
Really? You can't safely derive from it, can you? You can't add new member functions therefore, right?
Enhancements via function templates hardly can be used for praising a class design. On the contrary, they are global functions and I share the opinion that global functions mostly is poor OOP and the need for those functions show the heavy shortcomings of this class.
Even my very first string class in the early 90's had better functionality and the missing 'trim' was only one of many evidences that they had many problems to integrate string semantics like 'whitespace', 'words', 'sentences', and more to their string class which IMO is not much more then a container for char types - where even a vector<char> has some advantages.
Or take the case where you need to convert from wstring to string or vice versa or let alone character encoding like UTF-8 to ANSI and back. It is not alone that the basic_string doesn't give any concepts for this, it adds additional complexity to those conversions whenever different char types are involved.
Regards, Alex
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
Really? You can't safely derive from it, can you? You can't add new member functions therefore, right?
Enhancements via function templates hardly can be used for praising a class design. On the contrary, they are global functions and I share the opinion that global functions mostly is poor OOP and the need for those functions show the heavy shortcomings of this class.
Even my very first string class in the early 90's had better functionality and the missing 'trim' was only one of many evidences that they had many problems to integrate string semantics like 'whitespace', 'words', 'sentences', and more to their string class which IMO is not much more then a container for char types - where even a vector<char> has some advantages.
Or take the case where you need to convert from wstring to string or vice versa or let alone character encoding like UTF-8 to ANSI and back. It is not alone that the basic_string doesn't give any concepts for this, it adds additional complexity to those conversions whenever different char types are involved.
Regards, Alex
Well actually, some would say that in good OOP design, all these functions should NOT be member functions. The less member functions you have, the better.
I mean, boost was able to expand on string (or string-like containers) an entire string library algorithm. If that's not Enhancement, I don't know what is.
I'll grant you that basic_string is not great for holding (variable-sized) UTF, but it can hold char or wchar just fine, as well as any fixed size characters. You can convert one string type to another quite easily using iterator construction, provided you can construct the basic char type from the other type.
No, what I think is that there should have been a <string_algorithm> class, just like boost's, but right in the STL.
I know the STL philosophy is to not provide a tool if the combination of existing tools can do it for you (and they can), but I think there should have been an exception for strings, inside a library that operates specifically on strings.
My choice of words "std::string is a bit lacking in the interface/usability department" may have been less than ideal, and I will correct myself to: "the STL offers little practical facilities for string manipulations, forcing the user into more direct (and bulky) string iterator+algorithm+functor manipulation". And I think it's a pity.
-
Re: A question regarding *s++
Quote:
Originally Posted by itsmeandnobodyelse
Really? You can't safely derive from it, can you? You can't add new member functions therefore, right?
Enhancements via function templates hardly can be used for praising a class design. On the contrary, they are global functions and I share the opinion that global functions mostly is poor OOP and the need for those functions show the heavy shortcomings of this class.
Heh, when you stated that "the basic_string class actually is a poor string class", I actually thought that you had its over-abundance of member functions in mind. Apparently not. Read:
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
Really? You can't safely derive from it, can you? You can't add new member functions therefore, right?
Enhancements via function templates hardly can be used for praising a class design.
Not class design specifically----but architecture design as a whole, absolutely. The STL concept of separating containers from algorithms (except where an algorithm is specific to a container type, like std::list::sort) is brilliant, frankly. The idea that you can write an algorithm without having to care what type it operates on was a revelation when I first worked through the implications.
-
Re: A question regarding *s++
Quote:
Originally Posted by
itsmeandnobodyelse
Even my very first string class in the early 90's had better functionality and the missing 'trim'
Huge string classes that try to do everything under the sun and try to be all things to most people is one of the complaints given to std::string -- it is not considered a virtue to pack in all of these functions that are easily done using algorithms. All a string class should do IMO is to do concatenation, copy safely, and get substrings.
Quote:
Or take the case where you need to convert from wstring to string or vice versa or let alone character encoding like UTF-8 to ANSI and back. It is not alone that the basic_string doesn't give any concepts for this, it adds additional complexity to those conversions whenever different char types are involved.
That's why you have the std::transform algorithm -- I use it now to convert between different string types.
Regards,
Paul McKenzie