我在C++程序中为这样的边界分配值:
#include <iostream>
using namespace std;
int main()
{
int array[2];
array[0] = 1;
array[1] = 2;
array[3] = 3;
array[4] = 4;
cout << array[3] << endl;
cout << array[4] << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
程序打印3和4.应该是不可能的.我正在使用g ++ 4.3.3
这是编译和运行命令
$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4
Run Code Online (Sandbox Code Playgroud)
只有在分配时才array[3000]=3000会给我一个分段错误.
如果gcc没有检查数组边界,我怎么能确定我的程序是否正确,因为它可能会导致一些严重的问题?
我用上面的代码替换了
vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout …Run Code Online (Sandbox Code Playgroud) 我今天刚上课了 - 阅读C代码和输入,如果程序实际运行,所需答案就是屏幕上显示的内容.其中一个问题被声明a[4][4]为一个全局变量,并且在该程序的一个点上,它试图访问a[27][27],所以我回答了类似" 访问一个超出其边界的数组是一个未定义的行为 ",但老师说它a[27][27]的值将为0.
之后,我尝试了一些代码来检查"所有未初始化的golbal变量是否设置为0"是否为真.好吧,这似乎是真的.
所以现在我的问题是:
a[27][27]是0所有环境?编辑:
在该代码中,a[4][4]是唯一声明的全局变量,并且还有一些更多本地变量main().
我在DevC++中再次尝试了该代码.所有这些都是0.但在VSE中并非如此,其中大多数值都是,0但有些值具有Vyktor指出的随机值.
我在一个非常大的应用程序中发现了这个问题,从中做了一个SSCCE.我不知道代码是否有未定义的行为或-O2打破它.
用gcc a.c -o a.exe -O2 -Wall -Wextra -Werror它编译时打印5.
但是在编译时没有(例如)或取消注释2条注释行之一(防止内联)时打印25.-O2-O1
#include <stdio.h>
#include <stdlib.h>
// __attribute__((noinline))
int f(int* todos, int input) {
int* cur = todos-1; // fixes the ++ at the beginning of the loop
int result = input;
while(1) {
cur++;
int ch = *cur;
// printf("(%i)\n", ch);
switch(ch) {
case 0:;
goto end;
case 1:;
result = result*result;
break;
}
}
end:
return result;
}
int main() …Run Code Online (Sandbox Code Playgroud) std::vector<int> a;
a.push_back(1);
a.push_back(a[0]);
Run Code Online (Sandbox Code Playgroud)
我刚刚了解到上面的代码非常危险.
(如果不明白为什么,你并不孤单......对我来说也不是很明显.)
处理它的"标准"方式是什么?制作一个新变量,然后立即将其分配给某些东西对我来说似乎有点奇怪.有没有更好的方法来处理它?
你如何训练自己注意这样的混叠问题?你在寻找什么样的模式?我不知道这种情况; 当我restrict在C中学习关键字时,我才学会了别名,现在才明白这个问题究竟是什么.
我很乐意接受一个答案,但问题的第(2)部分似乎没有得到回答.我想知道人们使用什么策略来查找他们编写的代码中的别名错误.
到目前为止我提出的一个策略是避免在两个参数中传递相同的值.(在这种情况下,一个参数是隐式的,一个是显式的.)
是否还有其他容易注意到的事项需要注意和注意?
我很好奇这种事情是否合法:
std::vector<some_class_type> vec;
vec.reserve(10);
some_class_type* ptr = vec.data() + 3; // that object doesn't exist yet
Run Code Online (Sandbox Code Playgroud)
请注意,我没有尝试访问指向的值.
这是标准所说的data(),但我不确定它是否相关:
返回:指针,这
[data(),data() + size())是一个有效的范围.对于非空向量,data() == &front().
现在我们知道,越界 - 指针算术具有未定义的行为,如本SO问题所述.
我的问题是:我们可以通过转换为std :: uintptr_t进行算术运算然后转换回指针来解决这种限制吗?是保证工作吗?
例如:
char a[5];
auto u = reinterpret_cast<std::uintptr_t>(a) - 1;
auto p = reinterpret_cast<char*>(u + 1); // OK?
Run Code Online (Sandbox Code Playgroud)
真实世界的用法是优化偏移内存访问 - 而不是p[n + offset]我想做的offset_p[n].
编辑使问题更明确:
给定一个pchar数组的基指针,如果p + n是一个有效的指针,将reinterpret_cast<char*>(reinterpret_cast<std::uintptr_t>(p) + n)保证产生相同的有效指针?
根据N1570(C11草案)6.5.6/8 添加剂操作员:
此外,如果表达式
P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素之后,如果表达式Q指向一个超过数组对象的最后一个元素,则表达式(Q)-1指向最后一个元素数组对象
子条款6.5.6/9还包含:
此外,如果表达式
P指向要么一个数组对象或一个过去的阵列对象的最后元件,并且表达的元件Q指向相同的数组对象的最后一个元素时,表达((Q)+1)-(P)具有相同的值((Q)-(P))+1和作为-((P)-((Q)+1)),和如果表达式P指向一个超过数组对象的最后一个元素的值,则值为零,即使该表达式(Q)+1未指向数组对象的元素.106)
这证明指针的算术是有效的:
#include <stdio.h>
int main(void)
{
int a[3] = {0, 1, 2};
int *P, *Q;
P = a + 3; // one past the last element
Q = a + 2; // last element
printf("%td\n", ((Q)+1)-(P));
printf("%td\n", ((Q)-(P))+1);
printf("%td\n", -((P)-((Q)+1)));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望不允许指向数组越界的元素,其解除引用作为未定义的行为(数组溢出),因此它使它具有潜在的危险性.这有什么理由吗?
根据C++ 03标准(5.3.4/7):
当direct-new-declarator中的表达式的值为零时,将调用分配函数以分配不带元素的数组.
通过我的阅读,这意味着此代码是合法的并具有指定的效果:
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
A() : a_(++aa_) {};
int a_;
static int aa_;
};
int A::aa_ = 0;
int main()
{
A* a = new A[0];
// cout << "A" << a->a_ << endl; // <-- this would be undefined behavior
}
Run Code Online (Sandbox Code Playgroud)
当我在调试器下运行此代码时,我看到A永远不会调用构造函数. new不抛出,并返回一个非空的,显然有效的指针.但是,值为a->a_未初始化的内存.
问题:
a实际指向了什么?我的大学教授最近给了我们一个实现我们自己的智能指针类的任务.在他用于复制字符串的样板代码中,我发现这件作品很漂亮的语法糖:
while (*sea++ = *river++);// C Sting copy
Run Code Online (Sandbox Code Playgroud)
我进一步研究了这段代码,发现它是strcpy.c中的确切代码,并在下面的stackoverflow问题中进一步解释它是如何工作的: "while(*s ++ =*t ++)"如何复制一个字符串?
当我尝试在我的下面的代码中使用这个语法糖时,它给出了垃圾作为结果并删除了存储在"river"中的字符串:
#include<iostream>
#include<cstring>
using namespace std;
void main()
{
const char *river = "water";// a 5 character string + NULL terminator
char *sea = new char[6];
while (*sea++ = *river++);
cout << "Sea contains: " << sea << endl;
cout << "River contains: " << river << endl;
}
Run Code Online (Sandbox Code Playgroud)
结果:

