命名数字作为变量

时间:2014-02-26 09:56:06

标签: c# c++ conventions

我最近在高调代码中已经看过几次,其中常量值被定义为变量,以值命名,然后只使用一次。我想知道它为什么要完成?

E.g。 Linux Source(resize.c)

unsigned five = 5;
unsigned seven = 7;

E.g。 C#.NET源代码(Quaternion.cs)

double zero = 0;
double one = 1;

10 个答案:

答案 0 :(得分:83)

命名数字是一种糟糕的做法,有一天需要改变一些事情,你最终会得到unsigned five = 7

如果它有一些含义,请给它一个有意义的名字。 “幻数”five与幻数5相比没有任何进步,更糟糕的是因为它可能实际上不等于5

这种事情通常源于一些货物崇拜风格的编程风格指南,有人听说“魔术数字不好”,并且在没有完全理解原因的情况下禁止使用它们。

答案 1 :(得分:48)

命名良好的变量

为变量赋予适当的名称可以大大澄清代码,例如

constant int MAXIMUM_PRESSURE_VALUE=2;

这有两个关键优势:

  • 价值MAXIMUM_PRESSURE_VALUE可能会在许多不同的地方使用,如果由于某种原因价值发生变化,您只需要在一个地方进行更改。

  • 如果使用它会立即显示函数正在执行的操作,例如,下面的代码显然会检查压力是否非常高:

    if (pressure>MAXIMUM_PRESSURE_VALUE){
        //without me telling you you can guess there'll be some safety protection in here
    }
    

命名不佳的变量

然而,一切都有一个反驳论据,你所展示的内容看起来非常像是一个好主意到目前为止它没有意义。将TWO定义为2不会添加任何值

constant int TWO=2;
  • TWO可以在许多不同的地方使用,也许是为了加倍,也许是为了访问索引。如果将来您需要更改索引,不能只需更改为int TWO=3;,因为这会影响您使用TWO的所有其他(完全不相关)方式,现在您将成为三倍而不是倍增等
  • 如果使用它,它不会给你提供比你刚使用“2”更多的信息。比较以下两段代码:

    if (pressure>2){
        //2 might be good, I have no idea what happens here
    }
    

    if (pressure>TWO){
        //TWO means 2, 2 might be good, I still have no idea what happens here
    }
    
  • 更糟糕的是(在这里似乎是这种情况)TWO可能不等于2,如果是这样,这是一种混淆形式,其目的是使代码不那么明确:显然它实现了

通常的原因是编码标准禁止幻数,但不将TWO计为幻数;这当然是! 99%的时间你想使用一个有意义的变量名,但在1%的时间内使用TWO代替2不会获得任何东西(抱歉,我的意思是ZERO)。< / p>

此代码受Java启发,但意图与语言无关

答案 2 :(得分:30)

简短版本:

  • 只保持五号的常数five是没用的。不要无缘无故地去做这些(有时你必须因为语法或打字规则)。
  • Quaternion.cs中的命名变量并不是绝对必要的,但你可以认为代码的可读性要大于没有它们。
  • ext4 / resize.c中的命名变量根本不是常量。他们简洁地命名了计数器。他们的名字稍微掩盖了他们的功能,但这段代码实际上确实正确地遵循了项目的专业编码标准。

Quaternion.cs发生了什么?

这很容易。

在此之后:

double zero = 0;
double one = 1;

代码执行此操作:

return zero.GetHashCode() ^ one.GetHashCode();

没有局部变量,替代方案是什么样的?

return 0.0.GetHashCode() ^ 1.0.GetHashCode(); // doubles, not ints!
真是一团糟!可读性绝对是在这里创建本地人的一面。此外,我认为明确命名变量表示&#34;我们已仔细考虑过这个问题&#34;比写一个令人困惑的退货声明要清楚得多。

resize.c正在进行什么?

在ext4 / resize.c的情况下,这些数字实际上并不是常数。如果您按照代码进行操作,则会看到他们的计数器及其值实际上会在while循环的多次迭代中发生变化。

Note how they're initialized

unsigned three = 1;
unsigned five = 5;
unsigned seven = 7;

三等于一,是吗?那是什么意思?

请参阅实际发生的事情update_backups通过引用函数ext4_list_backups传递这些变量:

/*
 * Iterate through the groups which hold BACKUP superblock/GDT copies in an
 * ext4 filesystem.  The counters should be initialized to 1, 5, and 7 before
 * calling this for the first time.  In a sparse filesystem it will be the
 * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
 * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
 */
static unsigned ext4_list_backups(struct super_block *sb, unsigned *three,
                                  unsigned *five, unsigned *seven)

他们在多个通话过程中保留的计数器。如果你look at the function body,你会发现它正在调整计数器以找到下一个3,5或7 的强大功能,创建你在评论:1,3,5,7,9,25,27和&amp; c。

现在,对于最奇怪的部分:变量three被初始化为1,因为3 0 = 1.但是,幂0是特殊情况,因为它是唯一的时间3 x = 5 x = 7 x 。尝试重写ext4_list_backups以处理初始化为1的所有三个计数器(3 0 ,5 0 ,7 0 )并且您将看到代码变得多么繁琐。有时,在评论中告诉来电者做一些时髦的事情(将列表初始化为1,5,7)会更容易。

