19 12
发新话题
打印

声明的数组越界,为何还能正确运行,很怪,没有几行代码,大家看看!!!!

回去分析了一下:nm a.out|sort,发现intary后是__exit_function,结果用unistd.h的_exit(0)可以通过5 z! N8 ?4 B! s  }3 Y2 a
#include<unistd.h>9 j! I3 S+ |1 P- I2 a
...( `7 [3 d7 E/ I. u4 u3 e! m- B
main()& M2 O/ x" [0 G. j( d
{
/ h- e5 `* ]/ [& N1 R...8 M' t4 r4 ?+ y1 h2 c/ [
  printf("End\n");
$ S4 i& r$ y4 Y0 W9 ~* S- M  _exit(0);: P$ O+ ]1 B6 w. t6 m
}      

TOP

#include <stdio.h>
! F2 n: X1 }( V#include <stdlib.h>5 O3 m1 B4 l8 J$ p
) ^  _# g( M$ c9 K2 B$ z
#define BIGNUM 50
; b& x- V) R. ^$ V; t( [: L' W% [/ I
# N& J* |; b( E# r0 fvoid index_to_the_moon(int arr[]);, Y7 J% {+ C/ o7 n

* a! C7 f  J% _# X- Q- k/ T$ l/ Lint main(void)4 r. D' U' O$ ~& w( O
{
/ v! a! n4 p5 R9 h' A3 nint intary[10];* I. D# a; t' L8 z
index_to_the_moon(intary);
8 A0 U, p% ?$ D) U- j/ \9 V  o0 @* t. M0 |  q
exit(EXIT_SUCCESS);, i/ @' l4 O2 K# F) G, _0 k  a4 W5 B
}: ]3 I( F) E: O7 ]. F, T& v

) P" Q4 u; ?& i! y8 jvoid index_to_the_moon(int arr[])1 F7 z6 n' Z% S' Y' ]1 s( j1 b2 G
{/ R. t* B5 w2 R9 ^3 U
int i;
% X! \; v9 x, t0 @for(i=0;i<BIGNUM;i++)
3 ~. Z7 D0 w* h, q" Y6 S{
+ t3 a& Q& U. k8 i  L; @arr=i;; r" k7 b3 V6 G* D+ H6 w
printf("%d\n",arr);1 D7 _! `* R5 T5 s
}7 A5 H  `! G( x3 t
}' T3 S" R* ^$ L
+ w& G8 ?' d9 ?" I9 F

/ @; U  p4 ^0 {  g3 ?明显就越界了嘛,
9 R! m/ B7 l+ ]( q#define BIGNUM 50, ?( g" {8 C2 B) [
int intary[10];- L5 i: d# u3 U7 g5 _) z1 h
for(i=0;i<BIGNUM;i++)0 H: m8 ?  U' L& F: ~- `/ g' [) ^
4 N  D( l+ }# U- Y$ Q9 A( v" Z/ {7 a. r
你自己认真看看是不是越界了      
让linux走进每个人的电脑,linux忠实的朋友

TOP

c语言用数组作函数,传递的只是数组地址,它不会对数组边界进行检查,对于你的例子,主函数里的数组定了边界,但被调函数没有。对于内存空间数组后空间如果没有被占用,不会影响。但如果被占用了,可能会带来意想不到的后果。所以在这种情况下,你应该注意把握不要让数组越界。最好在被调函数里加上数组长度的参数。在参数传递时把数组地址传过去,同时也把数组长度传递过去。这样就不会有问题了。最后再说一句,C语言对数组越界不是严格检查的,只能靠自己把握。这也就是c语言的灵活性带来的问题,不过用长了就会注意的      

TOP

只有zhuomingliang可能是明白人,其他人都是糊涂虫      

TOP

[QUOTE=sunmoonlight]只有zhuomingliang可能是明白人,其他人都是糊涂虫[/QUOTE]' F$ K% J2 L0 x: X6 E! Y  n! Z
zhuomingliang 乐一个       
'
梅须逊雪三分白 雪却输梅一段香

TOP

不管main函数里面声明的变量,还是你的index_to_the_moon函数里面声明的变量,在系统运行的时候,都是在栈里面的,栈的空间是重上往下压的。压入栈的要么是参数的值,要么是指针,这根据你的参数的类型决定的。一般来说,当你的参数类型大于4个字节的时候,压入栈的就是你的参数的开始字节的地址。
: K; R8 ?, f  v2 ?所以,你的子函数里面使用越界,但是没有覆盖你的函数的返回地址。; U- J0 Z+ f8 ~6 N  k
这个时候,当你访问你的数组的空间是,前10个数字是正确的访问,但是当你访问超出了10个字节的时候,就会出现一个错误提示。
: L1 X6 c) o: _在linux里面,内存出现错误一般是保护错误,很多都是由page-fault这个页面异常引起的。因为你只使用了50个整形。而linux的一页是4K字节(一般)。你的程序出现的错误,不足以引起一个page-fault。同时,你的程序编译以后,首先是代码段,然后数据段,然后BBS段。而你的代码50个字节使用的数据正好是BBS段,看一下你的BBS段,没有其他的数据。往上延伸,又不会超出你的程序产生进程占有的用户空间(物理上占用的内存空间)。这样,你的程序可以正常运行,这很正常。因为他没有破坏操作系统的利益。2 ]; q. D4 r8 \9 _* \0 M% U- d( h
debug是用来监测你的程序错误的,他当然严格的检查程序的错误。所以。。。。。。。。      

TOP

参数传的是数组名。而数组名为指针。是地址。

所以可以运行,但是那样运行有危险啊。可能把有用的单元覆盖掉了。      

TOP

[QUOTE=王成毅]而你的代码50个字节使用的数据正好是BBS段,看一下你的BBS段,没有其他的数据。往上延伸,又不会超出你的程序产生进程占有的用户空间(物理上占用的内存空间)。这样,你的程序可以正常运行,这很正常。因为他没有破坏操作系统的利益。4 K% c/ B4 @6 {+ ]4 U- Q; l! F
debug是用来监测你的程序错误的,他当然严格的检查程序的错误。所以。。。。。。。。[/QUOTE]1 R- W! r. F4 F6 L# d

. l; \/ @5 ]0 ~* a自动变量分配空间是在运行栈上指派的,和.data, .bss什么的一点关系都没有。
0 y# c8 y" c2 u3 `$ O" N
1 F/ n% x- @3 J0 v而且对于用户进程来说,只关心其私有地址空间,和物理内存也没关系,那是操作系统的事情。      

