众所周知,C语言一开始只有#define,C程序员用#define定义符号常量。但后来ANSI C加入了const限定符,而const应该比#define更好,为什么现在的C程序员还在大量使用#define来定义常量呢?

这并不是我没有根据地乱说的。这样的例子有很多,例如<limits.h>,例如Windows API,例如OpenGL……

<limits.h>是C标准头文件,发明C语言的人难道不知道const吗?

Windows是Microsoft开发的,难道Microsoft的代码很差劲?

开发OpenGL的难道写的代码也很烂吗?

太奇怪了吧!因此,肯定是有什么特殊原因让他们使用#define……

曾今我在百度上用中文查这个问题,发现根本查不到;昨天我Google了“why use #define instead of const”,才在stackoverflow上找到了一个同样的问题:

Why do most C developers use define instead of const?

于是我阅读了这个问题的答案,然后挑选出一些答案,翻译出来。下面就是一些回答的原文和翻译(稍有修改),包括回答者的名字我也列出来了~

1. Bart van Ingen Schenau的回答

There is a very solid reason for this: const in C does not mean something is constant. It just means a variable is read-only.

In places where the compiler requires a true constant (such as for array sizes for non-VLA arrays), using a const variable, such as fieldWidth is just not possible.

这有个很可靠的原因:const在C中不表示一个变量是常量。const只表示一个变量是只读的。

在编译器需要一个真正的常量的情况下(例如不可变长数组的大小),使用const变量,例如fieldWidth是不可能的。

2. Vovanium回答

They're different.

const is just a qualifier, which says that a variable cannot be changed at runtime. But all other features of the variable persist: it has allocated storage, and this storage may be addressed. So code does not just treat it as a literal, but refers to the variable by accessing the specified memory location (except if it is static const, then it can be optimized away), and loading its value at runtime. And as a const variable has allocated storage, if you add it to a header and include it in several C sources, you'll get a "multiple symbol definition" linkage error unless you mark it as extern. And in this case the compiler can't optimize code against its actual value (unless global optimization is on).

#define simply substitutes a name with its value. Furthermore, a #define'd constant may be used in the preprocessor: you can use it with #ifdef to do conditional compilation based on its value, or use the stringizing operator # to get a string with its value. And as the compiler knows its value at compile time it may optimize code based on that value.

For example:

#define SCALE 1
...
scaled_x = x * SCALE;

When SCALE is defined as 1 the compiler can eliminate the multiplication as it knows that x * 1 == x, but if SCALE is an (extern) const, it will need to generate code to fetch the value and perform the multiplication because the value will not be known until the linking stage. (extern is needed to use the constant from several source files.)

A closer equivalent to using #define is using enumerations:

enum dummy_enum {
constant_value =
};

But this is restricted to integer values and doesn't have advantages of #define, so it is not widely used.

const is useful when you need to import a constant value from some library where it was compiled in. Or if it is used with pointers. Or if it is an array of constant values accessed through a variable index value. Otherwise, const has no advantages over #define.

它们不一样。

const只是个限定符,表示一个变量不能在运行时间被修改。但其他所有属于变量的特性仍保留着:它有已分配的存储器(原文是allocated storage),而且这个存储器可能有地址。所以代码不将它(const变量)看作常量,而通过访问指定的内存位置指代该变量(除非是static const,这样它就会被优化),然后再运行时间加载它的值。然后因为const变量有已分配的存储器,如果你将它加入一个头文件然后在多个C源代码文件中包含它,你会得到一个“符号重定义”的链接错误,除非你将它标记为extern。而且在这种情况下,编译器不能针对其真实值优化代码(除非打开全局优化)。

#define简单地用一个值替代一个名字。此外,一个#define定义的常量可以在预处理器中使用:你可以将其和#ifdef一起使用,在它的值的基础上做条件编译,或者使用连接符#以获取一个值对应的字符串。并且因为编译器在编译时知道它的值,编译器将可以在该值的基础上进行优化。

举个例子:

#define SCALE 1
...
scaled_x = x * SCALE;

当SCALE被定义为1时,编译器可以直接去掉这个乘法运算,因为它知道x * 1 == x。但如果SCALE是一个(extern)const变量,编译器就会需要生成代码以获取其值,然后进行乘法运算,因为SCALE的值在链接阶段以前都是未知的(在多个源代码文件中使用该const变量时需要extern)。

另一个更接近于#define的方法是枚举量:

enum dummy_enum {
constant_value =
};

但枚举量被限制为整数,与#define相比没有优势,因此使用不广泛。

在需要从库中导入常量值时const很有用;或者其和指针一起使用;或者其为一个通过索引值访问的常量数组。否则,const对于#define没有优势。

3. R..的回答

The reason is that most of the time, you want a constant, not a const-qualified variable. The two are not remotely the same in the C language. For example, variables are not valid as part of initializers for static-storage-duration objects, as non-vla array dimensions (for example the size of an array in a structure, or any array pre-C99).

原因是因为在大多数情况下,你想要一个常量,而不是有const限定符的变量。两者在C中不完全一样(原文是note remotely the same,不知道有没有犯错)。例如,变量作为静态存储周期对象的initializer的一部分是非法的,例如不可变长数组的大小(例如结构中一个数组的大小,或者C99前的任何数组)。

翻译了一部分,也不知道有没有错误,如果有错误请指出~

看了3个回答,我想答案已经十分明显了。

