Saltar al contenido
ES EN

Solved exercise: pass by value and by reference in C

Updated on

Exam Code C Programming fundamentals Basic level Tested code

Solved C exercise on pass by value and pass by reference with pointers, using stage times from a bike ride, with tested code and the usual exam mistakes.

Exam Code: Solved exercise: pass by value and by reference in C
CS 101: Introduction to Programming in C · Lab assignmentExercise 2 · 2.5 points · 25 min

On a charity bike ride, every rider carries a basic stopwatch. For each stage it records three independent counters: hours, minutes and seconds. The device never normalises anything, so a stage can be logged as 0 hours, 75 minutes and 130 seconds. The organisers want a program that cleans up these logs.

Write a C program that reads an integer N (0 ≤ N ≤ 1000) from standard input. It is followed by N lines, each holding three space-separated integers h m s, each between 0 and 10000. Each line is the duration of one stage.

You must implement and use these three functions. long long to_seconds(int h, int m, int s) receives the counters by value and returns the duration in seconds. void split_time(long long total, long long *h, long long *m, long long *s) receives a number of seconds and stores the hours, the minutes (0 to 59) and the seconds (0 to 59) in the three variables pointed to. void accumulate(long long sec, int stage, long long *total, long long *longest, int *longest_pos) adds sec to the total and keeps track of the longest stage.

Output: for each stage, in input order, print one line Stage i: HH:MM:SS. Here i starts at 1 and every field has at least two digits; hours may have more. Then print a line Total: HH:MM:SS with the sum of all stages, and a line Longest stage: k with the number of the longest stage. If there is a tie, the first of the tied stages wins. If N is 0, the total is 00:00:00 and the last line is Longest stage: none.

Do not use global variables. All data must move between functions through parameters and return values.

Sample input

3
0 75 130
1 0 0
0 59 61

Expected output

Stage 1: 01:17:10
Stage 2: 01:00:00
Stage 3: 01:00:01
Total: 03:17:11
Longest stage: 1
Function to_seconds uses pass by value, has no side effects and does not overflow
0.5
Function split_time returns hours, minutes and seconds through pointers
0.75
Function accumulate updates total, longest duration and its position by reference, with the correct tie-break
0.75
Main program: input reading, exact output format and the no-stage case
0.5

Hints

Hint 1 · Which function needs pointers, and which does not?

Count how many results each function produces. to_seconds produces exactly one, so return is enough and its parameters can be copies. split_time produces three, and accumulate changes variables that live in main. In both of those cases the function has to receive the addresses of those variables.

Hint 2 · How do you normalise without special cases?

Do not push carries from seconds to minutes and from minutes to hours with a chain of if statements. Convert everything to seconds, then reverse the conversion with integer division and remainder. Hours come from dividing by 3600. Minutes are the remainder of that division divided by 60. Seconds are the remainder after dividing by 60.

Hint 3 · What should the maximum start at?

A stage can last 0 seconds, and it must still be able to win. Initialise longest to -1 and longest_pos to 0. Then any real stage beats the starting value, and longest_pos == 0 at the end means no stage was read. Use the strict comparison > so that the first stage keeps the title when there is a tie.

Solution

Explained solution

The arithmetic here is simple. The real skill being tested is deciding what travels by value and what travels by reference. In C, every argument is passed by value: the function receives a copy. When a function has to modify a caller's variable, we pass a copy of that variable's address, and the function writes through the pointer with *. This is exactly what scanf does when you pass it &n.

The solution converts each stage to seconds, splits that number back to print it normalised, and adds it to the running state. At the end it splits the total with the same function. A well-designed function can be reused unchanged.

#include <stdio.h>

/* Pass by value: receives copies and returns a single result. */
static long long to_seconds(int h, int m, int s)
{
    return (long long)h * 3600 + (long long)m * 60 + s;
}

/* Pass by reference: writes three results into the caller's variables. */
static void split_time(long long total, long long *h, long long *m, long long *s)
{
    *h = total / 3600;
    *m = (total % 3600) / 60;
    *s = total % 60;
}

/* In/out parameters: keeps main's running state up to date. */
static void accumulate(long long sec, int stage, long long *total,
                       long long *longest, int *longest_pos)
{
    *total += sec;
    if (sec > *longest) {
        *longest = sec;
        *longest_pos = stage;
    }
}

int main(void)
{
    int n;
    long long total = 0;
    long long longest = -1;
    int longest_pos = 0;
    long long hh = 0, mm = 0, ss = 0;

    if (scanf("%d", &n) != 1 || n < 0) {
        n = 0;
    }

    for (int i = 1; i <= n; i++) {
        int h, m, s;
        if (scanf("%d %d %d", &h, &m, &s) != 3) {
            break;
        }
        long long sec = to_seconds(h, m, s);
        split_time(sec, &hh, &mm, &ss);
        printf("Stage %d: %02lld:%02lld:%02lld\n", i, hh, mm, ss);
        accumulate(sec, i, &total, &longest, &longest_pos);
    }

    split_time(total, &hh, &mm, &ss);
    printf("Total: %02lld:%02lld:%02lld\n", hh, mm, ss);

    if (longest_pos == 0) {
        printf("Longest stage: none\n");
    } else {
        printf("Longest stage: %d\n", longest_pos);
    }
    return 0;
}

to_seconds is pass by value in its purest form. It receives copies of h, m and s, so even if it changed them, main would never see the change. Note the cast in (long long)h * 3600. Without it, the multiplication is done in int and only the result is converted afterwards. Within the stated limits a single stage fits in an int. The total of 1000 maximal stages, roughly 3.66·1010 seconds, does not. Decide the type before you compute, not after.