TOP

[QUOTE=zhuomingliang]楼主试试这个( Y& `7 S2 k9 O, j7 y/ f
#include <stdio.h>
1 C; P9 \9 s, m; ?" Y#include <stdlib.h>( }. {0 `" W- s- {0 h

1 q6 |5 V4 o" U' ^2 I3 h5 ~#define BIGNUM 50
+ i" p) G* {5 u- Z8 |
8 X4 x* B1 q5 y& W5 gvoid index_to_the_moon(int arr[]);
2 v7 d* C' ], x& ^* Z / h' ^) w; X* T) c. [
int main(void), K$ T% f( T9 F' f4 ]
{1 Q6 v# ]5 g" e2 E" L7 [9 ]& ^( J
int intary[10];; I. E+ ?: x: U
index_to_the_moon(intary);! m" S& O) n( Y6 v1 b5 u1 ?

0 d/ ?( j7 u5 b# _. freturn 0;$ k; u# T3 e$ i# Z% f9 T! I. Y' y' `
}
# I8 W. ^# W  X: e0 }: {5 _( g 8 |( U& P  @% o# ]
void index_to_the_moon(int arr[])' ^7 S8 l! o2 j! C- x+ n7 n9 n
{! o/ S7 |7 ~) P3 E" M! O
int i;
* k3 \4 b# ], y' ]) J8 l, [for(i=0;i<BIGNUM;i++)9 V  D4 J, ^9 N* O/ `
{
& M0 U; C8 R$ f: L# z*arr=i;
; \' u3 Z# j$ q5 x0 u) V3 ?printf("%d\n",*arr++);
1 }3 O4 w4 G) i0 \# p# r}
6 R) Y6 v& @3 P7 X& e7 G}[/QUOTE]
1 X3 x4 i' w0 d' M' B* h
" n& ]! E2 o& [0 h这段代码和楼主的最大不同是,main中调用的是return 0 而不是exit()。
, U. x5 x1 H9 H" W! r2 i* u% A2 I5 x  r" g
众所周知,对于IA32, 运行栈往低地址增长。return导致 main返回,运行栈退到上一帧,而上一帧已经被 index_to_moon() 破坏光了,包括原先栈中保存的指令指针值(IP)也被修改了,自然会段错误。: b1 R/ _8 L7 N" @7 L6 s
. ~5 u% X' X- q& e
而exit() 执行了什么? exit() 被调用后,运行栈不是退栈到上一帧,而是直接调用 atexit() 例程注册的 函数中, 然后再调用 _exit(), 返回控制权给操作系统。 而atexit() 注册的函数栈桢位置处在更低的地址空间(atexit注册的函数直接在main下面开辟栈桢),没被 index_to_moon 破坏掉,因此程序没有发生段错误。但不要乐观,把BIGNUM改到足够大,一定也会段错误的。      

TOP

 19 12
发新话题