0

我对正在发生的事情感到困惑。这更像是一个我不知道答案的技巧问题。

main.m的 Objective-C 程序中有以下功能。

int incrementCounter(){
     int counter;
    counter+=2;
    return  counter;
}

在我的里面main.m

 NSLog(@"Counter: %d",incrementCounter());
 NSLog(@"Counter: %d",incrementCounter());
 NSLog(@"Counter: %d",incrementCounter());

奇怪的是一本mac书(不知道Xcode版本)当我执行这个输出是:

2013-05-28 19:16:27.131 SOQ[4923:707] Counter: 1
2013-05-28 19:16:27.132 SOQ[4923:707] Counter: 3
2013-05-28 19:16:27.132 SOQ[4923:707] Counter: 5

我的问题在这里:
1.如何counter初始化为1?(当我使用static它时,它被初始化为零)
2.如何通过连续的方法调用存储值?
3. 当我为程序的不同执行检查此变量的内存位置时,它保持不变。这是怎么发生的?

当我在另一个 mac(lion Xcode 4.2.1)中执行相同的代码时,它会给出以下输出:

2013-05-28 19:16:27.131 SOQ[4923:707] Counter: 32769
2013-05-28 19:16:27.132 SOQ[4923:707] Counter: 32769
2013-05-28 19:16:27.132 SOQ[4923:707] Counter: 32769

我的问题在这里:
1. 这种行为如何从 mac 到 mac 变化?//我在想不同的编译器版本,但它都使用相同的(虽然不确定)。还有其他方法吗?
2. get如何counter初始化为32767?我在想垃圾值,但是objective-c编译器不应该将像int这样的原语初始化为零吗?我一遍又一遍地得到相同的 32769。如何?

也许我错过了一些基本的东西。任何指针?

4

2 回答 2

4
  1. 计数器是如何初始化为 1 的?(当我使用静态时,它被初始化为零)
  2. 值是如何通过连续的方法调用存储的?
  3. 当我为程序的不同执行检查此变量的内存位置时,它保持不变。这是怎么发生的?

  1. 这种行为如何从 mac 更改为 mac?//我在想不同的编译器版本,但它都使用相同的(虽然不确定)。还有其他方法吗? 
  2. 计数器是如何初始化为 32767 的?我在想垃圾值,但是objective-c编译器不应该将像int这样的原语初始化为零吗?我一遍又一遍地得到相同的 32769。如何?

所有这些问题的答案都是:巧合。您正在使用未初始化的值。你不能依赖这个值,你根本不应该使用它。编译器可能也会警告您。

编译器不会初始化原始类型的局部变量。如果使用 ARC,本地对象会自动初始化为 nil,但不是原始类型。

原始实例变量初始化为 0,指向对象的实例变量初始化为 nil。

于 2013-05-28T14:26:42.627 回答
1

在您incrementCounter使用的函数中,counter这是一个未初始化的变量,因此行为是“未定义的” - 这意味着您看到的是完全有效的行为(因为任何行为都是有效的,因为行为是未定义的)。

至于实际发生的事情(或可能发生的事情,取决于编译器和运行时实现):

1-1:未定义初始值将是什么,但如果对象加载器以确定性方式运行并且恰好在开始之前在该内存中留下某个位模式,则对于多次执行它可能最终总是相同执行您的程序,甚至只是偶然。

1-2:当incrementCounter被调用时,堆栈上的一些内存被分配(下一个可用的块,在堆栈的顶部),然后incrementCounter递增并返回它。然后NSLog调用,使用相同的堆栈内存(但可能不同的大小)。NSLog碰巧没有践踏那个值(可能是因为编译器把它放在一个用于返回值的地方并且NSLog没有返回值)。然后incrementCounter再次调用,重复使用相同的堆栈内存,并且该值恰好没有被践踏。尝试otherIncrementCounter以完全相同的方式实现第二种方法,以查看它们是否为该返回值共享相同的内存,同时查看&counter两种方法,它可能是相同的。

1-3:程序作为进程运行时使用的本地地址空间是该进程的本地地址空间。写入同一地址的不同程序不会践踏您的进程的内存。内核实现可以随意映射该内存,例如,它可以为所有进程的堆栈地址空间使用相同的起点。

2-1 和 2-2:见上文 1-1 和 1-2。可能是内核、对象加载器、操作系统等的不同构建。在这种情况下,可能NSLog正在使用该值执行某些操作,可能暂时使用它,每次将其设置为 32767。的实现NSLog可能已经更改,或者可能只是在该机器上使用不同的编译器版本进行编译 - 它位于在运行时加载和链接的共享库中,而不是编译到您的可执行文件中。

一般来说,想知道这些事情是毫无意义的,事情可以按原样工作的原因太多了,但是了解编译器和堆栈的工作原理很有用,所以我希望我上面的回答能给你一些启发来了解这一点更多的。但是,当编程(而不是学习编译器)时,根本不依赖未定义的行为,打开大量警告并将它们视为错误,这样您就不会意外地依赖编译器、运行时或库实现的实现。

于 2013-05-28T14:45:26.757 回答