Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Exercise 10.5 #227

Closed
CyberLuc opened this issue Apr 29, 2015 · 13 comments
Closed

Exercise 10.5 #227

CyberLuc opened this issue Apr 29, 2015 · 13 comments
Assignees
Labels

Comments

@CyberLuc
Copy link

Hi, I'm here again :D

Exercise 10.5: In the call to equal on rosters, what would happen if both rosters held C-style strings, rather than library strings?

And the answer given is:

Answer It's the same as std::string

But I think it is actually not the same as std::string.

Code given:

int main() {
    std::vector<const char *> roster1{"Mooophy", "pezy", "Queequeg"};
    std::list<const char *> roster2{"Mooophy", "pezy", "Queequeg", "shbling", "evan617"};
    std::cout << std::equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());
}

As we know, c-style strings cannot be compared using == and < and >, 'casue these relational operators compare addresses in pointers rather than strings represented by char pointers.

And the "Mooophy", "pezy", "Queequeg" in roster1 and "Mooophy", "pezy", "Queequeg" in roster2 are all literals, and compiler optimized them to be stored in the same addresses. Thus yielded 1.

std::cout << (void*)roster1[0] << std::endl;
std::cout << (void*)roster2.front() << std::endl;

On my pc, I got 0x401ec6 and 0x401ec6.

Then look at this:

int main() {
    const char a[3][2] = {"A", "B", "C"};
    const char b[3][2] = {"A", "B", "C"};

    std::vector<const char*> v1(std::begin(a), std::end(a));
    std::list<const char*> v2(std::begin(b), std::end(b));

    std::cout << std::boolalpha
         << std::equal(v1.cbegin(), v1.cend(), v2.cbegin()) << std::endl;
    return 0;
}

Which yields false.

So the answer of this exercise depends. But I think the first situation is just a coincidence and should be avoided;

NOTE: If we define a and b as pointer array, then the result is true again.

const char *a[3] = {"A", "B", "C"};
const char *b[3] = {"A", "B", "C"};

So literals of same contents pointed to by pointers share the same memories, but literals in arrays are stored individually.

@Mooophy Mooophy added the bug label Apr 29, 2015
@pezy
Copy link
Collaborator

pezy commented Apr 29, 2015

@CyberLuc I couldn't agree more. 👍

@Mooophy
Copy link
Owner

Mooophy commented Apr 29, 2015

I admire @CyberLuc 's strong skill at such low level, hoping @CyberLuc make more contribution for us. The example given above is great as well.

As for this issue, can we change the current answer like below?

For such case, std::equal is going to compare the address value rather than the string value. So the result is not the same as std::string. Try to avoid coding this way.

@CyberLuc @pezy

@CyberLuc
Copy link
Author

Thank you for your praise! 😄

And yeah, the updated answer is good! 👍

@Mooophy

@Mooophy Mooophy self-assigned this Apr 29, 2015
@Mooophy Mooophy mentioned this issue Oct 13, 2015
@chihyang
Copy link
Contributor

I compiled the following program with g++ and cl respectively.

#include <iostream>
#include <vector>
#include <algorithm>
using std::cout;
using std::endl;
using std::vector;
int main()
{
    // just for explanation, don't use this way.
    vector<const char*> roster1 = { "C++", "Primer", "Plus" };
    vector<const char*> roster2 = { "C++", "Primer", "Plus" };
    cout << "Two const char arrays are " 
         << (equal(roster1.cbegin(), roster1.cend(), roster2.cbegin()) ? "equal" : "not equal") << endl;
    cout << "Address of roster1: ";
    for(const auto &cs : roster1)
        cout << static_cast<void*>(cs) << " ";
    cout << endl;
    cout << "Address of roster2: ";
    for(const auto &cs : roster2)
        cout << static_cast<void*>(cs) << " ";
    cout << endl;
    return 0;
}

With g++, the result is:

Two const char arrays are equal
Address of roster1: 0x4ab07e 0x4ab082 0x4ab089
Address of roster2: 0x4ab07e 0x4ab082 0x4ab089

With cl, the result is:

Two const char arrays are not equal
Address of roster1: 00A4F190 00A4F194 00A4F19C
Address of roster2: 00A4F1A4 00A4F1A8 00A4F1B0

Obviously, this depends on compiler. It accords with @CyberLuc's result. Thank you!

@Zjianglin
Copy link

@chihyang hi, I tried to test your code, but encountered compile error as below.

$ clang++ -std=c++11 test.cpp -o maintest.cpp:16:17: error: static_cast from 'const char *' to 'void *' is not
      allowed
        cout << static_cast<void*>(cs) << " ";
                ^~~~~~~~~~~~~~~~~~~~~~
test.cpp:20:17: error: static_cast from 'const char *' to 'void *' is not
      allowed
        cout << static_cast<void*>(cs) << " ";
                ^~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

It's same to g++ compiler. How did you get your output, or did I do somethig error? Thanks.

@chihyang
Copy link
Contributor

