Objective-C中的#define与const有何区别?

81

我刚接触Objective-C,关于const和预处理指令#define有几个问题。

首先,我发现无法使用 #define 定义常量的类型。这是为什么?

其次,它们之间是否有任何优势?

最后,哪种方法更有效和/或更安全?

6个回答

108

首先,我发现使用 #define 定义常量时无法指定其类型,为什么?

为什么什么?这不是真的:

#define MY_INT_CONSTANT ((int) 12345)
第二个问题是一个比较,是否有一个优于另一个的使用方式?
是的。#define 宏定义在编译开始之前就被替换了。const 仅仅是修改变量,以便于编译器将在您尝试更改它时标记错误。有一些上下文可以使用 #define,但是无法使用 const(尽管我正在努力查找最新的 clang)。理论上,const 占用可执行文件中的空间并需要对内存进行引用,但实际上这是微不足道的,并且可以被编译器优化。
相比于 #define,const 更适合编译器和调试器的使用。在大多数情况下,这是您在决定使用哪种方法时应考虑的最重要的因素。
突然想到一个可以使用 #define 但不能使用 const 的上下文。如果您有很多 .c 文件要使用常量,则可以使用 #define 将其放入头文件中。而对于 const,您必须在 C 文件中进行定义。
// in a C file
const int MY_INT_CONST = 12345;

// in a header
extern const int MY_INT_CONST;

在头文件中,MY_INT_CONST 不能在除了它被定义的文件之外的任何C文件中作为静态或全局范围数组的大小。

然而,对于整数常量,您可以使用 enum。事实上,这几乎是苹果公司经常采用的方法。这具有 #define 和 const 的所有优点,但仅适用于整数常量。

// In a header
enum
{
    MY_INT_CONST = 12345,
};

最后,哪种方法更有效和/或更安全?

#define在理论上更有效,尽管现代编译器可能会确保没有太大的区别。#define更安全,因为试图对其进行赋值总是会导致编译错误。

#define FOO 5

// ....

FOO = 6;   // Always a syntax error

const 变量可能会被欺骗并进行赋值,尽管编译器可能会发出警告:

const int FOO = 5;

// ...

(int) FOO = 6;     // Can make this compile

根据所在平台的不同,如果将常量放置在一个只读段中,赋值可能在运行时失败,并且根据C标准,这是正式未定义的行为。

就整数常量而言,我个人总是使用enum来定义常量,对于其他类型的常量,除非有很好的理由否则我会使用 const


我知道这个很老,但有一个情况,在那里你不能使用const而只能使用define的实例是:#define MY_NSNUM_CONSTANT @5。如果使用“NSNumber * const MY_NSNUM_CONSTANT = @5”是不行的。 - shortstuffsushi
我认为 #define 在可执行文件中占用的空间比 const 多。const 只存储一次,但是 #define 每次使用时都会被复制,因为它只是文本替换。但由于差异微不足道,所以我在无谓地追求严谨。 - Ryan Ballantyne
@RyanBallantyne 我认为你对于字符串常量可能是正确的,但对于整数常量来说不是,因为即使你将常量存储在一个地方,要访问它,你需要它的地址,这个地址至少和一个 int 一样大。然而,在现代编译器中,我非常惊讶它是否会有任何影响。 - JeremyP

18

来自一位C语言编码者:

const只是一个变量,其内容不能被更改。

然而,#define name value是一个预处理器命令,它将所有的name实例替换为value

例如,如果您使用#define defTest 5,则编译时代码中所有defTest的实例都将被替换为5


13

理解#define和const两个指令的区别非常重要,它们并不是用于相同的事情。

const

const用于生成一个在初始化后将是常量的所请求类型的对象。这意味着它是程序内存中的一个对象,并且可以被用作只读。 该对象在每次程序启动时生成。

#define

#define用于简化代码的可读性和未来的修改。使用define时,您只需在名称后面掩盖一个值。因此,在处理矩形时,您可以定义相应值的宽度和高度。然后,在代码中,它将更容易阅读,因为名称而不是数字。

如果以后您决定更改宽度的值,则只需在define中更改它,而不是在整个文件中进行乏味和危险的查找/替换。 编译时,预处理程序将在代码中将所有定义的名称替换为其值。因此,使用它们没有时间上的损失。


6

除了其他人的评论外,使用#define存在的错误通常很难调试,因为预处理器会在编译器之前占用它们。


3

由于预处理器指令并不被推荐使用,我建议使用const。在预处理器之前解析预处理器指令时,无法指定类型。虽然你可以这样做,但是:

#define DEFINE_INT(name,value) const int name = value;

并将其用作
DEFINE_INT(x,42) 

编译器会将其视为

const int x = 42;

首先,我发现使用#define无法定义常量的类型,为什么?
您可以查看我的第一个代码片段。
其次,使用其中一个是否有任何优势?
通常情况下,使用const而不是预处理器指令有助于调试,即使在这种情况下也是如此。
最后,哪种方法更有效和/或更安全?
两种方法都是有效的。我会说宏可能更安全,因为它不能在运行时更改,而变量可以。

为什么不直接键入 const ... 而使用宏呢? - Ed Heal
1
@EdHeal 不要这样做。我只是在回应“我发现使用#define无法定义常量的类型,为什么会这样?”时说你可以这样做。 - Luchian Grigore
2
预处理器指令不被赞成。[需要引用证明] - Ky -
我认为你可以使用这样的类型:#define myValue ((double) 2)。据我所知,预处理器将仅替换“myValue”与定义语句中其后面的内容,包括类型信息。 - John

1

我以前使用过 #define 来帮助创建更多的方法,例如如果我有像这样的东西。

 // This method takes up to 4 numbers, we don't care what the method does with these numbers.
 void doSomeCalculationWithMultipleNumbers:(NSNumber *)num1 Number2:(NSNumber *)num2 Number3:(NSNumber *)num23 Number3:(NSNumber *)num3;

但是我也想要一个只需要3个数字和2个数字的方法,所以我不想写两个新的方法,而是使用相同的方法并使用#define来实现,就像这样。

 #define doCalculationWithFourNumbers(num1, num2, num3, num4) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), (num4))

 #define doCalculationWithThreeNumbers(num1, num2, num3) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), (num3), nil)

 #define doCalculationWithTwoNumbers(num1, num2) \
         doSomeCalculationWithMultipleNumbers((num1), (num2), nil, nil)

我认为这是一个非常酷的东西,我知道你可以直接进入方法并在不需要的空格中放置nil,但如果你正在构建一个库,它非常有用。此外,这就是如何做到的。

     NSLocalizedString(<#key#>, <#comment#>)
     NSLocalizedStringFromTable(<#key#>, <#tbl#>, <#comment#>)
     NSLocalizedStringFromTableInBundle(<#key#>, <#tbl#>, <#bundle#>, <#comment#>)

已完成。

虽然我不相信您可以使用常量来做到这一点。但是常量具有比 #define 更好的优点,例如不能在 #define 中指定类型,因为它是在编译之前解析的预处理器指令,如果您在 #define 中出现错误,则很难调试,而常量则更容易调试。两者都有其优点和缺点,但我认为这完全取决于程序员决定使用哪一个。我使用 #define 编写了一个包含两者的库,用于执行所示操作,并使用常量声明需要指定类型的常量变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接