In doing a K&R exercise, I'm encountering a behavior that I can't make any sense of and for which I can't find any solutions here on SOF:
The program (converting base 10 ints to base n strings) runs fine with my extra printf()
statements. As soon as I begin removing or commenting them out, the unexpected behavior begins
The code in full:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#define MAXLINE 1000
char numtochar(unsigned n);
void reverse(char s[]);
void itob(int n, char s[], unsigned b);
int main() {
// test numtochar
assert(numtochar(1) == '1');
assert(numtochar(11) == 'b');
assert(numtochar(61) == 'Z');
assert(numtochar(62) == '+');
// test reverse
char t[] = "TestiNg";
reverse(t);
assert(!strcmp(t, "gNitseT"));
// test itob
printf("if this is commented out, it will segfault\n");
char s[MAXLINE];
itob(10, s, 10);
printf("%s\n", s);
assert(strcmp(s, "10") == 0);
itob(11, s, 2);
printf("%s\n", s);
assert(strcmp(s, "1011") == 0);
itob(100, s, 8);
printf("%s\n", s);
assert(strcmp(s, "144") == 0);
itob(1337, s, 32);
printf("%s\n", s);
assert(strcmp(s, "19p") == 0);
itob(127, s, 64);
printf("%s\n", s);
assert(strcmp(s, "1/") == 0);
return 0;
}
/* This numbering is not standard base-64, but will be consistent so long
* as base <= 64
* 0..63 => 0..9a..zA..Z+/
*/
char numtochar(unsigned n) {
assert(n < 64);
if (n < 10)
return ('0' + n);
else if (n >= 10 && n < 36)
return ('a' + (n - 10));
else if (n >= 36 && n < 62)
return ('A' + (n - 36));
else if (n == 62)
return '+';
else if (n == 63)
return '/';
}
void reverse(char s[]) {
int c, i, j;
for (i=0, j=strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
return;
}
void itob(int n, char s[], unsigned b) {
assert(b <= 64);
int c, i, sign;
if ((sign = n) < 0)
n = -n;
do {
s[i++] = numtochar(n % b);
} while ((n /= b) != 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return;
}
As demonstrated below, if I run with all the printf statements, it runs as expected. If I comment out the first statement, it will run fine once but not again.
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # no printf statements commented
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
if this is commented out, it will segfault
10
1011
144
19p
1/
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # comment out first printf statement
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
10
1011
144
19p
1/
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # resave after no changes
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
Segmentation fault (core dumped)
If however I uncomment out the first printf statement and comment out the second one (printf("%s\n", s);
), the result of itob is no longer passing the assert statement.
user@laptop:~/git/ansi_c/ch3 $ make ex05.o # uncomment first printf, comment second
gcc -o ex05.o ex05.c
user@laptop:~/git/ansi_c/ch3 $ ./ex05.o
if this is commented out, it will segfault
101101
ex05.o: ex05.c:33: main: Assertion `strcmp(s, "1011") == 0' failed.
Aborted (core dumped)
gcc version is 7.2.1
If I remove all printf statements it also segfaults. Being new to C, I am unsure where I could be underallocating memory if that is indeed the problem, as all similar questions I saw revolved around use of malloc.
An important thing to learn in C, is that if you don't initialise a variable with a value and then access it, it will lead to undefined behaviour. Take this function for example...
void itob(int n, char s[], unsigned b) {
assert(b <= 64);
int c, i, sign;
if ((sign = n) < 0)
n = -n;
do {
s[i++] = numtochar(n % b);
} while ((n /= b) != 0);
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
return;
}
What value does i
have at the start? It might be 0, but what it'll actually be is whatever random value happened to be in memory at the time. Which is why un-commenting code changes things as it's affecting what the value of i
has.
Just changing it to start with the value 0 fixes your problem. Also removed c
as it's not used
int i = 0, sign;
A good tool to use is valgrind
- it will tell you where memory corruption and leaks occur.