且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

数组的连续性

更新时间:2023-02-26 22:32:22




Steve Kobes写道:


Steve Kobes wrote:
这合法吗?它必须打印4吗?

int a [2] [2] = {{1,2},{3,4}},* b = a [0];
printf( %d \ n,*(b + 3));

- 智慧
Is this legal? Must it print 4?

int a[2][2] = {{1, 2}, {3, 4}}, *b = a[0];
printf("%d\n", *(b + 3));

--Steve




是的,这是合法的,并且会打印4.

这个数组描述了一个连续分配的非空对象集合

类型int。

b = a [0]定位int * b到数组对象的开头为

将b =& a [0] [0]。


#include< stdio.h>


int main(无效)

{

int a [2] [2] = {{1,2}, {3,4}},* b = a [0];


printf(" a [2] [2] =((1,2},{3,4} }}; \ nint * b; \ n"

"使用b = a [0]。*(b + 3)=%d \ n",*(b + 3 ));

b =& a [0] [0];

printf("使用b =& a [0] [0]。*(b + 3)=%d \ n",*(b + 3));

printf("指针值& a [0] [0]和[0]是% sequal\\\
",

(& a [0] [0] == a [0])?"":not not);
返回0;

}

-

Al Bowers

Tampa,Fl美国

mailto: xa******@myrapidsys.com (删除x发送电子邮件)
http://www.geocities.com/abowers822/



Yes it is legal and will print 4.
This array describes a contiguously allocated nonempty set of objects of
type int.
b = a[0] positions the int *b to the beginning of the array object as
would b = &a[0][0].

#include <stdio.h>

int main(void)
{
int a[2][2] = {{1, 2}, {3, 4}}, *b = a[0];

printf("a[2][2] = ((1,2},{3,4}};\nint *b;\n"
"Using b = a[0]. *(b + 3) = %d\n", *(b + 3));
b = &a[0][0];
printf("Using b = &a[0][0]. *(b + 3) = %d\n",*(b+3));
printf("The pointer values &a[0][0] and a[0] are%sequal\n",
(&a[0][0] == a[0])?" ":" not ");
return 0;
}
--
Al Bowers
Tampa, Fl USA
mailto: xa******@myrapidsys.com (remove the x to send email)
http://www.geocities.com/abowers822/


> Steve Kobes写道:
>Steve Kobes wrote:
这是合法的吗?它必须打印4吗?

int a [2] [2] = {{1,2},{3,4}},* b = a [0];
printf( %d \ n,*(b + 3));
Is this legal? Must it print 4?

int a[2][2] = {{1, 2}, {3, 4}}, *b = a[0];
printf("%d\n", *(b + 3));



文章< news:2r ************* @ uni-berlin.de>

Al Bowers< xa ****** @ rapidsys.com>写道:是的它是合法的,将打印4.


我相信comp.std.c中至少有人会不同意

with你好。

这个数组描述了一个连续分配的非空对象类型为int。
b = a [0]将int * b定位到数组对象的开头因为
会b =& a [0] [0]。


In article <news:2r*************@uni-berlin.de>
Al Bowers <xa******@rapidsys.com> wrote:Yes it is legal and will print 4.
I believe there are those in comp.std.c, at least, who will disagree
with you.
This array describes a contiguously allocated nonempty set of objects of
type int.
b = a[0] positions the int *b to the beginning of the array object as
would b = &a[0][0].




特别是b。指向两个连续的int中的第一个
{1,2}。这两个连续的int确实紧跟在两个连续的ints {3,4}内存中,但是编译器

是通过任意魔法手段允许在数组下标和指针算术上插入边界检查

。 (请记住,这些

两个非常相似:下标只是地址算术

后跟指针间接,而指针间接只是

下标 - - * p和p [0]是相同的。[%]注意我们允许

计算& array [N]但不跟随结果指针,所以有

是两个使用相同代码的轻微障碍 - 你还需要

a boolean flag"这个地址算术后面跟着

间接,或者不是 ,在任何边界检查代码中处理它。)


假设编译器实际上做了这个神奇的边界检查,

以便它 ;知道&QUOT; b指向数组a [0]中存储的唯一* * * int的第一个

。在这种情况下,允许b [0]和b [1],但是b [2]

被拒绝,带有越界数组下标。错误。


