我对最近发生的事情感到困惑。这更像是一个技巧问题,我不知道答案。
我在Objective-C程序的main.m
中有以下功能。
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. counter
如何初始化为32767?我在考虑垃圾值,但是不是Objective-c编译器应该将int这样的原语初始化为零吗?我一遍又一遍地得到同样的32769。怎么样?
也许我错过了一些基本的东西。有什么指针吗?
答案 0 :(得分:4)
- 计数器如何初始化为1? (当我使用静态时,它会被初始化为零)
- 如何通过连续方法调用存储值?
- 当我为程序的不同执行检查此变量的内存位置时,它保持不变。这是怎么发生的?
醇>
- 这种行为如何从mac变为mac? //我在考虑不同的编译器版本,但它们都使用相同的版本(不确定)。还有其他方法吗?
- 计数器是如何初始化为32767的?我在考虑垃圾值,但是不是Objective-c编译器应该将int这样的原语初始化为零吗?我一遍又一遍地得到同样的32769。怎么样?
醇>
所有这些问题的答案是:巧合。您正在使用未初始化的值。你不能依赖这个值,你根本不应该使用它。编译器可能也会警告你。
编译器不会初始化基本类型的局部变量。如果使用ARC,则本地对象会自动初始化为nil,而不是基本类型。
原始实例变量初始化为0,指向对象的实例变量初始化为nil。
答案 1 :(得分:1)
在incrementCounter
函数中,您正在使用counter
这是一个未初始化的变量,因此行为是“未定义的” - 这意味着您所看到的是完全有效的行为(因为任何行为都会由于行为未定义,因此有效。)
关于实际发生的事情(或可能发生的事情,取决于编译器和运行时实现):
1-1:未定义初始值是什么,但如果对象加载器以确定的方式运行并且恰好在该内存中留下某个位模式,那么对于多次执行它可能总是相同的在开始执行你的程序之前,甚至是偶然的。
1-2:当调用incrementCounter
时,会分配堆栈上的一些内存(堆栈顶部的下一个可用块),然后incrementCounter
递增并返回它。然后调用NSLog
,使用相同的堆栈内存(但可能是不同的大小)。 NSLog
碰巧没有践踏该值(可能是因为编译器将其放在用于返回值的位置,而NSLog
没有返回值)。然后再次调用incrementCounter
,重复使用相同的堆栈内存,并且该值恰好没有被践踏。尝试以完全相同的方式实现第二个otherIncrementCounter
方法,看看它们是否都为该返回值共享相同的内存,同时在两种方法中查看&counter
,它可能是相同的。< / p>
1-3:程序作为进程运行时程序使用的本地地址空间是该进程的本地地址空间。写入同一地址的不同程序不会破坏进程的内存。内核实现可以根据需要映射这个内存,例如它可以为其所有进程使用相同的起始点作为堆栈地址空间。
2-1&amp; 2-2:见上面的1-1和1-2。也许是内核,对象加载器,操作系统等的不同构建。在这种情况下,或许NSLog
正在使用该值执行某些操作(可能暂时使用它),每次都将其设置为32767。 NSLog
的实现可能已经改变,或者可能只是在该机器上使用不同的编译器版本进行编译 - 它位于在运行时加载和链接的共享库中,而不是编译到您的可执行文件中。
一般来说,对这些事情感到怀疑是毫无意义的,因为有很多理由可以按原样工作,但了解编译器和堆栈的工作方式是有用的,所以我希望我的答案上面给你灵感,了解更多。但是,当编程(而不是学习编译器)时,根本不依赖于未定义的行为,打开大量警告并将其视为错误,这样您就不会意外地依赖于编译器,运行时或库实现的实现。