Chinaunix

标题: 如何判断申请很大数组是否成功? [打印本页]

作者: mgqw    时间: 2010-06-03 11:09
标题: 如何判断申请很大数组是否成功?
本帖最后由 mgqw 于 2010-06-03 14:38 编辑

昨天去一个华为外包公司面试问到的这个问题,以前确实没有考虑过类似这样的问题:
比如在一个嵌入式系统上申请一个超级大的数组 int a[999999], 我如何知道这个数组是否申请成功了呢?
我当时回答是用malloc返回直判断是否为NULL来确定申请是否成功。

但malloc/free就使用原则来说,可以不有就尽量别用;有没有什么办法能判断int a[999999]是否申请成功呢??
作者: star1983653    时间: 2010-06-03 11:37
本帖最后由 star1983653 于 2010-06-03 11:44 编辑

这问题不考虑操作系统和编译器,单单就C语言本身没办法知道的吧。
在数据段和在栈上分配空间是不一样的。
我觉得:
在数据区分配的话,过大的数据,首先编译器会报错。其次通过编译,连接器会报错。
在栈上,也是编译器会报错,通过编译后要在运行时才能查的出来。
作者: peidright    时间: 2010-06-03 11:40
本帖最后由 peidright 于 2010-06-03 11:43 编辑

{:3_191:},这个问题。。。。,。。。。能否有异常机制?

感觉分配失败, 直接就挂了。 考虑alloca? 从栈中分配? 这个会不会与malloc有类似的不适合呢?
,。。哎,还是很菜啊
作者: rain_fish    时间: 2010-06-03 12:55
异常肯定是不行的。
作者: wwdwwd    时间: 2010-06-03 13:09
malloc如果申请失败了会发生段错误,在信号处理函数里面能捕捉到这种错误,在栈上分配内存过大了也会发生段错误,但是这时候在信号处理函数中就捕捉不到了
作者: c/unix    时间: 2010-06-03 13:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: cugb_cat    时间: 2010-06-03 13:38
malloc如果申请失败了会发生段错误,在信号处理函数里面能捕捉到这种错误,在栈上分配内存过大了也会发生段 ...
wwdwwd 发表于 2010-06-03 13:09

你听谁说的malloc失败就段错误??
作者: shmild    时间: 2010-06-03 13:44
本帖最后由 shmild 于 2010-06-03 13:47 编辑

malloc失败是返回一个NULL值,判断返回值是否是NULL
new的话用不起来这招,只能捕获异常
作者: wwdwwd    时间: 2010-06-03 15:05
你听谁说的malloc失败就段错误??
cugb_cat 发表于 2010-06-03 13:38

不用听说,我测试过,linux下,当然不一定每次malloc分配失败都发生段错误,可能是分配的太大了才发生。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>

  4. void signal_handler(int signo) {
  5.         printf("get signal %d\n",signo);
  6. }

  7. void test(void) {
  8.         int  *a = malloc(1000000000);
  9.         printf("%d\n",a[0]);
  10. }
  11. int main()
  12. {
  13.     struct sigaction act;
  14.     sigemptyset(&act.sa_mask);
  15.     act.sa_handler = signal_handler;
  16.     sigaction(SIGSEGV, &act, NULL);
  17.         test();
  18.         return 0;
  19. }
复制代码

作者: c/unix    时间: 2010-06-03 15:23
提示: 作者被禁止或删除 内容自动屏蔽
作者: rain_fish    时间: 2010-06-03 15:26
回复 9# wwdwwd


    朋友,问题出在这里:printf("%d\n",a[0]);,申请失败了,是NULL,你怎么能打印呢?
作者: wwdwwd    时间: 2010-06-03 16:11
错了就是错了,为啥这么要面子呢。
c/unix 发表于 2010-06-03 15:23

我知道是怎么回事了,下面的printf导致的段错误,多谢指正!
作者: wwdwwd    时间: 2010-06-03 16:12
回复  wwdwwd


    朋友,问题出在这里:printf("%d\n",a[0]);,申请失败了,是NULL,你怎么能打印呢? ...
rain_fish 发表于 2010-06-03 15:26

嗯,对,忘了判断了。
作者: wwdwwd    时间: 2010-06-03 16:26
cugb_cat兄,不好意思,刚才是我测试的问题。我刚才发现另外一个问题:如果malloc失败后继续使用,会导致段错误,但这时的段错误能用信号捕捉到;如果是栈上分配错误时后使用导致的段错误则捕获不到。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>

  4. void signal_handler(int signo) {
  5.         printf("get signal %d\n",signo);
  6. }

  7. void test(void) {
  8.         unsigned int  a[100000000];
  9.         printf("%d\n",a[0]);
  10. }
  11. int main()
  12. {
  13.     struct sigaction act;
  14.     sigemptyset(&act.sa_mask);
  15.     act.sa_handler = signal_handler;
  16.     sigaction(SIGSEGV, &act, NULL);
  17.     test();
  18.     return 0;
  19. }
复制代码
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <signal.h>

  4. void signal_handler(int signo) {
  5.         printf("get signal %d\n",signo);
  6. }

  7. void test(void) {
  8.         int  *a = malloc(1000000000);
  9.         printf("%d\n",a[0]);
  10. }
  11. int main()
  12. {
  13.     struct sigaction act;
  14.     sigemptyset(&act.sa_mask);
  15.     act.sa_handler = signal_handler;
  16.     sigaction(SIGSEGV, &act, NULL);
  17.         test();
  18.         return 0;
  19. }
