变量的C内存管理
我是C的新手,目前遇到了一些麻烦。请查看以下代码:变量的C内存管理,c,pointers,C,Pointers,我是C的新手,目前遇到了一些麻烦。请查看以下代码: int main (int argc, char *argv[]) { int j = 2; int i = 100; int *pi = &i; pi = &j; //those 2 lines should do nothing, in my opinion pi = &i; // pi[1] = -4; printf("i = %d,
int main (int argc, char *argv[]) {
int j = 2;
int i = 100;
int *pi = &i;
pi = &j; //those 2 lines should do nothing, in my opinion
pi = &i; //
pi[1] = -4;
printf("i = %d, j = %d, *pi = %d\n", i, j, *pi);
return EXIT_SUCCESS;
}
该代码因SegFault而失败。与gdb的一些调查:
(gdb) print &j
$1 = (int *) 0x7fffffffde80
(gdb) print &i
$2 = (int *) 0x7fffffffde84
但是,如果没有这两行代码,代码就可以正常工作,因为i和j似乎可以在内存中交换位置——但是为什么呢
(gdb) print &j
$1 = (int *) 0x7fffffffde84
(gdb) print &i
$2 = (int *) 0x7fffffffde80
我问我的老师,但不幸的是她不知道
提前谢谢
编辑:我指的是printf打印:i=100,j=-4,*pi=100--pi[1]点在j上,看起来
问题是,为什么这两行会改变什么呢?
pi
是一个指针,当你这样做时,你会让它指向一个整数
pi[1] = -4;
您正在访问不受您控制的内存或未分配的内存,因此会导致未定义的行为,从而导致seg故障。语句
pi[1]=-4代码>调用未定义的行为。任何事情都有可能发生。您可能会得到预期或意外的结果
pi[1]=-4
相当于*(pi+1)=-4代码>。允许指针越过对象i
,但取消引用它将调用未定义的行为
C11:6.5.6加法运算符:
7对于这些运算符,指向不是数组元素的对象的指针的行为与指向长度为1且对象类型为其元素类型的数组的第一个元素的指针的行为相同
8如果指针操作数和结果都指向同一数组对象的元素,或超过数组对象最后一个元素的元素,则计算不应产生溢出;否则,行为是未定义的如果结果指向数组对象的最后一个元素后一个元素,则不应将其用作计算的一元*运算符的操作数
问题是,为什么这两行会改变什么
答案是,这是因为。我可以猜测,pi=&j
阻止编译器向j
提供寄存器内存。因此,有了它,pi[1]
是对j
的引用,但没有它,j
在寄存器中,pi[1]
是对先前存储的ebp
寄存器的值的引用。由于ebp
变得混乱,当尝试从main()
返回时,进程崩溃
说到这里,我应该重复别人说过的话:这两种情况下都是未定义的行为。对于我的编译器,它如下所示:
当然,pi[1]
和&pi[1]
是未定义的行为
在pi[1]=-4上设置断点代码>并运行程序:
这是带有pi=&j;pi=&i代码>
Breakpoint 1, main (argc=1, argv=0x7fffffffe428) at tmp.c:12
12 pi[1] = -4;
(gdb) p &j
$1 = (int *) 0x7fffffffe334
(gdb) p &i
$2 = (int *) 0x7fffffffe330
(gdb) p &pi
$3 = (int **) 0x7fffffffe338
(gdb) p &pi[1]
$4 = (int *) 0x7fffffffe334
(gdb) c
Continuing.
i = 100, j = -4, *pi = 100
[Inferior 1 (process 2890) exited normally]
(gdb)
12 pi[1] = -4;
(gdb) p &j
$1 = (int *) 0x7fffffffe33c
(gdb) p &i
$2 = (int *) 0x7fffffffe32c
(gdb) p &pi
$3 = (int **) 0x7fffffffe330
(gdb) p &pi[1]
$4 = (int *) 0x7fffffffe330
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x000000000040056d in main (argc=1, argv=0x7fffffffe428) at tmp.c:13
13 printf("i = %d, j = %d, *pi = %d\n", i, j, *pi);
(gdb) p pi
$5 = (int *) 0x7ffffffffffc
&pi[1]
偶然指向j
这是不带pi=&j;pi=&i代码>
Breakpoint 1, main (argc=1, argv=0x7fffffffe428) at tmp.c:12
12 pi[1] = -4;
(gdb) p &j
$1 = (int *) 0x7fffffffe334
(gdb) p &i
$2 = (int *) 0x7fffffffe330
(gdb) p &pi
$3 = (int **) 0x7fffffffe338
(gdb) p &pi[1]
$4 = (int *) 0x7fffffffe334
(gdb) c
Continuing.
i = 100, j = -4, *pi = 100
[Inferior 1 (process 2890) exited normally]
(gdb)
12 pi[1] = -4;
(gdb) p &j
$1 = (int *) 0x7fffffffe33c
(gdb) p &i
$2 = (int *) 0x7fffffffe32c
(gdb) p &pi
$3 = (int **) 0x7fffffffe330
(gdb) p &pi[1]
$4 = (int *) 0x7fffffffe330
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x000000000040056d in main (argc=1, argv=0x7fffffffe428) at tmp.c:13
13 printf("i = %d, j = %d, *pi = %d\n", i, j, *pi);
(gdb) p pi
$5 = (int *) 0x7ffffffffffc
使用pi[1]=-4(0xfffffffc)
修改指针pi
,指向不允许读取进程的页面,因此出现分段错误
您没有打印感兴趣的pi
、&pi
和&pi[1]
(即UB)
你的问题的答案是:
编译器可以自由决定在堆栈框架中变量的排列位置和顺序。当您更改函数的源代码时,编译器可以做出不同的决定。另外,&pi[1]
可以指向任何地方,因为它是未定义的行为。未定义的行为是未定义的。其实就是这么简单。还有,“代码运行良好”是什么意思?对于这个代码,你认为什么样的行为?代码是我们班上的一个任务。“工作良好”是指printf打印:i=100,j=-4,*pi=100--pi[1]指向j,似乎你的老师不知道pi[1]
是未定义的行为,她不是一个很好的老师。@user2343039但为什么“工作良好”呢?到底是什么让这种行为“好”呢?@user2343039你看,C中的指针总是一种弄乱的方式:你可以创建一个指针,指向任何你喜欢的(编造的)地址,更改该地址的数据,甚至可以不受惩罚,或者以错误告终。因此,聪明地使用指针始终是程序员的工作。所以如果你在学习Cpi[1]=-4代码>是UB。您不应该使用ptr[ind]
语法来访问任意位置。但是,正如您所说,您的问题更多地是关于您的特定编译器的工作方式,而不是关于如何进行正确的C编程。谢谢,但是为什么这两行代码会改变代码中的某些内容?@user2343039一旦您的代码中有未定义的行为,任何事情都可能发生。所以你看到了崩溃。如果代码中有未定义的行为,则不能期望得到某些已定义的结果。。摆脱它我又不能摆脱它。。该代码是任务的一部分。我不是我,是我写的代码。@user2343039请理解指针是如何工作的。当代码中包含UB时,并不意味着代码是干净的。这两行没有任何意义,因为您一直在更改指针应该指向的地址。最后,您让它指向变量i
,这很好,但pi[1]不好。我们无法解释,gdb输出表明,j
在这两种情况下都没有分配寄存器