C中的const和C++的不同。在C中,const定义的常量和#define定义的常量不完全相同。#define是预处理器指令,在编译前就会把所有#define定义的常量的名字全部替换为其值。const定义的常量等同于只读变量,值在链接时才会知道。(我想,我之所以会有这个问题,是因为我直接学了C++而不是先学C,而C和C++中的const不一样。)

因此const定义的常量对于编译器就不是100%的常量,数组长度不能是const常量,只能是#define定义的常量。条件编译的时候也只能使用#define定义的常量。

那么也不是每个常量都是表示数组大小的吧,剩下的常量为什么也是用#define定义的呢?以我naive的思维,估计是因为为了代码风格上的统一吧。

那么在C++中为什么还有很多#define呢?那估计就是继承了C的习惯吧,或者为了兼容C吧……不过,如果是C++,建议使用const,而不是#define,来定义常量。

本文可转载,转载请注明出处:http://www.cnblogs.com/collectionne/p/6713651.html

为什么很多人使用#define而不是const定义常量的更多相关文章

  1. PHP中define()和const定义常量的区别

    在PHP中可以通过define()和const两种方式定义常量可是在开发中我们应该什么时候用define()定义常量,什么时候用const定义常量? 这两种方式定义常量的主要区别是什么? 从5.3版本 ...

  2. 在php中define和const定义常量的区别

    define和const都可以用来定义常量,但是const定义常量的时候大小写敏感,而define可以通过设置第三个参数为true的时候来取消大小写敏感! 如图: 引用地址:点这里

  3. C++ —— 非类中使用const定义常量的初始化,以及#define和typedef的区别

    总结一下在非类中使用const关键字定义常量时的初始化问题,亲测VS2015.顺便记录#define宏和typedef的区别. 1 首先对const声明的常量的初始化做简单小结: , w2 = , w ...

  4. PHP 中 define() 和 const 定义常量时的区别

    自 PHP 5.3.0 起,有两种方式定义常量,使用 const 关键字或者 define() 函数:   1 2 const FOO = 'BAR'; define('FOO', 'BAR'); 这 ...

  5. iOS define 宏定义 和 const定义常量区别

    const   const 是c++中的修饰符.  c++中常用来定义常量,修饰左值. #define 宏定义语句, 在预处理阶段直接做文本替换,不做类型检查. 它们之间的最大区别: 1.  对于co ...

  6. php中const定义常量

    const 常量 1.在定义时必须被初始值,2.前面不加任何修饰符3.变量名字母一般都大写4.常量可以被子类继承5.一个常量是属于一个类的,而不是某个对象的 作用:当某些值是固定不变的,就用const ...

  7. C中const 定义常量的使用

    先看如下代码 #include <stdio.h> #include <string.h> #define ARRSIZE(a) (sizeof(a)/sizeof(a[0]) ...

  8. #define宏与const的区别

    1.#define宏的用法 #define用宏名代替一个字符串,这样便于修改,提高了程序的可移植性.编译器在编译预处理时只对宏做文本替换,而不进行类型检查,所以替换后可能产生一些副作用. 带参数的宏类 ...

  9. const define 定义常量的区别

    1.用const定义常量在编译的时候,提供了类型安全检查,而define 只是简单地进行字符串的替换 2.const定义的常量,会分配相应的内存空间.而define没有分配空间,只是在程序中与处理的时 ...

随机推荐

  1. 非负矩阵分解(4):NMF算法和聚类算法的联系与区别

    作者:桂. 时间:2017-04-14   06:22:26 链接:http://www.cnblogs.com/xingshansi/p/6685811.html 声明:欢迎被转载,不过记得注明出处 ...

  2. 解决failed to push some refs to

    由于github我使用了dev和feature分支,团队合作合并到dev,个人开发都是feature....今天在本地feature中git pull origin dev 出现 在使用git 对源代 ...

  3. 微信小程序,前端大梦想(四)

    微信小程序图片轮播及滚动视图 移动端常用效果图片轮播及数据列表已被小程序封装成了组件,这里和大家分享一下如何使用. Scroll-view   可滚动视图区域 Swiper       图片轮播容器 ...

  4. java中创建对象中使用默认构造函数的注意点

    public class Test3 {   private int n;   Test3() {      System.out.println("调用默认构造器");   }  ...

  5. netty——私有协议栈开发案例

    netty--私有协议栈开发案例 摘要: 在学习李林峰老师的Netty权威指南中,觉得第十二章<私有协议栈开发>中的案例代码比较有代表性,讲的也不错,但是代码中个人认为有些简单的错误,个人 ...

  6. Python-一些实用的函数

    一,返回值为bool类型的函数 1.any()函数 any(iterable)->bool 当迭代器中有一个是Ture,则返回Ture:若interable=NUll,则返回False. > ...

  7. Ajax (AppServ服务器练习)

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  8. [Git]02 如何简单使用

     本章将介绍几个最基本的,也是最常用的 Git命令,以后绝大多数时间里用到的也就是这几个命令. 初始化一个新的代码仓库,做一些适当配置:开始或停止跟踪某些文件:暂存或提交某些更新.我们还会展示如何 ...

  9. JQuery Ajax使用及注意事项

    定义和用法 ajax() 方法通过 HTTP 请求加载远程数据. 该方法是 jQuery 底层 AJAX 实现.简单易用的高层实现见 $.get, $.post 等.$.ajax() 返回其创建的 X ...

  10. mysql导入导出sql文件(包括数据库和数据表的操作)

    废话不多说直接开始. 在windows命令行下登录mysql,创建一个test_01数据库,创建一个user表,并插入一条数据,如下 一.导出数据库test_01 1.退出数据库,在命令行中输入 my ...