我知道我可以使用以下代码简单地实现所需的结果:
int i = 0;
while (i<6)
{
sea[i] = river[i];
i++;
}
Run Code Online (Sandbox Code Playgroud)
但这不是我想要的答案.我想知道我的while循环的实现或我的char指针的实例化有什么问题吗?
如果我取消引用一个数组并添加超过数组中可用元素的5,那么打印的值是多少?我在我的IDE中返回32767,这没有多大意义.
#include <stdio.h>
int main(void)
{
int i;
int meatBalls[5] = {1, 2, 3, 4, 5};
printf("%12s %18s %6s\n", "Element", "Address", "Value");
for(i=0; i < 5; i++) {
printf("meatBalls[%d] \t %p \t %d \n", i, &meatBalls[i], meatBalls[i]);
}
printf("\nmeatBalls \t\t %p \n", meatBalls); /*already a pointer*/
/*dereference meatBalls and it will update to the value e.g. 1*/
printf("\n*meatBalls \t\t %d \n", *meatBalls); /*dereference with asterisk*/
printf("\n*(meatBalls+2) \t\t %d \n", *(meatBalls+2)); /*dereference but also go through the array from 0 + …Run Code Online (Sandbox Code Playgroud) 好的,所以在测试中我问了这个问题:
int* ptrA; // assigned memory address 100
int a = 1; // assigned memory address 600
ptrA = &a;
What is the memory address of ptrA + 2?
Run Code Online (Sandbox Code Playgroud)
我以为它是606(int是4个字节+其地址a是600 + 2 = 606但显然答案是608,我错过了什么才能使这个真实?