patterncModerate
"Restaurant" HackerRank challenge in C
Viewed 0 times
hackerrankchallengerestaurant
Problem
I don't write C all that often, so things to look for would be memory leaks and such. I also recognize that I'm not validating the user input as well as I could - I could use some feedback on how I might do better at that with this particular problem. I'm interested in hearing everything you find wrong with this code, however.
The problem, in so many words, is, given a list of dimensions for
rectangular bread slices, what's the fewest number of pieces I could
make to divide up each slice into perfect squares without wasting any
bread (0 cuts being 1 piece).
Input should look like:
where the first line provides the number of bread slices, and the
subsequent lines provide the length and width of each slice.
Output:
Original problem description
Note: I'm aware I could have used Lehmer's or the Euclidean methods for finding the GCD, but I wanted to implement the problem this way first just for the challenge of writing the brute force implementation. Other than alternative GCD algorithms, though, I would be interested in any opportunities found for performance improvement.
```
#include
#include
#include
#include
#include
#include
void free_2d_array(int** arr) {
int i = 0;
while (arr[i][0]) {
free(arr[i]);
i++;
}
free(arr[i]);
free(arr);
}
void flag_error(char msg[]) {
puts(msg);
exit(EXIT_FAILURE);
}
// Gets the number of slices for this test according to the first input value from stdin.
int read_num_slices() {
int num_slices = 0;
scanf("%i", &num_slices);
if (num_slices == 0) {
goto error;
}
return num_slices;
error:
flag_error("ERROR: Could not parse the number of entries from first input line.");
return 1;
}
// Gets a single line from stdin and attempts to parse it into a 2D int array representing the dimensions of a slice.
// Returns [0,0] on error.
int* read_slice_dimension() {
static int loaf_dimension[2] = {0};
scanf("%i %i", &loaf_dim
The problem, in so many words, is, given a list of dimensions for
rectangular bread slices, what's the fewest number of pieces I could
make to divide up each slice into perfect squares without wasting any
bread (0 cuts being 1 piece).
Input should look like:
2
2 2
6 9where the first line provides the number of bread slices, and the
subsequent lines provide the length and width of each slice.
Output:
1
6Original problem description
Note: I'm aware I could have used Lehmer's or the Euclidean methods for finding the GCD, but I wanted to implement the problem this way first just for the challenge of writing the brute force implementation. Other than alternative GCD algorithms, though, I would be interested in any opportunities found for performance improvement.
```
#include
#include
#include
#include
#include
#include
void free_2d_array(int** arr) {
int i = 0;
while (arr[i][0]) {
free(arr[i]);
i++;
}
free(arr[i]);
free(arr);
}
void flag_error(char msg[]) {
puts(msg);
exit(EXIT_FAILURE);
}
// Gets the number of slices for this test according to the first input value from stdin.
int read_num_slices() {
int num_slices = 0;
scanf("%i", &num_slices);
if (num_slices == 0) {
goto error;
}
return num_slices;
error:
flag_error("ERROR: Could not parse the number of entries from first input line.");
return 1;
}
// Gets a single line from stdin and attempts to parse it into a 2D int array representing the dimensions of a slice.
// Returns [0,0] on error.
int* read_slice_dimension() {
static int loaf_dimension[2] = {0};
scanf("%i %i", &loaf_dim
Solution
Profiling & Testing Your Approach
-
Valgrind test:
Now, Valgrind is not just a leak checker. It can give us a
lot of useful insights into your program and how it's working
internally. For instance, you see this little snippet that Valgrind
output after I gave your program some input? It's pointing us to
this line in your code:
We now know that we are possibly accessing the contents of something
that is unassigned on this line and that it could result in undefined
behavior. My guess to Valgrind's issue with this line has something
to do with
just starting out with my use of this tool in everyday programming.
-
Bash
So your program runs pretty fast, but we seemed to have uncovered
some sort of strange behavior that causes a segmentation fault in
your program.
Reviewing Your Approach
-
You have no need for the
you do not accept a variable amount of arguments in any of your
functions.
-
I notice in some areas that you use
loops, but you still have the counter variable in which you don't use
after the loop has concluded.
Why was this not written as a
-
Your
your
would change the
instead. Also, since you don't modify this array in any way within the function, it should be declared as a
-
The
try to avoid
Rewritten function:
-
You declare the
retain the values every time you run through the function? My guess
is not, so I would remove that identifier.
-
In your
declaration of one line.
-
Again, there is no need for the
function.
-
Use the
included the
-
Declarer your counter variable whenever you can within the `for
-
Valgrind test:
$ valgrind ./original
==43831== Memcheck, a memory error detector
==43831== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==43831== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info
==43831== Command: ./original
==43831==
--43831-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option
--43831-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 2 times)
--43831-- UNKNOWN mach_msg unhandled MACH_SEND_TRAILER option (repeated 4 times)
2
2 2
6 9
==43831== Conditional jump or move depends on uninitialised value(s)
==43831== at 0x1003FBC3F: _platform_memchr$VARIANT$Haswell (in /usr/lib/system/libsystem_platform.dylib)
==43831== by 0x1001EFBB6: __sfvwrite (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x1001FA005: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x10021F9CE: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x10021FCA0: __xvprintf (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x1001F5B91: vfprintf_l (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x1001F39F7: printf (in /usr/lib/system/libsystem_c.dylib)
==43831== by 0x100000E27: main (main.c:132)
==43831==
1
6
==43831==
==43831== HEAP SUMMARY:
==43831== in use at exit: 43,127 bytes in 427 blocks
==43831== total heap usage: 512 allocs, 85 frees, 49,319 bytes allocated
==43831==
==43831== LEAK SUMMARY:
==43831== definitely lost: 16 bytes in 1 blocks
==43831== indirectly lost: 0 bytes in 0 blocks
==43831== possibly lost: 13,090 bytes in 117 blocks
==43831== still reachable: 30,021 bytes in 309 blocks
==43831== suppressed: 0 bytes in 0 blocks
==43831== Rerun with --leak-check=full to see details of leaked memory
==43831==
==43831== For counts of detected and suppressed errors, rerun with: -v
==43831== Use --track-origins=yes to see where uninitialised values come from
==43831== ERROR SUMMARY: 2 errors from 1 contexts (suppressed: 0 from 0)
Now, Valgrind is not just a leak checker. It can give us a
lot of useful insights into your program and how it's working
internally. For instance, you see this little snippet that Valgrind
output after I gave your program some input? It's pointing us to
this line in your code:
printf("%i\n", find_min_number_of_slices(slices[i]));
We now know that we are possibly accessing the contents of something
that is unassigned on this line and that it could result in undefined
behavior. My guess to Valgrind's issue with this line has something
to do with
slices[i], but I can't quite say for sure since I amjust starting out with my use of this tool in everyday programming.
-
Bash
time test:$ printf '2\n2 2\n6 9' | time ./original
1
6
1
1925460560
Command terminated abnormally.
0.16 real 0.00 user 0.00 sys
So your program runs pretty fast, but we seemed to have uncovered
some sort of strange behavior that causes a segmentation fault in
your program.
Reviewing Your Approach
-
You have no need for the
stdarg.h heading that you include sinceyou do not accept a variable amount of arguments in any of your
functions.
-
I notice in some areas that you use
while loops instead of forloops, but you still have the counter variable in which you don't use
after the loop has concluded.
int i = 0;
while (arr[i][0]) {
free(arr[i]);
i++;
}
free(arr[i]);
free(arr);
Why was this not written as a
for loop?for (int i = 0; arr[i]; i++)
{
free(arr[i]);
}
free(arr);
-
Your
flag_error() function declaration accepts a char array, but inyour
free_2d_array() you accept a double pointer to an int. Iwould change the
flag_error declaration to accept a char pointerinstead. Also, since you don't modify this array in any way within the function, it should be declared as a
const parameter.-
The
goto in your read_num_slices() function is useless. Alwaystry to avoid
goto, since it leads to spaghetti code. Rewritten function:
int read_num_slices()
{
int num_slices = 0;
scanf("%i", &num_slices);
if (!num_slices)
{
flag_error("ERROR: Could not parse the number of entries from first input line.");
return -1;
}
return num_slices;
}
-
You declare the
loaf_dimension variable in yourread_slice_dimension() to be static, but do you really want it toretain the values every time you run through the function? My guess
is not, so I would remove that identifier.
int loaf_dimension[2] = {};
-
In your
get_slices() method, merge the declaration and assignment ofslices that are on two separate lines into an initializationdeclaration of one line.
-
Again, there is no need for the
goto in your get_slices()function.
-
Use the
pow() function in next_square() since you haveincluded the
math.h header anyways and it should be a bit faster.-
Declarer your counter variable whenever you can within the `for
Context
StackExchange Code Review Q#73517, answer score: 11
Revisions (0)
No revisions yet.