The current section applies many concepts from the previous pointer chapter. Please review the following as needed:
Programs frequently use pointers in conjunction with structures. Consequently, programmers use all pointer and memory allocation operators with them. However, the arrow operator is the most frequent because it selects individual fields within a structure, allowing programs to save and retrieve structure data. Our previous explorations of the arrow operator focused on syntax and usage. In this section, we take a deeper, more elaborative approach, attempting better to understand the operator's impact on data in memory.
Structures allow programmers to group related data items into cohesive blocks the program can move and organize as a unit. Imagine that a programmer specifies a student structure that includes a student's name, identification number, and other relevant data. A program can move and organize a student's information as a unit. For example, it can arrange all students alphabetically or in ID order. But to realize structures' full benefits, a program must be able to access the individual fields saved within the structure, which is the selection operators' task.
Choosing the correct selection operator depends on how the program references the structure. Programs can create structures on the stack, on the heap with new
, or use the address of a stack object. Programs use the dot operator in the first case and the arrow operator for pointers in the second and third cases.
struct student // 1 { int id; string name; double gpa; }; . . . student s1 = { 123, "dilbert", 3.0 }; // 2 student* sp1 = &s1; // 3 student* sp2 = new student; // 4 . . . cout << s1.id << endl; // 5 cout << sp1->name << endl; // 6 cin >> sp2->gpa; // 7 |
|
(a) | (b) |
new
and stores the address in a structure pointer named sp2.student* sp2 = new student { 987, "alice", 4.0 }; student* sp3 = sp2; |
|
(a) | (b) |
student* sp2 = new student { 987, "alice", 4.0 };
student s2 = *sp2;
|
|
(c) | (d) |
new
operator, saving its address in the pointer variable sp2. The second statement defines the pointer variable sp3 and copies the address saved in sp2 to it, but it does not copy the structure. Following the assignment, the two pointers point to the same student object.Understanding the dereference operator is challenging because it is a shorthand combining two distinct operations. Teasing apart the operations and representing each with a separate operator demonstrates the operations and their relative order. For example, sp->name
is equivalent to (*sp).name
. The dot operator has a higher precedence than the dereference operator, making the parentheses mandatory. The dereference operator, *, runs first, followed by the dot selection operator. Although programmers may use this two-step process, the arrow operator is more compact, clear, common, and typically preferred.
Throughout the previous discussions, I assert that structures are convenient for programmers to "move" data in a program as a unit. By "move," I mean passing data to and returning it from functions. The textbook formally introduces functions in the next chapter, covering them in depth. Until then, your previous experience with Java methods or Python functions is sufficient for you to understand the basic concepts and syntax. If needed, please see Function and Function call for a quick review or overview.
Passing a structure into a function copies the entire structure from one variable to another. C++ does not impose a size limit on structures, but copying very large structures takes a (relatively) long time. Passing a structure pointer to a function is one way to eliminate the overhead of passing a large structure. Paraphrasing Gertrude Stein, "An address is an address is an address." The size of an address is fixed, small, and independent of the data it points to. So, the value saved in a structure pointer, regardless of the structure's size, is the size of an address.
The print function demonstrates one way that pointers improve program performance. But pointers can improve performance in another way. The previous section ended the read function, demonstrating a function returning a structure object by copying it from the function to the function call. However, if the program passes the address of the structure, as in Figure 4 above, there is only one structure but with two names: temp in the function scope and s2 in the calling scope. So, any changes the function makes through temp are seen in s2.
void read(student* temp) { cout << "Enter a student id: "; cin >> temp->id >> endl; cout << "Enter a student name: "; cin >> temp->name >> endl; cout << "Enter a student gpa: "; cin >> temp->gpa >> endl; } . . . student s; read(&s); |
Enter a student id: 975 Enter a student name: wally Enter a student gpa: 1.3 |
(a) | (b) |
(c) |