简体   繁体   中英

Pointers to Data Structure Members

I recently read part of a C++ tutorial about pointers to data structures , whose struct is defined as:

struct movies_t {
    string title;
    int year;
};

movies_t * pmovie;

It mentioned how (*pmovie).title is different from *(pmovie.title) . Could someone explain why?

You cannot just interchange that syntax, as both mean different things.

The first case, (*pmovie).title means that pmovie is a pointer, you are dereferencing it, and then extracting the "title" field from that object. An equivalent/simpler syntax would be pmovie->title.

Your second case says, "Pmovie is an object, and title is a pointer to something. I want to dereference that pointer."

For example:

struct Movie {
    std::string title;
};

Movie movie;
Movie * pmovie = &movie;  // here pmovie is a pointer to a movie
pmovie->title = "Trading Places";
(*pmovie).title = "Trading Places";  // same meaning as line above

vs

struct Movie {
    std::string * title;
};

std::string title;
Movie pmovie;  // here "pmovie" is an insanely named variable for a non-ptr
pmovie.title = &title;

*(pmovie.title) = "V For Vendetta";  // Note, pmovie not a ptr, title is

Clearly the second case is ridiculous and really a poor interface (you don't want raw pointers in your object interface, regardless if owned internally or externally to the object.) I only gave the examples to show what the difference is. Hopefully you only run into code as in the first case.

The first dereferences the pointer pmovie . It is equivalent to

pmovie->title

The second is illegal syntax, assuming that pmovie is a pointer.

If pmovie is a pointer (eg movies_t * pmovie ), then accessing a member of it must either use the -> -operator (ie pmovie->title ) or dereference the pointer in order to address it with the . -notation (ie (*pmovie).title , which is equivalent to pmovie->title ). In contrast, *(pmovie.title) is simply wrong then, because operator . cannot be applied to a pointer.

In C++, just like in mathematics, operations of a complex expression are evaluated in a particular order. The order of the operations often changes the meaning of the expression. Parentheses can be used to explicitly specify the intended order. For example, 1 + (2 * 3) == 1 + 6 == 7 while (1 + 2) * 3 == 3 * 3 == 9 .

(*pmovie).title is different from *(pmovie.title) because the parenthesis change the order of operations. First indirects (== dereferences) the expression pmovie , and the accesses the member title of the result of the indirection. Second accesses the member title of the expression pmovie and indirects that member.

Now, given the declaration movies_t * pmovie; , the expression pmovie.title is ill-formed because the member access operator cannot be applied to a pointer.

When you say (*pmovie).title, it means that you are first getting what pmovie points to AND THEN asking what it's title is.

When you say *(pmovie.title), you are FIRST asking for the title of the movie AND THEN asking what that points to. This second one is wrong because you can't ask a pointer what its title is since it's just a pointer and not an actual movies_t object.

NOTE: this is just for the specific example you posted. There are times when *(pmovie.title) makes sense. Consider the case where you have

struct movies_t {
    // note how I made title a string pointer, not just a string
    string* title; 
    int year;
};

int main(){
    // note that movie_object is NOT a pointer, so *movie_object makes no 
    // sense
    movies_t movie_object;
    // the following expects a string, but 'title' is a string pointer
    // so movie_object.title returns a string pointer. I use * to access the 
    // actual string
    cout << *(movie_object.title) << endl;
}

It has to do with operator precedence . Here, the 2 operators we are talking about are * (dereference operator, also called indirection operator) and . (struct/class member access operator). They must be executed in the correct order, or the result you get doesn't make sense.

Let's try with an example. You go to a swimming pool. You put on your swimsuit and leave all your stuff in a locker, for which you get a key with a number on it. The number refers to the locker, and lets you identify it. In a way, it "points" to the locker (like a pointer points to a variable).

Now, you are about to swim and you realize that you have forgotten your swim cap. You remember that you left it in the top drawer inside the locker. So you have to: read the number on the key to find your locker (and open it), then open the top drawer and take your swim cap. Would it work if, instead, you took the key's number, tried to open its top drawer (yes, the key's number's top drawer!) expecting to find a swim cap in it, and then tried to read the number on the cap to find your locker? Of course not. It wouldn't make sense. This would mean executing the correct operations, but in the wrong order and thus the wrong objects.

Now, back to your example. pmovie is a pointer to a struct. Of the 2 operations we are discussing (dereference and member access), you want to dereference it to get to the struct. You certainly don't want to access one of its members, because a pointer doesn't have members any more than a key's number has drawers.

Likewise, a struct has members, and in this case one is called title . Once you have got to the struct (the locker), you want to access its content (the member title , or the drawer with the swim cap).

So, what we want to do is clear: first we want to dereference the pointer (like reading the number on the key and finding the locker), and then, on the result of this operation (the struct, or the locker), we want to take what we need (the title, or the top drawer).

Back to C++ now: if you just write *pmovie.title , what operation would you execute first, the dereferencing with * or the member access with . ? Note that what you want is not necessarily what you get. In this case, it isn't: according to the rules of C++, the dot must be evaluated first, because it has higher precedence (to avoid checking the table every time, a simple mnemonic rule is that what is on the right, like the dot, has higher precedence than what is on the left, like the star). This would lead to an operation that doesn't make sense.

To ensure the correct result, the operators must be applied in the right order: the pointer dereference first, getting a struct, and then the member access applied to it. To ensure this we have to force the compiler to evaluate the operators in an order that is not the default one, and the way to do this is to use the parentheses: by writing (*pmovie).title , the star is applied before the dot, which gives the expected result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM