free函数如何正确使用,求大神解答。。

心之痕 2014-01-09 11:44:57
这是我做的ACM的一道题,有个地方实在想不明白错在哪。。求大神帮看看
先上题:

It was said that when testing the first computer designed by von Neumann, people gave the following problem to both the legendary professor and the new computer: If the 4th digit of 2^n is 7, what is the smallest n? The machine and von Neumann began computing at the same moment, and von Neumann gave the answer first.

Now you are challenged with a similar but more complicated problem: If the K-th digit of M^n is 7, what is the smallest n?

Input

Each case is given in a line with 2 numbers: K and M (< 1,000).

Output

For each test case, please output in a line the smallest n.

You can assume:

The answer always exist.
The answer is no more than 100.
Sample Input

3 2
4 2
4 3
Sample Output

15
21
11

题目很简单,但用到大数乘法,以下是我的代码


#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void reverse(char *s)
{
int len = strlen(s);
int i, j;
char c;
for (i = 0, j = len - 1; i < j; i++, j--)
{
c = *(s + i);
*(s + i) = *(s + j);
*(s + j) = c;
}
}

char *appendTailZero(char *s, int zeros)
{
int i, len = strlen(s);
char *r = malloc(len + zeros + 1);
for (i = 0; i < len; i++)
*(r + i) = *(s + i);
for (i = len; i < len + zeros; i++)
*(r + i) = '0';
*(r + len + zeros) = '\0';
return r;
}

char *add(char *s1, char *s2)
{
int l1 = strlen(s1);
int l2 = strlen(s2);
int len = l1 > l2 ? l1 : l2;
char *r = malloc(len + 2);
int i, prev = 0, a, b, sum;
for (i = 0; i < len; i++)
{
a = l1 - 1 - i >= 0 ? *(s1 + l1 - 1 - i) - '0' : 0;
b = l2 - 1 - i >= 0 ? *(s2 + l2 - 1 - i) - '0' : 0;
sum = a + b + prev;
*(r + i) = sum > 9 ? sum - 10 + '0' : sum + '0';
prev = sum > 9 ? 1 : 0;
}
if (prev)
{
*(r + len) = '1';
*(r + len + 1) = '\0';
}
else
*(r + len) = '\0';
reverse(r);
return r;
}

char *multiplyHelper(char *s1, int digit)
{
int i, res, prev = 0, len = strlen(s1);
char *r = malloc(len + 2);
if (!digit)
{
*r = '0';
*(r + 1) = '\0';
return r;
}

for (i = 0; i < len; i++)
{
res = (*(s1 + len - 1 - i) - '0') * digit + prev;
*(r + i) = res % 10 + '0';
prev = res / 10;
}
if (prev)
{
*(r + len) = prev + '0';
*(r + len + 1) = '\0';
}
else
*(r + len) = '\0';
reverse(r);
return r;
}

char *multiply(char *s1, char *s2)
{
if (strlen(s1) < strlen(s2))
{
char *temp;
temp = s1;
s1 = s2;
s2 = temp;
}
int l2 = strlen(s2);
int i;
char *t, *zt;
char *sum = malloc(2);
char *tp;
sum = "0";
for (i = 0; i < l2; i++)
{
tp = sum;
t = multiplyHelper(s1, *(s2 + l2 - 1 - i) - '0');
zt = appendTailZero(t, i);
sum = add(tp, zt);
free(t);
free(zt);
free(tp);//加入这行代码提交就报Runtime Error, 不加就Accepted,但是内存占用很大
}
return sum;
}

int main()
{
int k;
char m[5];
while (scanf("%d %s", &k, m) != EOF)
{
char *r = m;
int count = 1;
while (strlen(r) < k)
{
r = multiply(r, m);
count++;
}
r = r + strlen(r) - k;
while (*r != '7')
{
r = multiply(r, m);
r = r + strlen(r) - k;
count++;
}
printf("%d\n", count);
}
return 0;
}


疑问就出在我注释的那行代码上,由于乘法中间用到了加法,因此每次都会产生加和的临时变量,我想把这些内存free掉,但是一加上这行代码提交就报Runtime Error, 在本地运行没有问题,去掉后提交可以过,但是内存占用很大。想问问这个地方free使用哪里出错了?我free的变量都是前一次的sum结果。。还有最后main函数里面每次做乘法的临时变量内存也可以free,但是加了类似代码也报错。。自己实在想不明白,求大神们帮看看啊,新手分不多见谅
...全文
273 9 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
心之痕 2014-01-10
  • 打赏
  • 举报