split_time must deliver three numbers, and return can only deliver one. So it takes three pointers and writes through them, for example *h = total / 3600. In the call split_time(sec, &hh, &mm, &ss), the & operator takes the address of each variable in main. The function gets copies of those addresses and uses them to modify the originals. In the example, the first stage is 0·3600 + 75·60 + 130 = 4630 seconds, which splits into 1 hour, 17 minutes and 10 seconds.

accumulate combines both mechanisms. sec and stage arrive by value because they are only read. total, longest and longest_pos arrive by reference because the function both reads and updates them; these are in/out parameters. The comparison sec > *longest is strict. In the tie test there are two stages of exactly one hour, one logged as 0 0 3600 and the other as 1 0 0. Stage 1 keeps the title.

In main, the starting value longest = -1 solves two problems. First, the first stage is always recorded, even if it lasts 0 seconds, as in the single zero-length stage test. Second, longest_pos stays at 0 when there are no stages, so the program can print Longest stage: none for N = 0. The same case prints Total: 00:00:00 with no extra code, because total starts at zero.

The format %02lld pads with zeros to two digits but never truncates. In the limits test each stage is 36,610,000 seconds and is printed as 10169:26:40. Finally, the program checks the return value of scanf. If the input ends early, the loop stops and prints whatever has been accumulated up to that point.

Step-by-step trace · generated by running the code

Stepisectotallongestlongest_pos
114630463046301
223600823046301
3336011183146301

Test cases

CaseInputExpected outputActual outputResult
Statement example3 0 75 130 1 0 0 0 59 61Stage 1: 01:17:10 Stage 2: 01:00:00 Stage 3: 01:00:01 Total: 03:17:11 Longest stage: 1Stage 1: 01:17:10 Stage 2: 01:00:00 Stage 3: 01:00:01 Total: 03:17:11 Longest stage: 1OK
No stages0Total: 00:00:00 Longest stage: noneTotal: 00:00:00 Longest stage: noneOK
Single zero-length stage1 0 0 0Stage 1: 00:00:00 Total: 00:00:00 Longest stage: 1Stage 1: 00:00:00 Total: 00:00:00 Longest stage: 1OK
Tie: first one wins2 0 0 3600 1 0 0Stage 1: 01:00:00 Stage 2: 01:00:00 Total: 02:00:00 Longest stage: 1Stage 1: 01:00:00 Stage 2: 01:00:00 Total: 02:00:00 Longest stage: 1OK
Maximum values2 10000 10000 10000 10000 10000 10000Stage 1: 10169:26:40 Stage 2: 10169:26:40 Total: 20338:53:20 Longest stage: 1Stage 1: 10169:26:40 Stage 2: 10169:26:40 Total: 20338:53:20 Longest stage: 1OK
Longest is the last3 0 0 59 0 2 0 0 1 61Stage 1: 00:00:59 Stage 2: 00:02:00 Stage 3: 00:02:01 Total: 00:05:00 Longest stage: 3Stage 1: 00:00:59 Stage 2: 00:02:00 Stage 3: 00:02:01 Total: 00:05:00 Longest stage: 3OK

Actual outputs: code compiled with gcc 14.4.0 (C17) and run in an isolated container on 6 October 2026.

Complexity

Each stage is processed once with a fixed number of operations: one conversion, one split, one print and one update. Running time is therefore O(N), linear in the number of stages, and every helper function is O(1).

Memory is O(1). The stages are never stored in an array, because everything the output needs (total, longest duration and its position) is updated as each stage is read. That is the point of giving accumulate reference parameters: the state lives in main, and the function keeps it current without copying it.

Common mistakes

  • Declaring split_time with parameters long long h, long long m, long long s instead of pointers. The function computes correctly but writes to its local copies, so main prints whatever its variables held before the call.
  • Forgetting the asterisk inside the function and writing h = total / 3600 when h is a pointer. This changes the address stored in the pointer copy, not the value it points to, and the compiler warns about converting an integer to a pointer.
  • Calling split_time(sec, hh, mm, ss) without &. Values are passed where addresses are expected, the code does not compile cleanly, and if you force it through it usually ends in a segmentation fault.
  • Initialising longest to 0 and comparing with >. A single stage of length 0 is then never recorded, and the program answers none when the correct answer is 1.
  • Using >= in the maximum comparison. With tied stages the last one wins, which contradicts the statement.
  • Doing the arithmetic in int without casts or long long. With many long stages the total overflows and negative hours appear.

Variants

Normalise in place without converting to seconds

The examiner may ask for void normalise(int *h, int *m, int *s), which modifies the three counters directly. Solve it with carries: *m += *s / 60; *s %= 60; *h += *m / 60; *m %= 60;. Order matters. Move surplus seconds into minutes first, then surplus minutes into hours. All three parameters are both input and output here.

Return a struct instead of using pointers

Another way to return several values is to define struct duration { long long h, m, s; }; and write struct duration split_time(long long total), which builds the struct and returns it by value. This reads more clearly and rules out null pointers, but the struct is copied on every call. With three fields that cost is negligible. Be ready to defend either design in the exam.

Swap the two longest stages

A classic follow-up is to add void swap(long long *a, long long *b) with a temporary variable: long long t = *a; *a = *b; *b = t;. It checks whether the student understands that a version without pointers swaps only the copies and leaves the original variables untouched.

Produced with AI support and reviewed by the newsroom

Byline

· Chief editor · English edition · London

“A pointer in a signature is a contract: this function will touch your data, and you must know exactly which data.”

Comentarios

Publicar un comentario