(就个人而言,我觉得这一切都不太可能,但它似乎确实是允许的。
。我也认为,如果编写一个编译器与

绝对非魔法边界检查通过胖指针

或某种运行时表查找完成,b的边界应该运行

从0到3(包括0和3),在这种情况下,而不是0到1.但是这些

是我对它应该工作的方式的看法,而不是什么

C标准的字母说。


在实践中,我不相信任何人还没有找到实施

其中b [3](或等效地,*(b + 3))被捕获为错误。

最有可能找到这种运行时[%%]检查的地方是

Lisp机器的C编译器,我还没有访问过一个

多年来。


[%因此,您总是可以用p [0]替换(* p),包括

在像(* p).field这样的表达式中,通常写成

p->字段。因此 - >运算符可替换为[0]。,例如,

而不是:


if(a-> b-> c = = 42)


你可以写:


if(a [0] .b [0] .c == 42)


这个例子只是表明C有很多运算符。 :-)]


[%%这个特殊情况可以在编译时捕获,但在

一般会想要测试b中的下标[ i),或者在运行时在(b + i)中的偏移量

,因为i和i都是可能很难或不可能预测,也因为b可能也很难

或无法预测:


int a1 [10],a2 [2];

int * b;

...

if(user_input == 42)

b = a1;

else if( user_input == 6 * 9)

b = a2;

...

do_something(b [3]);


这里,一个通用的边界检查器可能会模糊地做某事

这样:


struct bounds * bp;


bp = bounds_lookup(b);

if(bp == NULL)

__runtime_error(" invalid pointer");

if(bp-> upper_limit< 3)

__runtime_error(" out of bounds array subscript");

tmp = b [ 3];

do_something(tmp);


来处理对do_something()的调用。 bounds_lookup()函数

将提供的指针值与所有活动的

数组的表进行比较 - 所有全局变量具有数组类型,所有未完成的
malloc()s,以及仍然存在于当前

堆栈帧中的所有局部变量 - 并找到相应的数组

界限信息。我们可以做到这一点,而不用关注自己

使用普通的简单指针(& localvar,其中localvar是*不是*

数组),因为我们有一个非零下标。如果这是使用

b [i]代码必须这样做:


if(i!= 0){

struct bounds * bp;

......和以前一样......

}其他

tmp = * b; / *不能对零下标进行边界检查* /

do_something(tmp);


因为每个普通变量就像一个大小为0的数组。

或者,bounds_lookup()实际上可能具有bounds-table

信息,用于* all *变量的地址,而不是

只是所有数组。


上面的边界检查需要将指针算法处理为

,所以如果我们做b + +,上限减少一个

,下限从0到-1。 (还有其他方法

处理这个可能更实用的实际

bounds-checkers;这个版本只是为了说明。)
>
边界检查往往会使程序变慢,并且是一个良好的编译器级优化非常重要的区域。如果

a循环保证从0到N-1(包括0和N-1)运行,那么在进入循环之前,N-1上的单个

边界检查将取代许多

运行时测试。如果边界检查可以在编译时完成,那么
然后完全从运行时代码中省略,那就更好了。]

-

In-Real-Life:风河系统Chris Torek

美国犹他州盐湖城(40°39.22''N,111°50.29''W)+1 801 277 2603

电子邮件:忘了它 http:// web。 torek.net/torek/index.html

由于垃圾邮件发送者,阅读电子邮件就像在垃圾中搜索食物一样。