回复
终于知道第一个free那边为什么会错了。。原来是去free了一个常量字符串,我以为malloc之后就能调free,中间指向常量0后那个malloc(2)完全就没用到了 ALNG的思路真的不错,针对题目给出了特定的省时省内存的解法,收获不少,感谢。。其实我原先写了个通用点的大数加乘法看这题有用到就拷过来用了,好了结贴了
肖邦之离歌 2014-01-10
  • 打赏
  • 举报
回复
free函数只能释放通过malloc、realloc、calloc函数开辟的内存,你的代码里sum开始是malloc的,但是你紧接着就把sum指向了“0”(这里实际上已经造成了内存泄漏),后面tp又指向了sum指向的地址即“0”,这样的地址不是通过上述三个函数分配的,所以会报错。
孩皮妞野 2014-01-10
  • 打赏
  • 举报
回复
闲的无聊,我实现了一遍,你先自己做在用我的对照吧。 好久不写C了,尽量按照记忆中的C写的,难保没有露马脚的地方 :)

/*
It was said that when testing the first computer designed by von Neumann, people gave the 
following problem to both the legendary professor and the new computer: If the 4th digit 
of 2^n is 7, what is the smallest n? The machine and von Neumann began computing at the 
same moment, and von Neumann gave the answer first.

Now you are challenged with a similar but more complicated problem: If the K-th digit of 
M^n is 7, what is the smallest n?

Input

Each case is given in a line with 2 numbers: K and M (< 1,000).

Output

For each test case, please output in a line the smallest n.

You can assume:

The answer always exist.
The answer is no more than 100.
Sample Input

3 2
4 2
4 3
Sample Output

15
21
11
*/

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
 
int find_n(int k, int m);


int main()
{
	int k,m;
    while (scanf("%d %d", &k, &m) == 2)
        printf("%d\n", find_n(m,k));
    return 0;
}

/* test if the digit'th(starting from 0) digit of num's decimal representation is 7
 * return 1 when test true, false otherwise.
 *
 * eg. test7(157434,2) is true, while test7(157432,3) is false
 * 
 * note: num should be between 0 and 10^9-1
 *        and digit should be 0 to 8
 */
int test7(long num, int digit)
{
	char _[10];
	sprintf(_,"%ld", num);
	return _[digit]=='7';
}

/* for debug only.
 * I leave it behind because it sheds some light on how the large number
 * should be interpreted
 *
 */
void dump(long * p, int cnt)
{
	printf("%d", p[cnt-1]);
	for(int i=cnt-2; i>=0; --i)
		printf("%09d",p[i]);
	printf("\n");
}


void multiply(long* p, int* cnt_element, int m);

/* given integer k and m, find the smallest integer n such that
 * the k'th digit of m^n (decimal representation) is 7
 *
 */
int find_n(int k, int m)
{
	// sizeof(long) is guaranteed to be 4
	long array[34];
	int cnt=1, // number of elements currently filled
		n=1; // the value that we are looking for
	array[0]=m; // we will put least significant int(s) in element 0

	--k; // so that first digit is the 0th digit 
	while( cnt< k/9 || !test7(array[k/9], k%9) )
	{
		++n;
		multiply(array, &cnt, m);
	}
	return n;
	
}

/* our ad hoc large number multiplication function
 * param: p: points to array of long's which represent a large number. note the least
 *          significant element is the 0th element and each element should be between
 *          0 and 10^9-1;
 *        pcnt: points to number of elements actually used in the above array
 *        m: number to multiply into the large number(no more than 100 according to problem description)   
 *
 *
 */
void multiply(long* p, int* pcnt, int m)
{
	long long v=0;
	long carry=0;
	for(int i=0; i<*pcnt; ++i)
	{
		v=carry+(long long)p[i]*m;
		p[i]=(long)(v%1000000000);
		carry=(long)(v/1000000000);
	}
	if(carry)
		p[(*pcnt)++]=carry;
}

孩皮妞野 2014-01-10
  • 打赏
  • 举报
