I am using Okamura's implementation of lzss : https://oku.edu.mie-u.ac.jp/~okumura/compression/lzss.c
int enc_i = 0;
int getbit(int n, unsigned char *enco_buf) /* get n bits */
{
int i, x;
static int buf, mask = 0;
x = 0;
for (i = 0; i < n; i++) {
if (mask == 0) {
//if ((buf = fgetc(infile)) == EOF) return EOF;
//if ((buf = fgetc(infile)) == EOF) return EOF;
if((buf = enco_buf[enc_i]) == '\0') break;
//buf = enco_buf[enc_i];
enc_i++;
//printf("\nValue of enc : %d\n", enc_i);
mask = 128;
}
x <<= 1;
if (buf & mask) x++;
mask >>= 1;
}
return x;
}
void decode(unsigned char *enco_buf)
{
int i, j, k, r, c;
outfile = fopen ("test_lib.txt", "wb");
for (i = 0; i < N - F; i++) buffer[i] = ' ';
r = N - F;
while ((c = getbit(1, enco_buf)) != '\0') {
if (c) {
if ((c = getbit(8, enco_buf)) == '\0') break;
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
} else {
if ((i = getbit(EI, enco_buf)) == '\0') break;
if ((j = getbit(EJ, enco_buf)) == '\0') break;
for (k = 0; k <= j + 1; k++) {
c = buffer[(i + k) & (N - 1)];
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
}
}
}
fclose(outfile);
}
Rather than taking the input from file, I am taking an encoded buffer which I am passing to the function decode() which in-turns passes to getbit(). But it is decoding upto a certain number of bits ie. 43. What I am guessing is I put '\0' instead of EOF (as it was in file end marker) to detect string end. and that might be the problem where break statement is executed finding '\0'. It might not be true but It might be the case. The encoding part has no problem as I verified it with the original code. Can anyone suggest where might be the problem?
EDIT : source code:
/* LZSS encoder-decoder (Haruhiko Okumura; public domain) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lzss_lib.h"
#define EI 11 /* typically 10..13 */
#define EJ 4 /* typically 4..5 */
#define P 1 /* If match length <= P then output one character */
#define N (1 << EI) /* buffer size */
#define F ((1 << EJ) + 1) /* lookahead buffer size */
int bit_buffer = 0, bit_mask = 128;
unsigned long codecount = 0, textcount = 0;
unsigned char buffer[N * 2];
int payload_i = 0;
int enc_i = 0;
FILE *infile, *outfile;
void error(void)
{
printf("Output error\n"); exit(1);
}
void putbit1(unsigned char *put_buf0)
{
bit_buffer |= bit_mask;
if ((bit_mask >>= 1) == 0) {
put_buf0[payload_i] = bit_buffer;
payload_i++;
bit_buffer = 0; bit_mask = 128; codecount++;
}
}
void putbit0(unsigned char *put_buf1)
{
if ((bit_mask >>= 1) == 0) {
put_buf1[payload_i] = bit_buffer;
payload_i++;
bit_buffer = 0; bit_mask = 128; codecount++;
}
}
void flush_bit_buffer(unsigned char *flush_bit)
{
if (bit_mask != 128) {
flush_bit[payload_i] = bit_buffer;
codecount++;
}
}
void output1(int c, unsigned char *buf_output1)
{
int mask;
putbit1(buf_output1);
mask = 256;
while (mask >>= 1) {
if (c & mask) putbit1(buf_output1);
else putbit0(buf_output1);
}
}
void output2(int x, int y, unsigned char *buf_output2)
{
int mask;
putbit0(buf_output2);
mask = N;
while (mask >>= 1) {
if (x & mask) putbit1(buf_output2);
else putbit0(buf_output2);
}
mask = (1 << EJ);
while (mask >>= 1) {
if (y & mask) putbit1(buf_output2);
else putbit0(buf_output2);
}
}
void encode(unsigned char *string_buf, unsigned char *out_buf, unsigned long *out_buf_len)
{
int i, j, f1, x, y, r, s, bufferend, c, tt;
tt = 0;
for (i = 0; i < N - F; i++) buffer[i] = ' ';
for (i = N - F; i < N * 2; i++) {
if((c = string_buf[tt]) == '\0') break;
buffer[i] = c; textcount++; tt++;
}
bufferend = i; r = N - F; s = 0;
while (r < bufferend) {
f1 = (F <= bufferend - r) ? F : bufferend - r;
x = 0; y = 1; c = buffer[r];
for (i = r - 1; i >= s; i--)
if (buffer[i] == c) {
for (j = 1; j < f1; j++)
if (buffer[i + j] != buffer[r + j]) break;
if (j > y) {
x = i; y = j;
}
}
if (y <= P) { y = 1; output1(c, out_buf); }
else output2(x & (N - 1), y - 2, out_buf);
r += y; s += y;
if (r >= N * 2 - F) {
for (i = 0; i < N; i++) buffer[i] = buffer[i + N];
bufferend -= N; r -= N; s -= N;
while (bufferend < N * 2) {
if((c = string_buf[tt]) == '\0') break;
buffer[bufferend++] = c; textcount++; tt++;
}
}
}
flush_bit_buffer(out_buf);
*out_buf_len = codecount;
printf("text: %ld bytes\n", textcount);
printf("code: %ld bytes (%ld%%)\n", codecount, (codecount * 100) / textcount);
}
int getbit(int n, unsigned char *enco_buf) /* get n bits */
{
int i, x;
static int buf, mask = 0;
x = 0;
for (i = 0; i < n; i++) {
if (mask == 0) {
//if ((buf = fgetc(infile)) == EOF) return EOF;
//if ((buf = fgetc(infile)) == EOF) return EOF;
if((buf = enco_buf[enc_i]) == '\0') break;
//buf = enco_buf[enc_i];
enc_i++;
printf("\nValue of enc : %d\n", enc_i);
mask = 128;
}
x <<= 1;
if (buf & mask) x++;
mask >>= 1;
}
return x;
}
void decode(unsigned char *enco_buf)
{
int i, j, k, r, c;
outfile = fopen ("test_lib.txt", "wb");
for (i = 0; i < N - F; i++) buffer[i] = ' ';
r = N - F;
while ((c = getbit(1, enco_buf)) != '\0') {
if (c) {
if ((c = getbit(8, enco_buf)) == '\0') break;
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
} else {
if ((i = getbit(EI, enco_buf)) == '\0') break;
if ((j = getbit(EJ, enco_buf)) == '\0') break;
for (k = 0; k <= j + 1; k++) {
c = buffer[(i + k) & (N - 1)];
fputc(c, outfile);
buffer[r++] = c; r &= (N - 1);
}
}
}
fclose(outfile);
}
and the test example :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lzss_lib.h"
unsigned char compr_data[4096];
unsigned long pw = 0;
unsigned long payload_len = 0;
volatile int errno;
unsigned char *unc_data = "d(2306):AuthorisationScheme:RADIUSserveratfd04:bd3:80e8:1::1usingPAPD/6LoWPANd(2306):WritingModule:SecurityConfigD/6LoWPANd(2306):WritingModule:RunCoordinatorD/6LoWPANd(2306):RequestingmoduleaddressD/6LoWPANd(2306):WritingModule:GetAddressD/smartcard-jni(2781):SmartCard_state_update_callback:status=6D/SmartCardNative(2781):status=6D/smartcard(2781):PN532Smartcard_loop_threadexitD/smartcard-jni(2781):SmartCard_loop_thread:SMARTCARD_STATUS_ABORTD/smartcard(2781):Smartcard_loop_uninitD/smartcard(2781):2D/serialcomm_pn532(2781):PN532:Readingfrom/dev/ttyUSB0-->D/smartcard(2781):Received(0x3)fromPN532:(dataBuf[0]:0x1)(0x1f5988)D/smartcard(2781):ReceivedStatusfromPN532:OK(cmd:0x2)D/smartcard-jni(2781):SmartCard_listener_update_callbackD/smartcard(2781):Received(0x1c2)fromPN532:(dataBuf[0]:0x32)(0x1f5988)D/smartcard(2781):vd(2306):AuthorisationScheme:RADIUSserveratfd04:bd3:80e8:1::1usingPAPD/6LoWPANd(2306):";
FILE *outfile;
int main(void) {
memset (compr_data, '\0', sizeof(compr_data));
encode(unc_data, compr_data, &payload_len);
decode(compr_data);
return 0;
}
I`m just a newbie.
I got a mystery problem from my code that is actually OS class assignment.
my code works actually, but when I try with over 16 integers,
It returns unsorted values.
any values under 16 integers WORK.
why is that kinds of problem happened?
is that a issue about dynamic memory or pipe buffer size?
(I`m under Ubuntu 14.04 if it is helpful.)
my sorting code is :
void merge_conq(unsigned int *A, unsigned int *B, unsigned int *C, int size1, int size2) {
unsigned int *a = A;
unsigned int *b = B;
unsigned int *c = C;
int count_a = 0;
int count_b = 0;
while (count_a < (size1) && count_b < (size2)) {
if (*a < *b) {
*c = *a;
a++;
c++;
count_a++;
}
else {
*c = *b;
b++;
c++;
count_b++;
}
}
if (count_a == (size1)) {
while( count_b < (size2)) {
*c = *b;
b++;
c++;
count_b++;
}
}
else if(count_b == (size2)) {
while(count_a < (size1)) {
*c = *a;
a++;
c++;
count_a++;
}
}
}
and my merge divide code is:
unsigned int* merge(int start, int end, unsigned int* array) {
//detecting status of children
int status;
int state1, state2;
int fd1[2], fd2[2];
state1 = pipe(fd1);
state2 = pipe(fd2);
if ( state1 == -1 ) {
printf("error\n");
exit(0);
}
if ( state2 == -1 ) {
printf("error\n");
exit(0);
}
int length = end - start + 1;
int sizel, sizer;
if ( (length % 2) == 0 ) {
sizel = length / 2;
sizer = length / 2;
}
else{
sizel = (length / 2) + 1;
sizer = length / 2;
}
pid_t child1 = fork();
if ( child1 == 0 ) {
end = (start + end) / 2;
length = end - start + 1;
if ( (length % 2) == 0 ) {
sizel = length / 2;
sizer = length / 2;
}
else {
sizel = (length / 2) + 1;
sizer = length / 2;
}
if ( start != end ) {
unsigned int* a;
a = merge(start, end, array);
write(fd1[1], a, sizeof(int) * length);
exit(0);
}
else {
unsigned int last = array[start];
write(fd1[1], &last, 4);
exit(0);
}
}
else {
//right child
pid_t child2 = fork();
if ( child2 == 0 ) {
start = ((start + end) / 2) + 1;
length = end - start + 1;
if ( (length % 2) == 0 ) {
sizel = length / 2;
sizer = length / 2;
}
else {
sizel = (length / 2) + 1;
sizer = length / 2;
}
if ( start != end ) {
unsigned int* a;
a = merge(start, end, array);
write(fd2[1], a, sizeof(int) * length);
exit(0);
}
else {
unsigned int last = array[start];
write(fd2[1], &last, 4);
exit(0);
}
}
//parent code
else {
unsigned int *left=(unsigned int*)malloc(sizel);
unsigned int *right=(unsigned int*)malloc(sizer);
unsigned int *result=(unsigned int*)malloc(length);
child1 = wait( &status);
child2 = wait( &status);
read(fd1[0], left, sizeof(unsigned int) * sizel);
int k;
for ( k = 0; k < sizel; k++) {
printf("--%u--", left[k]);
};
printf("\n");
read(fd2[0], right, sizeof(unsigned int) * sizer);
int s;
for ( s = 0; s < sizer; s++ ) {
printf("..%u..",right[s]);
};
printf("\n");
merge_conq(left, right, result, sizel, sizer);
/*
int i;
for( i = 0; i < length; i++ ) {
printf("**%u**",result[i]);
};
printf("\n");
*/
return result;
}
}
}
It seems to me that you are not allocating the correct amount of memory. For example:
unsigned int *left=(unsigned int*)malloc(sizel);
Will allocate only sizel bytes, while you need:
unsigned int *left = malloc( sizel * sizeof(unsigned int) );
Besides, (note, it's not an error) you can avoid the two if's in your first snippet, because:
while ( count_a < (size1) && count_b < (size2) ) {
// ...
}
if ( count_a == (size1) ) {
while( count_b < (size2)) {
// ...
}
}
else if( count_b == (size2) ) {
while(count_a < (size1)) {
// ...
}
}
Is logically equivalent (for your code) to:
while ( count_a < size1 && count_b < size2 ) {
// ...
}
while( count_b < size2 ) {
// if you end up here then count_a == size1
}
while( count_a < size1 ) {
// sure count_b == size2
}
it seems that you are not using recursive technique here
void merge(int arr_new[],int p, int q)
{
int mid;
if(p<q)
{
mid=(q+p)/2;
merge(arr_new,p,mid);
merge(arr_new,mid+1,q);
merge_sequence(arr_new,p,mid,q);
}
}
this given function call will do the work of divide now you just need to write the code for merge the sequences. more help check here
See Latest Update
Given the following function, note the location of the call free(tmp):
int *power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp, *rt, *bufp;
int bufp_length, i, dbg_i;
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
if (!rt || !bufp)
{
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0)
{
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp)
{
exit(-1);
}
copy(rt, tmp, *res_length);
//free(tmp); // produces undefined output?
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
free(tmp);
free(bufp);
return rt;
}
The result of the following main function:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int b[] = { 3 };
int r, i;
int *rlength, *res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++)
{
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
Is:
Length = 2
i=2
i=7
My understanding of the first scenario, is that there is a memory leak for every subsequent tmp = sum(rt, *res_length, bufp, bufp_length, res_length); call. Which point, I decided to move the call to free(tmp) inside the for loop. Once moved, I had noticed that the output had changed, as follows:
Length = 4
i=1018670
i=4
i=2
i=7
I can see that the answer begins at i[3]. Why does the movement of the free(tmp) call, cause this effect?
My understanding is that tmp becomes a dangling pointer after the free() call. Then, it is re-assigned a value returned by the function sum() - which was retrieved by a call to malloc(). Which point, a memory leak would occur by placing the call to free() in its original location. As the value of tmp will change, only freeing the last pointer assigned to it.
Edit:
Below is the code for the supporting functions.
int *pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i, j;
int *padded;
if (nlength < 1 || new_length <= nlength)
{
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded)
{
return NULL;
}
if (side == LOW)
{
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
{
padded[j--] = n[i];
}
}
else
{
j = 0;
for (i = 0; i < nlength; i++)
{
padded[j++] = n[i];
}
}
return padded;
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
return res;
}
int *sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i, tmp, carry;
int *result, *trimmed, *op1, *op2;
enum SIDE side = LOW;
if (nlength == mlength)
{
op1 = n;
op2 = m;
}
else if (nlength > mlength)
{
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
else
{
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result)
{
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--)
{
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
{
carry = 0;
}
if (tmp >= 10)
{
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
if (carry > 0)
{
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
if (!trimmed)
{
return NULL;
}
return trimmed;
}
void copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++)
{
to[i] = from[i];
}
}
Update:
After implementing the changes suggested in the first post, a double free error has begun occurring, to debug, I added the following print statements to the power_arr(). The output following shows that tmp is being assigned the same value from sum(), as it received on the initial call. Why?
Updated code showing debug printf statements:
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
printf("first tmp = %d\n", tmp);
if (!tmp)
{
printf("tmp was null\n");
exit(-1);
}
copy(rt, tmp, *res_length);
printf("second tmp = %d\n", tmp);\
if (tmp != NULL)
{
printf("freeing tmp\n");
free(tmp);
tmp = NULL;
}
printf("tmp = %d\n", tmp);
}
The output:
first tmp = 11227072
second tmp = 11227072
freeing tmp
tmp = 0
first tmp = 11227072 <-- Why has the pointer value not changed?
second tmp = 11227072
freeing tmp <-- Double free now occuring.
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000ab4fc0 ***
Aborted
Update:
I believe I have tracked the bug down to the trim() function. I have posted the function with a loop to execute it 10 times in succession. As you can see in the output, trim() - which calls malloc(), returns the same pointer value on subsequent calls. Yet, each successive call to free does not trigger a double free error. Why is this the case?
int main()
{
int i, j, length;
int n[] = { 4, 5, 6 };
int m[] = { 0, 3, 5 };
int *num;
int *trimmed, *trimmed_length;
trimmed_length = &length;
for (i = 0; i < 10; i++)
{
num = (i % 2 == 0) ? n : m;
trimmed = trim(num, 3, trimmed_length);
if (!trimmed)
{
printf("trimmed was null\n");
exit(-1);
}
for (j = 0; j < *trimmed_length; j++)
{
printf("%d", trimmed[j]);
}
printf("\n");
free(trimmed);
}
exit(0);
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
printf("Returned pointer from trim() %d\n", res);
return res;
}
Output:
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
This also appears to be the behavior in my original question - which triggers a double free error. Why is the double free error not encountered in this particular situation?
You have a number of memory leak points and a full bug which is why the free fails [due to double free of the same pointer].
NOTE: I've done an update to this answer, but it's too big to fit here, so I've posted it as a second answer
I've joined all files into one so I could compile it [please pardon the gratuitous style cleanup], fixed the bug, and annotated all the hot spots [this compiles, but I didn't test it]:
#include <stdlib.h>
#include <stdio.h>
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free"
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
int *
pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i,
j;
int *padded;
if (nlength < 1 || new_length <= nlength) {
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded) {
return NULL;
}
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--) {
padded[j--] = n[i];
}
}
else {
j = 0;
for (i = 0; i < nlength; i++) {
padded[j++] = n[i];
}
}
return padded;
}
int *
trim(int *n, int nlength, int *res_length)
{
int i,
j;
int *res;
for (i = 0; i < nlength; i++) {
if (n[i] > 0) {
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res) {
return NULL;
}
j = 0;
while (i < nlength) {
res[j++] = n[i++];
}
return res;
}
int *
sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i,
tmp,
carry;
int *result,
*trimmed,
*op1,
*op2;
int padflg;
enum SIDE side = LOW;
// NOTE: this helps us remember whether to free op2 or not
padflg = 1;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
// case (3)
else {
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0) {
carry = 0;
}
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called pad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0) {
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
return trimmed;
}
void
copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++) {
to[i] = from[i];
}
}
int *
power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp,
*rt,
*bufp;
int bufp_length,
i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp) {
exit(-1);
}
copy(rt, tmp, *res_length);
// NOTE: this will now work because of the padflg changes in
// sum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
int
main(void)
{
int b[] = { 3 };
int r,
i;
int *rlength,
*res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++) {
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
UPDATE: I'm adding a second answer here because the new code example is too big to fit as an update to my previous one.
I found a few more issues. If you run the original test program repeatedly, it gives different [incorrect] answers on each run. That is, loop on it from a script.
I wasn't able to fix the remaining bug(s) with the existing functions and arguments. The sheer complexity was adding to the problems.
Once I realized what you were really trying to do (e.g. multiprecision math), I was able to apply several simplifications:
Instead of passing around int *vals, int *len everywhere, I created a "big number" struct:
- contains pointer to data and length
- length of data buffer can grow/shrink dynamically
- has reference count
You were doing your math in "big endian" mode. This made it necessary to reallocate frequently (e.g. as in pad and copy). This also added to the complexity. I switched this to use "little endian", which is much easier to work with. And, it's also a bit [considerably] faster.
I also noticed in your power function that you were using sum to do what was essentially rt += bufp;. So, I created a version sumeq that operated on power's rt value directly, which further simplified things.
Note: You can still do it big endian, but most packages I've seen use little. Still, the base routines to create/grow the struct are endian agnostic, and you could create big endian versions of the little endian functions I created [which came from your big endian functions]
Here's the actual code. It is buildable and runnable. It will test all versions, including your original code [with the issues].
// badfree -- test program
#define TSTDEFALL
// badfree/badfree.h -- badfree control
#ifndef _badfree_badfree_h_
#define _badfree_badfree_h_
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef DEBUG
#define dbgprtf(_fmt...) printf(_fmt)
#define dbgexec(_exec) _exec
#else
#define dbgprtf(_fmt...) /**/
#define dbgexec(_exec) /**/
#endif
#define dbgleshow(_num,_who) dbgexec(leshow(_num,0,_who))
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
fflush(stdout); \
_sysfault(); \
} while (0)
typedef unsigned int u32;
#if BIGDIG == 1
typedef char bigdig_t;
#elif BIGDIG == 2
typedef short bigdig_t;
#else
typedef int bigdig_t;
#endif
typedef bigdig_t *bigdig_p;
typedef const bigdig_t *bigdig_pc;
// multiprecision number control
struct bignum {
u32 num_opts; // options
const char *num_tag; // symbol name
int num_refcnt; // reference count
int num_curlen; // current length
int num_maxlen; // maximum length
bigdig_p num_base; // pointer to vector
};
typedef struct bignum bgn_t;
typedef bgn_t *bgn_p;
typedef const bgn_t *bgn_pc;
// num_opts definitions
#define BGNASTRUCT (1 << 0) // struct was allocated
#define BGNABASE (1 << 1) // num_base was allocated
#define BGNINITAV (1 << 2) // bgninit -- init values
#define BGNINITDUP (1 << 3) // bgninit -- init values
#define BGNTRIM (1 << 4) // trim number
#define BGNRAW (1 << 5) // output number in raw order
#define BGNACQUIRE(_num) \
bgnacquire(_num)
#define BGNRELEASE(_num) \
_num = bgnrelease(_num)
#define BGNINIT(_sym,_opt,_len...) \
_sym = bgninit(#_sym,_opt,_len)
#define BGNASSERT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) == (_opt)) \
break; \
sysfault("BGNASSERT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
#define BGNASSERT_NOT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) != (_opt)) \
break; \
sysfault("BGNASSERT_NOT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
#define MIN(_x,_y) (((_x) < (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free" and use of pointer after it has been freed
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
// test control
struct tstctl {
int tst_allow; // allow test to be performed
const char *tst_tag; // name of test
void (*tst_fnc)(void); // test function
};
typedef struct tstctl tstctl_t;
typedef tstctl_t *tstctl_p;
typedef const tstctl_t *tstctl_pc;
#define FORTSTALL(_tst) \
_tst = tstlist; _tst->tst_tag != NULL; ++_tst
int
main(int argc, char **argv);
void
dotests(void);
void
dotest(tstctl_p tst);
void
usage(void);
void
becopy(bigdig_t *to, const bigdig_t *from, int length);
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side);
bigdig_t *
betrim(const bigdig_t *n, int *res_length);
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length);
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length);
void
betest_orig(void);
void
betest_trim(void);
void
betest_show(bigdig_pc num,int len,const char *sym);
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...);
bgn_p
bgnacquire(bgn_p num);
bgn_p
bgnrelease(bgn_p num);
void
bgngrow(bgn_p num,int newlen);
void
bgnshrink(bgn_p num);
void
lecopy(bgn_p to,bgn_pc from);
bgn_p
lepad(bgn_pc num,int newlen);
void
letrim(bgn_p num);
int
_letrim(bgn_pc num);
void
leshow(bgn_pc num,u32 opt,const char *who);
void
lesumeq(bgn_p result,bgn_p op2);
bgn_p
le_power_arr_eq(bgn_pc num,int exp);
bgn_p
lesumrt(bgn_p n,bgn_p m);
bgn_p
le_power_arr_rt(bgn_pc n, int exp);
void
letest_lesumrt(void);
void
letest_lesumeq(void);
void
_sysfault(void);
void
reverse(bigdig_p arr, int length);
#define TSTDEF(_on,_fnc) \
{ .tst_allow = _on, .tst_tag = #_fnc, .tst_fnc = _fnc }
#ifdef TSTDEFALL
tstctl_t tstlist[] = {
TSTDEF(1,betest_orig),
TSTDEF(0,betest_trim),
TSTDEF(1,letest_lesumrt),
TSTDEF(1,letest_lesumeq),
{ .tst_tag = NULL }
};
#endif
#endif
// badfree/bemath -- big endian math
void
becopy(bigdig_t *to, const bigdig_t *from, int length)
{
int i;
for (i = 0; i < length; i++)
to[i] = from[i];
}
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side)
{
int i;
int j;
bigdig_t *padded;
if (nlength < 1 || new_length <= nlength) {
sysfault("bepad: length fault -- nlength=%d new_length=%d\n",
nlength,new_length);
return NULL;
}
padded = calloc(new_length,sizeof(bigdig_t));
if (!padded)
return NULL;
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
padded[j--] = n[i];
}
else {
j = 0;
for (i = 0; i < nlength; i++)
padded[j++] = n[i];
}
return padded;
}
bigdig_t *
betrim(const bigdig_t *n, int *res_length)
{
int i;
int j;
int nlength;
bigdig_t *res;
nlength = *res_length;
for (i = 0; i < nlength; i++) {
if (n[i] > 0)
break;
}
nlength -= i;
*res_length = nlength;
res = malloc(sizeof(bigdig_t) * nlength);
if (!res) {
sysfault("betrim: null malloc\n");
return NULL;
}
j = 0;
for (; i < nlength; ++i, ++j)
res[j] = n[i];
return res;
}
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length)
{
int i;
int tmp;
int carry;
bigdig_t *result;
bigdig_t *trimmed;
bigdig_t *op1;
bigdig_t *op2;
int padflg;
enum SIDE side = LOW;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = bepad(m, mlength, nlength, side);
padflg = 1;
}
// case (3)
else {
op1 = m;
op2 = bepad(n, nlength, mlength, side);
padflg = 2;
}
result = malloc(sizeof(bigdig_t) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
sysfault("besum: null fault -- padflg=%d op1=%p op2=%p result=%p\n",
padflg,op1,op2,result);
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
carry = 0;
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called bepad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0)
result[0] = carry;
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = betrim(result, sum_length);
FREEME(result);
return trimmed;
}
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length)
{
bigdig_t *tmp;
bigdig_t *rt;
bigdig_t *bufp;
int bufp_length;
int i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(bigdig_t) * 1000);
bufp = malloc(sizeof(bigdig_t) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
becopy(rt, n, nlength);
becopy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = besum(rt, nlength, bufp, bufp_length, res_length);
if (tmp == NULL)
sysfault("be_power_arr: null besum return\n");
nlength = *res_length;
becopy(rt, tmp, nlength);
// NOTE: this will now work because of the padflg changes in
// besum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
becopy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
// badfree/betest -- big endian tests
void
betest_orig(void)
{
#if 1
bigdig_t b[] = { 3 };
#else
const bigdig_t b[] = { 3 };
#endif
int rlength;
int exp;
bigdig_t *res;
exp = 3;
printf("\n");
printf("betest_orig: exp=%d\n",exp);
betest_show(b,1,"b");
rlength = 0;
res = be_power_arr(b, 1, exp, &rlength);
betest_show(res,rlength,"res");
FREEME(res);
}
void
betest_trim(void)
{
int i;
int j;
int length;
const bigdig_t n[] = { 4, 5, 6 };
const bigdig_t m[] = { 0, 3, 5 };
const bigdig_t *num;
bigdig_t *trimmed;
printf("\n");
printf("betest_trim:\n");
for (i = 0; i < 10; i++) {
num = (i % 2 == 0) ? n : m;
length = 3;
trimmed = betrim(num, &length);
if (!trimmed)
sysfault("betest_trim: trimmed was null\n");
printf("pass %d: num=%p trimmed=%p\n",i,num,trimmed);
for (j = 0; j < length; j++)
printf(" %d", trimmed[j]);
printf("\n");
FREEME(trimmed);
}
}
// betest_show -- show number
void
betest_show(bigdig_pc num,int len,const char *sym)
{
int i;
printf(" sym %s length %d --",sym,len);
for (i = 0; i < len; i++)
printf(" %d",num[i]);
printf("\n");
}
// badfree/bgn -- big number control
// bgninit -- create new number
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...)
// opt -- options (BGNINIT* -- has initializer data)
// maxlen -- length of number
{
va_list ap;
int i;
bgn_pc from;
bgn_p num;
va_start(ap,maxlen);
num = calloc(1,sizeof(bgn_t));
opt |= BGNASTRUCT;
opt |= BGNABASE;
num->num_opts = opt;
num->num_tag = tag;
num->num_refcnt = 1;
if (maxlen <= 0)
maxlen = 1;
from = NULL;
if (opt & BGNINITDUP) {
from = va_arg(ap,bgn_pc);
maxlen = MAX(maxlen,from->num_curlen);
}
num->num_maxlen = maxlen;
num->num_base = calloc(maxlen,sizeof(bigdig_t));
// initialize from varargs
if (opt & BGNINITAV) {
for (i = 0; i < maxlen; ++i)
num->num_base[i] = va_arg(ap,int);
num->num_curlen = maxlen;
}
// initialize by cloning data
if (opt & BGNINITDUP) {
maxlen = from->num_curlen;
for (i = 0; i < maxlen; ++i)
num->num_base[i] = from->num_base[i];
num->num_curlen = maxlen;
}
va_end(ap);
return num;
}
// bgnacquire -- increment reference count
bgn_p
bgnacquire(bgn_p num)
{
num->num_refcnt += 1;
return num;
}
// bgnrelease -- decrement reference count and deallocate
bgn_p
bgnrelease(bgn_p num)
{
if (--num->num_refcnt == 0) {
if (num->num_opts & BGNABASE)
FREEME(num->num_base);
if (num->num_opts & BGNASTRUCT)
FREEME(num);
// this zaps caller's pointer
num = NULL;
}
return num;
}
// bgngrow -- grow allocated number to given length
void
bgngrow(bgn_p num,int newlen)
{
int growlen;
int maxlen;
int i;
BGNASSERT(num,BGNABASE);
maxlen = num->num_maxlen;
growlen = newlen - maxlen;
if (growlen > 0) {
maxlen += growlen;
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * maxlen);
// zero extend the new area
for (i = num->num_maxlen; i < maxlen; ++i)
num->num_base[i] = 0;
num->num_maxlen = maxlen;
}
}
// bgnshrink -- shrink allocated number to current length
void
bgnshrink(bgn_p num)
{
int curlen;
BGNASSERT(num,BGNABASE);
curlen = num->num_curlen;
if (num->num_maxlen > curlen) {
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * curlen);
num->num_maxlen = curlen;
}
}
// badfree/lecom -- little endian math common
// lecopy -- copy big number
void
lecopy(bgn_p to,bgn_pc from)
{
int newlen;
int i;
dbgprtf("lecopy: ENTER\n");
dbgleshow(to,"lecopy");
dbgleshow(from,"lecopy");
newlen = from->num_curlen;
bgngrow(to,newlen);
for (i = 0; i < newlen; ++i)
to->num_base[i] = from->num_base[i];
to->num_curlen = newlen;
dbgleshow(to,"lecopy");
dbgprtf("lecopy: EXIT\n");
}
// lepad -- clone and pad number
bgn_p
lepad(bgn_pc num,int newlen)
{
int i;
int curlen;
bgn_p padded;
curlen = num->num_curlen;
#if 0
if ((curlen < 1) || (newlen <= curlen)) {
sysfault("lepad: length fault -- curlen=%d newlen=%d\n",curlen,newlen);
return NULL;
}
#endif
BGNINIT(padded,0,newlen);
if (!padded) {
sysfault("lepad: bgninit returned null\n");
return NULL;
}
// copy existing digits
for (i = 0; i < curlen; ++i)
padded->num_base[i] = num->num_base[i];
// zero extend the larger number
for (; i < newlen; ++i)
padded->num_base[i] = 0;
return padded;
}
// letrim -- get rid of leading zeroes by adjusting current length
void
letrim(bgn_p num)
{
num->num_curlen = _letrim(num);
}
// _letrim -- get rid of leading zeroes by adjusting current length
// RETURNS: trimmed length
int
_letrim(bgn_pc num)
{
int i;
int curlen;
curlen = num->num_curlen;
for (i = curlen - 1; i >= 0; --i) {
if (num->num_base[i] > 0)
break;
}
if (i <= 0)
i = 1;
return i;
}
// leshow -- show number
void
leshow(bgn_pc num,u32 opt,const char *who)
{
int curlen;
int i;
if (opt & BGNTRIM)
curlen = _letrim(num);
else
curlen = num->num_curlen;
if (who != NULL)
printf("%s: ",who);
printf("sym=%s ref=%d len=%d/%d",
num->num_tag,num->num_refcnt,curlen,num->num_maxlen);
printf(" trim=%s order=%s",
(opt & BGNTRIM) ? "yes" : "no",
(opt & BGNRAW) ? "raw" : "flip");
printf("\n");
if (who != NULL)
printf("%s:",who);
printf(" ");
if (opt & BGNRAW) {
for (i = 0; i < curlen; ++i)
printf(" %d",num->num_base[i]);
}
else {
for (i = curlen - 1; i >= 0; --i)
printf(" %d",num->num_base[i]);
}
printf("\n");
}
// badfree/lesumeq -- little endian sum / power (in-place)
// lesumeq -- do x += y
void
lesumeq(bgn_p result,bgn_p op2)
{
int op2len;
int i;
int tmp;
int carry;
int maxlen;
int minlen;
op2len = op2->num_curlen;
tmp = result->num_curlen;
maxlen = MAX(tmp,op2len);
minlen = MIN(tmp,op2len);
bgngrow(result,maxlen + 1);
carry = 0;
i = 0;
for (; i < minlen; ++i) {
tmp = result->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
++maxlen;
for (; i < maxlen; ++i) {
if (! carry)
break;
tmp = result->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
result->num_curlen = maxlen;
}
// le_power_arr_eq -- raise number to power
bgn_p
le_power_arr_eq(bgn_pc num,int exp)
{
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
BGNINIT(rtpwr,BGNINITDUP,1000,num);
BGNINIT(bufp,BGNINITDUP,1000,num);
ilim = num->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur)
lesumeq(rtpwr,bufp);
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
return rtpwr;
}
// badfree/lesumrt -- little endian sum / power (alloc/return mode)
bgn_p
lesumrt(bgn_p n,bgn_p m)
{
int i;
int tmp;
int carry;
int maxlen;
bgn_p rtsum;
bgn_p op1;
bgn_p op2;
dbgprtf("lesumrt: ENTER\n");
dbgleshow(n,"lesumrt");
dbgleshow(m,"lesumrt");
// case (1)
if (n->num_curlen == m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = BGNACQUIRE(m);
}
// case (2)
else if (n->num_curlen > m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = lepad(m,n->num_curlen);
}
// case (3)
else {
op1 = BGNACQUIRE(m);
op2 = lepad(n,m->num_curlen);
}
maxlen = MAX(n->num_curlen,m->num_curlen);
dbgprtf("lesumrt: PAD maxlen=%d\n",maxlen);
BGNINIT(rtsum,0,maxlen + 1);
carry = 0;
for (i = 0; i < maxlen; ++i) {
tmp = op1->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
rtsum->num_base[i] = tmp;
}
rtsum->num_base[i] += carry;
rtsum->num_curlen = maxlen + 1;
BGNRELEASE(op1);
BGNRELEASE(op2);
dbgleshow(rtsum,"lesumrt");
dbgprtf("lesumrt: EXIT\n");
return rtsum;
}
bgn_p
le_power_arr_rt(bgn_pc n, int exp)
{
bgn_p tmp;
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
dbgprtf("le_power_arr_rt: ENTER\n");
BGNINIT(rtpwr,BGNINITDUP,1000,n);
BGNINIT(bufp,BGNINITDUP,1000,n);
ilim = n->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur) {
tmp = lesumrt(rtpwr,bufp);
if (tmp == NULL)
sysfault("le_power_arr_rt: null lesumrt return\n");
lecopy(rtpwr,tmp);
BGNRELEASE(tmp);
}
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
dbgprtf("le_power_arr_rt: EXIT\n");
return rtpwr;
}
// badfree/letest -- little endian tests
void
letest_lesumrt(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumrt: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumrt");
res = le_power_arr_rt(b,exp);
leshow(res,0,"letest_lesumrt");
BGNRELEASE(res);
BGNRELEASE(b);
}
void
letest_lesumeq(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumeq: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumeq");
res = le_power_arr_eq(b,exp);
leshow(res,0,"letest_lesumeq");
BGNRELEASE(res);
BGNRELEASE(b);
}
// badfree/util -- utility functions
void
_sysfault(void)
{
exit(1);
}
void
reverse(bigdig_p arr, int length)
{
int lhs;
int rhs;
bigdig_t tmp;
for (lhs = 0, rhs = length - 1; lhs < rhs; ++lhs, --rhs) {
tmp = arr[lhs];
arr[lhs] = arr[rhs];
arr[rhs] = tmp;
}
}
int opt_fork;
int opt_T;
// main -- main program
int
main(int argc, char **argv)
{
char *cp;
tstctl_p tst;
int tstno;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'f':
opt_fork = 1;
break;
case 'h':
usage();
break;
case 'T':
opt_T = atoi(cp + 2);
break;
default:
usage();
break;
}
}
if (opt_T <= 0)
opt_T = 1;
for (FORTSTALL(tst))
tst->tst_allow = (argc <= 0);
for (; argc > 0; --argc, ++argv) {
cp = *argv;
for (FORTSTALL(tst)) {
if (strcmp(tst->tst_tag, cp) == 0)
tst->tst_allow = 1;
}
}
for (tstno = 0; tstno < opt_T; ++tstno)
dotests();
return 0;
}
// dotests -- perform tests
void
dotests(void)
{
tstctl_p tst;
pid_t pid;
int status;
for (FORTSTALL(tst)) {
if (!tst->tst_allow)
continue;
if (!opt_fork) {
dotest(tst);
continue;
}
pid = fork();
if (pid) {
waitpid(pid, &status, 0);
continue;
}
dotest(tst);
exit(0);
}
}
// dotest -- perform test
void
dotest(tstctl_p tst)
{
tst->tst_fnc();
}
// usage -- show usage
void
usage(void)
{
tstctl_pc tst;
printf("usage: [options] [test names]\n");
printf("\n");
printf("options:\n");
printf(" -f -- run tests in forked child\n");
printf(" -T<repeat> -- test repeat count\n");
printf("\n");
printf("tests:\n");
for (FORTSTALL(tst))
printf(" %s\n", tst->tst_tag);
exit(1);
}
If I had two strings:
a = "1234"
b = "4321"
I could add the two numbers together like this:
for(i=0; i<width-1; i++){
sum = (a[width-2-i]-48) + (b[width-2-i]-48) + carry;
carry = 0;
if(sum > 9){
carry = 1;
sum-=10;
}
answer[i] = sum+48;
}
if(carry) answer[i++] = carry+48;
answer[i]= 0;
And then reverse it (width is equal to strlen(a)).
How could I do the same thing if the following?
a = "12345"
b = "4321"
Would I need to reallocate memory? Or what?
(BTW - the problem I'm trying to solve is using many numbers all with 50 digits, so strtoul or strtoull is out of the question as I understand. Here's my code so far.)
int getcharval(const char *s, int idx) {
if (idx < strlen(s))
return s[strlen(s) - idx - 1] - 48;
return 0;
}
void add() {
const char *a = "1234";
const char *b = "13210";
char answer[256];
int i, wa=strlen(a), wb=strlen(b), width, sum, carry;
width = wa > wb ? wa : wb;
for(i=0; i<width; i++){
char ca = getcharval(a, i);
char cb = getcharval(b, i);
printf("%d %d\n", ca, cb);
sum = ca + cb + carry;
carry = 0;
if(sum > 9){
carry = 1;
sum-=10;
}
answer[i] = sum+48;
}
if(carry) answer[i++] = carry+48;
answer[i]= 0;
for (i = 0; i < strlen(answer) / 2; i++) {
char t = answer[i];
answer[i] = answer[strlen(answer) - i - 1];
answer[strlen(answer) - i - 1] = t;
}
printf("%s\n", answer);
}
If you insist on using the "elementary school addition", find the length of both strings, advance to their ends, and then move back until the shorter string's length is exhausted. Then continue moving in only the longer string, assuming that the remaining digits of the shorter string are zeros:
12345
04321
You need to move all the way to the beginning of the longer string, and process the carry there. Note that you need to allocate a new result anyway, because adding two N-digit numbers may result in a N+1-digit number due to the carry.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define c2d(c) (c-'0')
#define d2c(c) (c+'0')
char* add(const char *a, const char *b, char *ans){
int alen, blen;
int i, carry=0;
char *wk;
char *awk=strdup(a);
char *bwk=strdup(b);
alen=strlen(strrev(awk));
blen=strlen(strrev(bwk));
if(alen<blen){
alen ^= blen;blen ^= alen;alen ^= blen;//swap
wk = awk ; awk = bwk ; bwk = wk;
}
ans[alen+1]=ans[alen]='\0';
for(i=0;i<alen;++i){
int sum = c2d(awk[i])+(i<blen ? c2d(bwk[i]): 0)+carry;
ans[i] = d2c(sum % 10);
carry = sum / 10;
}
if(carry){
ans[i++]='1';
}
free(awk);
free(bwk);
return strrev(ans);
}
int main(){
const char *a="12345";
const char *b="4321";
char ans[6];
printf("{%s}+{%s}={%s}\n", a, b, add(a,b, ans));
return 0;
}
cited from C - Adding the numbers in 2 strings together if a different length
answer, I write a more readable codeļ¼
void str_reverse(char *beg, char *end){
if(!beg || !end)return;
char cTmp;
while(beg < end){
cTmp = *beg;
*beg++ = *end;
*end-- = cTmp;
}
}
#define c2d(c) (c - '0')
#define d2c(d) (d + '0')
void str_add(const char* s1, const char* s2, char* s_ret){
int s1_len = strlen(s1);
int s2_len = strlen(s2);
int max_len = s1_len;
int min_len = s2_len;
const char *ps_max = s1;
const char *ps_min = s2;
if(s2_len > s1_len){
ps_min = s1;min_len = s1_len;
ps_max = s2;max_len = s2_len;
}
int carry = 0;
int i, j = 0;
for (i = max_len - 1; i >= 0; --i) {
// this wrong-prone
int idx = (i - max_len + min_len) >=0 ? (i - max_len + min_len) : -1;
int sum = c2d(ps_max[i]) + (idx >=0 ? c2d(ps_min[idx]) : 0) + carry;
carry = sum / 10;
sum = sum % 10;
s_ret[j++] = d2c(sum);
}
if(carry)s_ret[j] = '1';
str_reverse(s_ret, s_ret + strlen(s_ret) - 1);
}
test code as below:
void test_str_str_add(){
char s1[] = "123";
char s2[] = "456";
char s3[10] = {'\0'};
str_add(s1, s2, s3);
std::cout<<s3<<std::endl;
char s4[] = "456789";
char s5[10] = {'\0'};
str_add(s1, s4, s5);
std::cout<<s5<<std::endl;
char s7[] = "99999";
char s8[] = "21";
char s9[10] = {'\0'};
str_add(s7, s8, s9);
std::cout<<s9<<std::endl;
}
output:
579
456912
100020
int num(char x,int len){
if(len <0)
return 0;
return ((x=='1') ? 1 : (x=='2') ? 2 : (x=='3') ? 3 : (x=='4') ? 4 : (x=='5') ? 5 : (x=='6') ? 6 : (x=='7') ? 7 : (x=='8') ? 8 : 9);
}
int main(){
int result[100];
int i=0;
char num1[] = "123456789123456789";
char num2[] = "1234567811111111111111111111";
int carry = 0;
int l1= strlen(num1)-1;
int l2 = strlen(num2)-1;
int result1;
while(1){
if(l1 < 0 && l2 <0 && carry == 0)
break;
result1 = num(num1[l1],l1) + num(num2[l2],l2);
l1--;
l2--;
if(carry>0){
result1 +=carry;
carry = 0;
}
carry = result1 / 10;
result[i] = (result1 % 10);
i++;
}
i--;
printf("\n");
while(i>=0){
printf("%d",result[i]);
i--;
}
}