Returning a Local Structure from a C++ Lab Program
In a Plus Two computer science lab, a team writes a C++ program to store the name and score of the top three students in a structure named Student. A function Student* topper(Student a, Student b) compares the two scores and returns the address of the higher-scoring parameter. In main they call Student *p = topper(s1, s2); and print p->name. The program prints a correct name on the lab machine but garbage after the teacher compiles it with a different compiler. A second version, with return type Student and the call topper(s1, s2).name, works reliably. The same team has Student list[3] and wants one function that prints all three records using a single pointer variable, starting with Student *q = list; and using q++. They ask whether the pointer version is faster than indexing, and whether returning &a from the function can ever be made safe without changing the function's parameters.
1. Explain why the pointer version can print garbage even though it sometimes works. State the condition under which returning `&a` would be safe, and identify the smallest change that keeps the function returning a pointer but removes the bug.
Read the worked answer
The parameter a is a local copy with automatic storage duration. Its lifetime ends when topper returns, so the returned address points to an object that no longer exists. Using that address is undefined behaviour: it may show the old bytes, show garbage, or crash, and a different compiler or optimisation level changes what happens. Returning &a is safe only if the object outlives the call. That happens if a were declared static, or if the function returned a pointer to a dynamically allocated object that the caller later deletes. The smallest safe pointer-returning change is to accept pointers or references to the caller's objects and return one of those addresses, for example Student* topper(Student *a, Student *b) returning a or b. The caller's s1 and s2 outlive the call. Returning by value is simpler and should be preferred unless the design needs polymorphism or a null result.
2. Write the corrected function and the call using references. Then explain whether the array loop with `q++` is correct and whether it is faster than `list[i]`.
Read the worked answer
A safe reference version avoids copying and does not return a local object:
const Student& topper(const Student& a, const Student& b) {
return (a.score >= b.score) ? a : b;
}
const Student& top = topper(s1, s2);
cout << top.name;
The returned reference is valid as long as s1 and s2 are alive. The const qualifiers prevent accidental modification and allow the function to accept ordinary and const objects.
The array loop is correct if q starts at list and stops at list + 3:
const Student *q = list;
for (int i = 0; i < 3; i++) {
cout << q->name;
q++;
}
q++ advances by sizeof(Student) bytes, so it points to the next structure, not the next byte. It is not inherently faster than list[i]; a compiler usually generates equivalent code. The real risks are advancing past the one-past-the-end pointer and losing the original base pointer, so keep the loop bound explicit.
3. The team also wants to write the whole array to a binary file with `fwrite(list, sizeof(Student), 3, fp)` and read it on another machine. What should they check about the structure layout and pointer arithmetic before relying on this?
Read the worked answer
q++ works because pointer arithmetic scales by the complete sizeof(Student), including any padding the compiler inserts between a character array and an integer. That same padding is a portability problem for the file. The padding bytes may contain indeterminate values, and the size of int, the endianness, and the alignment rules can differ across machines. The file might be larger than the visible fields and might not be readable by a program compiled with different settings. For portable storage, write fields individually or serialise to a defined text or binary format with fixed-width types such as int32_t. Before using raw fwrite, the team should confirm the same structure definition, compiler packing, integer sizes, and byte order on both machines. Otherwise the array may print correctly in memory but produce corrupted records after transfer.