In particular, "b" points to the first of the two consecutive "int"s
{1, 2}. These two consecutive "int"s are indeed followed immediately
in memory by the two consecutive "int"s {3, 4} -- but a compiler
is allowed, through arbitrarily magical means, to insert bounds-checking
on array subscripts and pointer arithmetic. (Remember that these
two are quite similar: subscripting is simply address arithmetic
followed by pointer indirection, and pointer indirection is simply
subscripting -- *p and p[0] are identical.[%] Note that we are allowed
to compute &array[N] but not follow the resulting pointer, so there
is a slight hitch in using the same code for both -- you also need
a boolean flag "this address arithmetic will be followed by
indirection, or not", to handle this in any bounds-checking code.)

Suppose that the compiler does in fact do this magical bounds-checking,
so that it "knows" that b points to the first of only *two* ints stored
in the array a[0]. In this case, b[0] and b[1] are allowed, but b[2]
is rejected with an "out of bounds array subscript" error.

(Personally, I find it all quite unlikely, but it does seem to be
allowed. I also think that, if one writes a compiler with the
decidedly non-magical bounds-checking done via either "fat pointers"
or some kind of runtime table lookups, the bounds for b should run
from 0 to 3 inclusive, in this case, rather than 0 to 1. But these
are my opinions of "the way it ought to work" rather than "what
the letter of the C standard says".)

In practice, I do not believe anyone has yet found an implementation
where b[3] (or equivalently, *(b + 3)) is caught as an error. The
most likely place to find this kind of runtime[%%] checking is in
C compilers for Lisp machines, and I have not had access to one of
those in years.

[% As a result, you can always replace (*p) with p[0], including
in expressions like (*p).field, which is more usually written as
p->field. Hence the -> operator is replaceable with "[0].", e.g.,
instead of:

if (a->b->c == 42)

you can write:

if (a[0].b[0].c == 42)

This example just goes to show that C has a plethora of operators. :-)]

[%% This particular case can be caught at compile time, but in
general one would want to test the subscript in b[i], or the offset
in (b+i), at runtime, both because "i" might be difficult or
impossible to predict, and also because "b" might also be difficult
or impossible to predict:

int a1[10], a2[2];
int *b;
...
if (user_input == 42)
b = a1;
else if (user_input == 6 * 9)
b = a2;
...
do_something(b[3]);

Here, a general-purpose bounds-checker might do something vaguely
like this:

struct bounds *bp;

bp = bounds_lookup(b);
if (bp == NULL)
__runtime_error("invalid pointer");
if (bp->upper_limit < 3)
__runtime_error("out of bounds array subscript");
tmp = b[3];
do_something(tmp);

to handle the call to do_something(). The bounds_lookup() function
compares the supplied pointer value against a table of all active
arrays -- all "global variables" that have array type, all outstanding
malloc()s, and all local variables that are still live on the current
stack frame, for instance -- and finds the corresponding array
bounds information. We can do this without concerning ourselves
with ordinary simple pointers (&localvar, where localvar is *not*
an array) because we have a nonzero subscript. If this were using
b[i] the code would have to do:

if (i != 0) {
struct bounds *bp;
... as before ...
} else
tmp = *b; /* cannot do bounds checking on zero subscripts */
do_something(tmp);

becaues each ordinary variable "acts like" an array of size 0.
Alternatively, bounds_lookup() might actually have bounds-table
information for *all* variables whose address is taken, instead
of just all arrays.

The bounds-checking above needs to handle pointer arithmetic as
well, so that if we do "b++", the upper limit is reduced by one
and the lower limit goes from 0 to -1. (There are other methods
for handling this that are probably more practical in real
bounds-checkers; this version is just for illustration.)

Bounds-checking tends to slow down programs quite a bit, and is an
area in which good compiler-level optimization is important. If
a loop is guaranteed to run from 0 to N-1 inclusive, a single
bounds-check on N-1 before entering the loop at all replaces many
runtime tests. If the bounds-check can be done at compile time,
then omitted entirely from the runtime code, that is even better.]
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22''N, 111°50.29''W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.


Chris Torek写道:
Chris Torek wrote:
Steve Kobes写道:
Steve Kobes wrote:
这是合法的吗?它必须打印4吗?

int a [2] [2] = {{1,2},{3,4}},* b = a [0];
printf( %d \ n,*(b + 3));
Is this legal? Must it print 4?

int a[2][2] = {{1, 2}, {3, 4}}, *b = a[0];
printf("%d\n", *(b + 3));



在文章< news:2r ********** ***@uni-berlin.de>
Al Bowers< xa ****** @ rapidsys.com>写道:



In article <news:2r*************@uni-berlin.de>
Al Bowers <xa******@rapidsys.com> wrote:

是的,它是合法的,将打印4。
Yes it is legal and will print 4.



我相信comp.std.c中有那些,至少,谁会不同意



I believe there are those in comp.std.c, at least, who will disagree
with you.




如果它是


int a [2] [2] = {{1, 2},{3,4}},* b =(int *)a;

^^^^^^^

然后我相信你可以访问b [ 3]。


-

pete



If it was

int a[2][2] = {{1, 2}, {3, 4}}, *b = (int *)a;
^^^^^^^
then I believe you could access b[3].

--
pete