回复
讨厌的CSDN不许连续回复超过3次! 回到这题,如果没有聪明的方法(估计有), 就用你的暴力法,那你的乘法实现还是太低效率了,即使写对了也10有8、9会timeout. 其实根本不需要那么多次动态分配内存, 已知M<1000, n<100, 所以M^n不大于10^300, 你直接在堆上开一个300的数组,就对它操作,这样一次malloc和free都不用,会大大提高你的程序的效率。 还可以再提高,你用一个字符来表示一个十进制的数位,这当然可以得到正确的结果,但效率还可以提高。 可以相信目标机器不会低于32位处理器,更准确的说法应该是目标机器上的c编译器的sizeof(int)不会低于4. 你完全可以用size_t或者int数组(instead of char[]), 在int间用10^9进位,每个int降级到10^9进位,即如果一个整数存的是10^9-1, 对它increment后,它会归0,而高位整数进一......希望你听懂了我的意思。这样的好处是计算更紧凑而保留了快速检查第k位是否为7的能力...... 如果你能看懂我上面的意思,建议你用那个思路再实现一遍,甚至对比实现一遍,比较两者的效率,假定可以通过ACM的机器检测。 哦,你没必要定义乘法(*),定义自乘(*=)就好了。形如: void multiply_by(int self[], int num); 其中的self是一个足够装下10^300次方,成员间10^9进位的整型数组(啰嗦一下,每个成员装10^9, 10^300需要34个这样的int, 可以想一下你原来用成员用char,成员间10进位的方案要用300个元素, 哪一个会更快?) 你先试试,搞不定再问吧。
大奶兔白糖 2014-01-10
  • 打赏
  • 举报
回复
第一次进循环的时候,sum="0",tp=sum,这时候tp指向了常量字符串“0”,这种怎么能free呢?
心之痕 2014-01-10
  • 打赏
  • 举报
回复
引用 1 楼 ALNG 的回复:

    char *sum = malloc(2);
    char *tp;
    sum = "0";
    for (i = 0; i < l2; i++)
    {
        tp = sum;
        t = multiplyHelper(s1, *(s2 + l2 - 1 - i) - '0');
        zt = appendTailZero(t, i);
        sum = add(tp, zt);
        free(t);
        free(zt);
        free(tp);//假设在这free
    }
    return sum;
}
tp就是sum, 循环体第二次再进入时那个指针就已经free掉了, tp和sum都指向未定义的内存位置了。不可以在循环体内free. 又因为你把返回值放在那里,如果你在本函数结束前free掉tp(亦即sum),调用函数使用返回值的时候又会发生访问错误,所以应该由调用者free.
谢谢你的解答,下面两个回复我能理解,这里还不是很清楚,以我的理解,在第一次进入循环开始,tp和sum都指向0字符串,而经过加和以后,sum指向add函数返回的新的内存区域,而我没有再tp=sum,这时候tp应该还是指向0字符串啊?难道这时tp和sum都是指向add后的新的内存区域,我free(tp)把加和的结果free掉了?
孩皮妞野 2014-01-10
  • 打赏
  • 举报
回复
说一说你的multiply的设计,每一个函数原型都是一份合同,你的mutiply函数这份合同是这样的: s1指向乘数1字符数组的指针,s2指向乘数2字符数组的指针; 返回指向存放两者相乘的运算结果的字符数组的指针,这个字符数组是动态用malloc从堆中分配的,在使用完毕后free之是调用者的责任. 再看你的代码

        while (strlen(r) < k)
        {
            r = multiply(r, m);
            count++;
        }
第一遍的时候r就是m, 等于计算m的平方,这个确实不需要free, 第二遍的时候那个r已经是动态分配的了,你如果不free掉它就直接把multiply的运算结果(一个新的动态分配的内存指针)赋给r, 那么前一次分配的内存就泄露了,如此反复,除了最后一个有用,之前的都泄露了......
孩皮妞野 2014-01-10
  • 打赏
  • 举报
回复

int main()
{
    int k;
    char m[5];
    while (scanf("%d %s", &k, m) != EOF)
    {
        char *r = m;
        int count = 1;
        while (strlen(r) < k)
        {
            char * tmp=multiply(r, m);
            if(r!=m)
                free(r);
            r = tmp;
            count++;
        }
        r += strlen(r) - k;
        while (*r != '7')
        {
            char *tmp=multiply(r, m);
            if(r!=m) // you should be able to skip this line!!
                free(r);
            r=tmp;
            r += strlen(r) - k;
            count++;
        }
        printf("%d\n", count);
    }
    return 0;
}
对应的, main应该做如上修改。没有查你的程序的逻辑和其他问题,不保证能通过。
孩皮妞野 2014-01-10
  • 打赏
  • 举报
回复

    char *sum = malloc(2);
    char *tp;
    sum = "0";
    for (i = 0; i < l2; i++)
    {
        tp = sum;
        t = multiplyHelper(s1, *(s2 + l2 - 1 - i) - '0');
        zt = appendTailZero(t, i);
        sum = add(tp, zt);
        free(t);
        free(zt);
    }
    return sum;
}
tp就是sum, 循环体第二次再进入时那个指针就已经free掉了, tp和sum都指向未定义的内存位置了。不可以在循环体内free. 又因为你把返回值放在那里,如果你在本函数结束前free掉tp(亦即sum),调用函数使用返回值的时候又会发生访问错误,所以应该由调用者free.

70,022

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