Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.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,我是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,

我是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, 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中的指针总是一种弄乱的方式:你可以创建一个指针,指向任何你喜欢的(编造的)地址,更改该地址的数据,甚至可以不受惩罚,或者以错误告终。因此,聪明地使用指针始终是程序员的工作。所以如果你在学习C
pi[1]=-4是UB。您不应该使用
ptr[ind]
语法来访问任意位置。但是,正如您所说,您的问题更多地是关于您的特定编译器的工作方式,而不是关于如何进行正确的C编程。谢谢,但是为什么这两行代码会改变代码中的某些内容?@user2343039一旦您的代码中有未定义的行为,任何事情都可能发生。所以你看到了崩溃。如果代码中有未定义的行为,则不能期望得到某些已定义的结果。。摆脱它我又不能摆脱它。。该代码是任务的一部分。我不是我,是我写的代码。@user2343039请理解指针是如何工作的。当代码中包含UB时,并不意味着代码是干净的。这两行没有任何意义,因为您一直在更改指针应该指向的地址。最后,您让它指向变量
i
,这很好,但pi[1]不好。我们无法解释,gdb输出表明,
j
在这两种情况下都没有分配寄存器