那么,five = 5编码风格是否良好?

是&#34;五&#34;变量five在resize.c中表示的东西的一个好名字?在我看来,它不是你应该在任何随机项目中模仿的风格。简单名称five并未就变量的用途进行多少交流。如果您正在使用网络应用程序或快速制作视频聊天客户端或其他原型并决定命名变量five,那么您可能会为其他需要的人制造麻烦和烦恼维护和修改你的代码。

然而,这是一个关于编程的普遍性不能描绘全貌的例子Take a look at the kernel's coding style document,特别是关于命名的章节。

  

GLOBAL变量(仅在真正需要它们时才使用)需要   具有描述性名称,全局函数也是如此。如果你有一个功能   计算活跃用户的数量,您应该调用它   &#34; count_active_users()&#34;或者类似的,你应该称之为&#34; cntusr()&#34;。

     

...

     

LOCAL变量名称应该简短,并且要点。如果你有   一些随机整数循环计数器,它应该被称为&#34; i&#34;。   把它叫做&#34; loop_counter&#34;如果没有机会,就没有生产力   被误解了。同样,&#34; tmp&#34;可以是任何类型的   用于保存临时值的变量。

     

如果你害怕混淆你的本地变量名,你就会有另一个   问题,这被称为功能 - 生长 - 激素 - 失衡综合症。   见第6章(功能)。

部分内容是C风格的编码传统。其中一部分是有目的的社会工程。许多内核代码都是敏感的东西,并且经过多次修改和测试。由于Linux是一个很大的开源项目,它并没有真正伤害到贡献 - 在大多数情况下,更大的挑战是检查这些贡献的质量。

调用该变量five而不是nextPowerOfFive之类的内容是阻止参与者干涉他们不理解的代码的方法。在您尝试进行任何更改之前,尝试强制您在执行任何更改之前,逐行地详细阅读您要详细修改的代码。

内核维护者做出了正确的决定吗?我不能说。但这显然是一个有目的的举动。

答案 3 :(得分:11)

我的组织有一些编程指南,其中一个是魔术数字的使用......

例如:

if (input == 3) //3 what? Elephants?....3 really is the magic number here...

这将改为:

#define INPUT_1_VOLTAGE_THRESHOLD 3u 
if (input == INPUT_1_VOLTAGE_THRESHOLD) //Not elephants :(

我们还有一个-200,000的源文件 - &gt; 200,000 #defined格式为:

#define MINUS_TWO_ZERO_ZERO_ZERO_ZERO_ZERO -200000

可用于代替幻数,例如在引用数组的特定索引时。

我想这已经为“可读性”做了。

答案 4 :(得分:7)

数字0,1,...是整数。这里,'命名变量'给整数赋予不同的类型。指定这些常量(const unsigned five = 5;)

可能更合理

答案 5 :(得分:7)

我曾经使用过几次类似的东西来为文件写入值:

const int32_t zero = 0 ;

fwrite( &zero, sizeof(zero), 1, myfile );

fwrite接受一个const指针,但是如果某个函数需要一个非const指针,你最终会使用一个非const变量。

P.S。:这总让我想知道可能是零的大小。

答案 6 :(得分:6)

你是如何得出它只使用一次的结论?它是公开的,它可以在任何组件中使用任何次数。

public static readonly Quaternion Zero = new Quaternion();
public static readonly Quaternion One = new Quaternion(1.0f, 1.0f, 1.0f, 1.0f);

同样适用于.Net framework decimal类。这也暴露了这样的公共常量。

public const decimal One = 1m;
public const decimal Zero = 0m;

答案 7 :(得分:6)

当这些数字具有特殊含义时,数字通常会被赋予名称。

例如,在Quaternion情况下,标识四元数和单位长度四元数具有特殊含义,并且经常在特殊上下文中使用。即具有(0,0,0,1)的四元数是一个单位四元数,因此通常的做法是定义它们而不是使用幻数。

例如

// define as static 
static Quaternion Identity = new Quaternion(0,0,0,1);


Quaternion Q1 = Quaternion.Identity;
//or 
if ( Q1.Length == Unit ) // not considering floating point error

答案 8 :(得分:2)

我的第一个编程工作之一是在使用Basic的PDP 11上。 Basic解释器为所需的每个数字分配了内存,因此每次程序提到0时,都会使用一个或两个字节来存储数字0.当然,在那些日子里,内存比现在更加有限,所以很重要节约。

该工作地点的每个程序都以:

开头
10  U0%=0
20  U1%=1

也就是说,对于那些忘记了他们的基础的人:

Line number 10: create an integer variable called U0 and assign it the number 0
Line number 20: create an integer variable called U1 and assign it the number 1

这些变量,按照惯例,从不保留任何其他值,因此它们实际上是常量。他们允许在整个程序中使用0和1,而不会浪费任何内存。

Aaaaah,过去的美好时光!

答案 9 :(得分:1)

有时候写作更具可读性:

double pi=3.14; //Constant or even not constant
...
CircleArea=pi*r*r;

而不是:

CircleArea=3.14*r*r;

并且您可能会再次使用pi(您不确定,但您认为以后或其他课程可能会公开)

然后如果您想将pi=3.14更改为pi=3.141596,则更容易。

和其他一些像e=2.71Avogadro