复制代码

作者: chinesedragon    时间: 2010-06-03 17:03
好高深啊
作者: shmild    时间: 2010-06-03 18:37
cugb_cat兄,不好意思,刚才是我测试的问题。我刚才发现另外一个问题:如果malloc失败后继续使用,会导致段 ...
wwdwwd 发表于 2010-06-03 16:26



发之前自己先好好想想啊,堆上分配为什么会段错误?
是因为malloc失败,返回指针是NULL,你打印a[0]就是操作空指针,你捕捉的信号是操作空指针的硬件信号
你改成在栈上分配,失败后a是一个随机值,很可能不是NULL或者其他禁止访问的内存,你打印a[0]当然有可能没问题了
作者: cugb_cat    时间: 2010-06-03 19:06
cugb_cat兄,不好意思,刚才是我测试的问题。我刚才发现另外一个问题:如果malloc失败后继续使用,会导致段 ...
wwdwwd 发表于 2010-06-03 16:26


http://www.linux-ask.com/Linux%B1%E0%B3%CC%CE%CA%CC%E2/4672.html
这篇文章应该可以帮到你
因为信号处理函数的上下文与进程栈的上下文是同一个,即使用相同的ebp和esp,当把栈空间耗尽时,已经没有栈空间可以供信号处理函数使用了,所以,就无法对捕捉到的SIGSEGV信号做出处理了。
如果是堆溢出导致的SIGSEGV,是可以通过捕捉该信号来做善后处理的,但如果是由于函数递归次数太多,导致的SIGSEGV,则是无法进行善后了。
或许可以通过使用守护线程,将被守护的线程的esp强制往回移动,以跳出无尽的递归,但这个方法尚不知是否可用,因为我还不清楚保存线程上下文的内存是属于核态还是用户态,如果是核态,则无能为力了。
还有一种方式,就是通过堆空间来将栈增大,提供非常的栈空间,但这样尚不能彻底解决无穷递归导致的栈耗尽,毕竟再大都是有限的。
作者: mgqw    时间: 2010-06-04 12:36
目前还是没有发现比检查malloc返回是否为NULL更好的办法{:3_190:}{:3_190:}{:3_190:}
作者: yikaikai    时间: 2010-06-04 13:54
好像还是没有回答楼主的问题,malloc 申请可以判断有没有出错,但是char AA[10000000000000000000000000000000000000000000000]这样的怎么判断呢?
作者: cugb_cat    时间: 2010-06-04 14:09
好像还是没有回答楼主的问题,malloc 申请可以判断有没有出错,但是char AA[10000000000000000000000000000 ...
yikaikai 发表于 2010-06-04 13:54


这个判断不了,因为这个是在编译时就确定了的,如果是在栈上,则为该数组分配栈空间时,才会出错,即调用定义该数组的函数时,而且捕捉不到SIGSEGV信号,原因我前面已经说了,如果是全局变量,程序一启动就会出错了。
作者: 没本    时间: 2010-06-04 15:48
一个嵌入式系统,是什么CPU没说,什么操作系统没说,能讨论的似乎不多。
作者: yulihua49    时间: 2010-06-04 22:06
malloc如果申请失败了会发生段错误,在信号处理函数里面能捕捉到这种错误,在栈上分配内存过大了也会发生段 ...
wwdwwd 发表于 2010-06-03 13:09



    malloc失败返回NULL,你误用了NULL才会段失败。小系统用大资源最好malloc,安全些。用栈太危险。
大系统里用小资源,用栈方便。
作者: djking1986    时间: 2010-06-05 11:18
期待。。答案。。
作者: 思一克    时间: 2010-06-05 12:22
如果不用malloc(嵌入式不用最好),
用全局变量
int a[NNNNN];
形式的大数组。

要知道成功与否,那么
int a[NNNNN] = {0, }
即可。
这样,数组在程序中(程序变的很大),程序运行时候申请物理内存,如果程序能运行了,那么一定是成功了得到内存的。如果程序不能运行就是无内存

这样的:
int a[NNNN]; 不行。程序很小,但运行时候不申请物理内存。等用到的时候就可能是没有的。

如果用malloc, 判断返回值仅仅是有虚拟内存。不代表有物理的。OS有个控制malloc的参数,可以在物理的不够的时候让malloc返回NULL.
作者: 思一克    时间: 2010-06-05 12:24
malloc失败返回NULL,你误用了NULL才会段失败。小系统用大资源最好malloc,安全些。用栈太危险。 ...
yulihua49 发表于 2010-06-04 22:06



MALLOC分配失败不会有段错误。如果有,那是库函数有BUG.
作者: mgqw    时间: 2010-06-05 13:30
如果不用malloc(嵌入式不用最好),
用全局变量
int a[NNNNN];
形式的大数组。

要知道成功与否,那 ...
思一克 发表于 2010-06-05 12:22



    恩,版主这个方法不错,全局变量在程序一开始跑就申请,申请成功就继续跑,申请失败程序就完蛋。
作者: cugb_cat    时间: 2010-06-05 20:36
如果不用malloc(嵌入式不用最好),
用全局变量
int a[NNNNN];
形式的大数组。

要知道成功与否,那 ...
思一克 发表于 2010-06-05 12:22


嗯,全局变量可以这么搞,但是栈上的局部变量就不行了。
版主说的这个是rss段和数据段的区别了。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2