Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 指针运算超出有效内存范围会产生什么危害?_C_Pointers - Fatal编程技术网

C 指针运算超出有效内存范围会产生什么危害?

C 指针运算超出有效内存范围会产生什么危害?,c,pointers,C,Pointers,我跟踪了上个月的讨论 据我所知,那次讨论的要点是: char *p = malloc(4); 然后,可以使用指针算法获得p+4的指针。如果使用p+5获得指针,则行为未定义 我可以理解为什么解引用p+5会导致未定义的行为。但是仅仅使用指针运算的未定义行为 为什么算术运算符+和-不是有效的运算?我看不出在指针上加上或减去一个数字有什么害处。毕竟,指针是由捕捉对象地址的数字表示的 当然,我不是标准化委员会的成员:)我不知道他们在编纂标准之前进行的讨论。我只是好奇。任何洞察都是有用的。最简单的答案是,

我跟踪了上个月的讨论

据我所知,那次讨论的要点是:

char *p = malloc(4);
然后,可以使用指针算法获得p+4的指针。如果使用
p+5
获得指针,则行为未定义

我可以理解为什么解引用
p+5
会导致未定义的行为。但是仅仅使用指针运算的未定义行为

为什么算术运算符
+
-
不是有效的运算?我看不出在指针上加上或减去一个数字有什么害处。毕竟,指针是由捕捉对象地址的数字表示的


当然,我不是标准化委员会的成员:)我不知道他们在编纂标准之前进行的讨论。我只是好奇。任何洞察都是有用的。

最简单的答案是,可以想象机器会捕获整数溢出。如果是这种情况,那么任何不局限于单个存储区域的指针算法都可能导致溢出,从而导致陷阱,中断程序的执行。在尝试指针运算之前,C不应该被迫检查可能的溢出,因此标准允许在这样一台机器上的C实现只允许陷阱发生,即使随后出现混乱

另一种情况是对内存进行分段的体系结构,因此指针由段地址(带有隐式尾随0)和偏移量组成。任何给定的对象都必须适合单个段,这意味着有效的指针算法只能在偏移量上工作。同样,在指针运算过程中溢出偏移量可能会产生随机结果,C实现没有义务对此进行检查

最后,在假设所有指针算法都有效的情况下,编译器可能会产生一些优化。作为一个简单的激励案例:

if (iter - 1 < object.end()) {...}

未指明;当且仅当
b
恰好位于内存中
a
之后,否则
0
时,它将是
1
。由于您通常不能依赖于知道(除非
a
b
是同一数组的数组元素),因此比较通常没有意义;但这不是未定义的行为,编译器需要安排生成
0
1
(而且,为了使相同的比较始终具有相同的值,因为您可以依赖内存中不移动的对象。)

对指针执行基本+/-算术不会导致问题。指针值的顺序是连续的:
&p[0]<&p[1]<&p[n]
对于类型
n
对象长。但未定义此范围之外的指针算法<代码>&p[-1]可以小于或大于
&p[0]

int *p = malloc(80 * sizeof *p);
int *q = p + 1000;
printf("p:%p q:%p\n", p, q);
在指针范围外甚至内存范围内取消引用指针,但未对齐是一个问题

printf("*p:%d\n", *p);  // OK
printf("*p:%d\n", p[79]);  // OK
printf("*p:%d\n", p[80]);  // Bad, but &p[80] will be greater than &p[79]
printf("*p:%d\n", p[-1]);  // Bad, order of p, p[-1] is not defined 
printf("*p:%d\n", p[81]);  // Bad, order of p[80], p[81] is not defined
char *r = (char*) p;
printf("*p:%d\n", *((int*) (r + 1)) );  // Bad
printf("*p:%d\n", *q);  // Bad
问:为什么
p[81]
行为未定义?

答:例如:内存从
0
运行到
N-1
char*p
的值为
N-81
p[0]
p[79]
定义良好
p[80]
也有很好的定义
p[81]
需要值
N
来保持一致,但是溢出的
p[81]
可能有值0,N或者谁知道呢。

我能想到的一种情况是,
+
-
的结果可能会在溢出或下溢的情况下产生意外的结果

您提到的问题指出,对于
p=malloc(4)
,您可以执行
p+4
进行比较。这需要保证的一件事是
p+4
不会溢出。它不能保证
p+5
不会溢出


也就是说,
+
-
本身不会引起任何问题,但是有一个机会,无论多么小,它们都会返回一个不适合比较的值。

这里有几件事,在这种情况下,p+4之所以有效,是因为迭代到最后一个位置后的一个位置是有效的


p+5从理论上讲不会是问题,但根据我的说法,问题将是您何时尝试取消引用(p+5),或者您是否尝试覆盖该地址

在您的示例中,取消对
p+4
的引用也可能导致未定义的行为。如果这是正确的(我不确定是否正确),我猜他们是在尝试允许体系结构和环境在指针数学错误发生时而不是在使用时检测指针数学错误。再一次,未定义,这意味着没有承诺它会失败。。。只是没有保证它会成功。@user3277173您仍然可以与p+4进行比较。@pat,该标准保证计算
p+4
是有效的操作。取消对
p+4
的引用显然不是。在
p
p+4
范围内执行算术运算也一定会成功。@user3277173:为了终止循环,您通常会将其与结束指针后的数值进行比较。(例如,
iter!=foo.end()
)。超过结束指针一次的合法性就是为了允许这种习惯用法。OP是正确的,执行指针算术超出范围会调用未定义的行为。事实上,它在大多数(如果不是所有)平台上的行为都符合预期,这并没有改变这一点。我理解关于取消引用
p+5
的部分。问题是为什么计算
p+5
会导致未定义的行为。你是说你不能打印f,p+5?p+5,p+10,p+12;所有操作都是有效的。您可以继续打印任意数量的地址。唯一的问题是引用这些值。或者甚至在某些情况下,地址不存在
printf("*p:%d\n", *p);  // OK
printf("*p:%d\n", p[79]);  // OK
printf("*p:%d\n", p[80]);  // Bad, but &p[80] will be greater than &p[79]
printf("*p:%d\n", p[-1]);  // Bad, order of p, p[-1] is not defined 
printf("*p:%d\n", p[81]);  // Bad, order of p[80], p[81] is not defined
char *r = (char*) p;
printf("*p:%d\n", *((int*) (r + 1)) );  // Bad
printf("*p:%d\n", *q);  // Bad