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.

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 61Expected 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
| Step | i | sec | total | longest | longest_pos |
|---|---|---|---|---|---|
| 1 | 1 | 4630 | 4630 | 4630 | 1 |
| 2 | 2 | 3600 | 8230 | 4630 | 1 |
| 3 | 3 | 3601 | 11831 | 4630 | 1 |
Test cases
| Case | Input | Expected output | Actual output | Result |
|---|---|---|---|---|
| Statement example | 3
0 75 130
1 0 0
0 59 61 | Stage 1: 01:17:10
Stage 2: 01:00:00
Stage 3: 01:00:01
Total: 03:17:11
Longest stage: 1 | Stage 1: 01:17:10
Stage 2: 01:00:00
Stage 3: 01:00:01
Total: 03:17:11
Longest stage: 1 | OK |
| No stages | 0 | Total: 00:00:00
Longest stage: none | Total: 00:00:00
Longest stage: none | OK |
| Single zero-length stage | 1
0 0 0 | Stage 1: 00:00:00
Total: 00:00:00
Longest stage: 1 | Stage 1: 00:00:00
Total: 00:00:00
Longest stage: 1 | OK |
| Tie: first one wins | 2
0 0 3600
1 0 0 | Stage 1: 01:00:00
Stage 2: 01:00:00
Total: 02:00:00
Longest stage: 1 | Stage 1: 01:00:00
Stage 2: 01:00:00
Total: 02:00:00
Longest stage: 1 | OK |
| Maximum values | 2
10000 10000 10000
10000 10000 10000 | Stage 1: 10169:26:40
Stage 2: 10169:26:40
Total: 20338:53:20
Longest stage: 1 | Stage 1: 10169:26:40
Stage 2: 10169:26:40
Total: 20338:53:20
Longest stage: 1 | OK |
| Longest is the last | 3
0 0 59
0 2 0
0 1 61 | Stage 1: 00:00:59
Stage 2: 00:02:00
Stage 3: 00:02:01
Total: 00:05:00
Longest stage: 3 | Stage 1: 00:00:59
Stage 2: 00:02:00
Stage 3: 00:02:01
Total: 00:05:00
Longest stage: 3 | OK |
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_timewith parameterslong long h, long long m, long long sinstead of pointers. The function computes correctly but writes to its local copies, somainprints whatever its variables held before the call. - Forgetting the asterisk inside the function and writing
h = total / 3600whenhis 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
longestto 0 and comparing with>. A single stage of length 0 is then never recorded, and the program answersnonewhen 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
intwithout casts orlong 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



Comentarios
Publicar un comentario