@Zjianglin Hi, I usually used the following commands when I learned the primer (supposing the directory obj exists in current path):

g++ -std=c++11 -Wall -o <output> <input>
clang++ -std=c++11 -stdlib=libstdc++ -Wall -o <output> <input>
cl -EHsc -W4 <input> -Fo./obj/ -Fe./obj/

And the compiler versions I used are as follows:

g++: (tdm64-1) 5.1.0
clang++: clang version 3.7.0 (tags/RELEASE_370/final)
cl: 18.00.40629(Visual Studio 2013)

On my machine all of the commands above worked well. g++ and clang++ gave very warm warnings. cl just quietly passed everything. But none of them threw an error. Maybe the problem lies in the compiler version?

Hope the info above can help you.

@Zjianglin
Copy link

Zjianglin commented Jan 11, 2017

@chihyang Thanks for your reply. I guess the problem maybe lies in the compiler version. After changing
static_cast<void*>(cs) to static_cast<const void*>(cs), the compile error was disappeared.

Another question, I saw you converted the const char * to void *, then cout it would print the the address of the element. I am confused that why not use address-of operator (&)?. And I think static_cast<void*>(cs) will generate an anonymous object, why its address could indicate the original element(cs)'s address?
I did a test as below:

 const char* test =  "test it";
    void *p = &test;
    const auto  &refer = test;
    cout << test << "===" << refer << endl;
    cout << static_cast<const void*>(test) << "\t" << &test << '\t' << p << endl;
    cout << static_cast<const void*>(refer) << "\t" << &refer << endl;

output(Based on g++ 5.4.0, simial to clang++ 3.8.0 ) :

test it===test it
0x401d08	0x7ffddcad4a40	0x7ffddcad4a40
0x401d08	0x7ffddcad4a40

@chihyang
Copy link
Contributor

chihyang commented Jan 11, 2017

Hi, @Zjianglin. got your reply.

  • First question:

Why not use address-of operator (&)?

All of the snippets here are used to prove that STL algorithm equal shouldn't be applied to const char* (or char*) because the result depends on platforms. As @Mooophy put it:

For such case, std::equal is going to compare the address value rather than the string value. ...

Thus the snippets try to output the real value a const char* pointer holds, to prove the values of elements in two vector<const char*> depend on platforms. The real value of an element in vector<const char*> is an address value, so we are trying to output address values. To get such values, we cannot directly use the code below:

const char* cstr = "c style string";
cout << cstr << endl;

Because cout will output the string literals a const char* pointer points to rather than the address value(see this answer), we have to convert a const char* pointer to const void* pointer. The address-of operator & is not helpful because it evaluates the address of a pointer resides rather than the value the pointer holds. The table below best explains everything:

Symbol         *cstr    cstr    &cstr 
Memory Value   ['c']    [1000]  [1004]
Memory Address 1000     1004    1008 

Here we want 1004, but &cstr yields 1008. For this reason, I don't use & operator.

  • Second question

Why the anonymous object static_cast generates could indicate the original element(cs)'s address?

The link above has given a very detailed explanation. I guess you confuse the address a pointer holds and the address the pointer resides. Here we are only interested in the former. The latter doesn't matter to us.

This is a little messy. Hope my reply doesn't increase your confusions if it doesn't ease them.

@Zjianglin
Copy link

Zjianglin commented Jan 12, 2017

Hi, @chihyang . Thanks for your detailed explanation. I think I did not realize The real value of an element in vector<const char*> is an address value(a pointer), and confused the address a pointer holds and the address the pointer resides.
What's more, static_cast<const void *>(cs) should really generate an anonymous object, which is a void pointer to the same object cs point to. So cout << static_cast<const char *>(cs) actually print out the address cs holds.

Thank you again!

@lanyiyrt
Copy link

thanks !!

@swahpy
Copy link

swahpy commented May 15, 2020

Thanks a lot !! Vehry helpful.

@hy-zhao23
Copy link

hy-zhao23 commented Mar 31, 2021

Hi, I'm here again :D

Exercise 10.5: In the call to equal on rosters, what would happen if both rosters held C-style strings, rather than library strings?

And the answer given is:

Answer It's the same as std::string

But I think it is actually not the same as std::string.

Code given:

int main() {
    std::vector<const char *> roster1{"Mooophy", "pezy", "Queequeg"};
    std::list<const char *> roster2{"Mooophy", "pezy", "Queequeg", "shbling", "evan617"};
    std::cout << std::equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());
}

As we know, c-style strings cannot be compared using == and < and >, 'casue these relational operators compare addresses in pointers rather than strings represented by char pointers.

Here is the problem for this conclusion. I disagree the compiler compares address rather than strings. Simply change the code to std::cout << std::equal(roster2.cbegin(), roster2.cend(), roster1.cbegin());, you will get 0.

For the original example, it outputs 1 because compiler assumes the first vector is as long as the second one(it doesn't check the length for you), which means only when the 2nd one is shorter, you can get a right answer.

@YacineDeghaies
Copy link

I learned a few things from this. thanks a lot !

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants