当const int,enum或#define更有意义时,为什么要对引脚使用int变量


24

当引脚在整个代码执行过程中不太可能更改时,为什么人们使用变量来指定引脚号?

很多次,我看到int有人用它来定义引脚,

int led = 13;

当使用 const int

const int led = 13;

enum#define

#define LED 13

更有意义。

甚至在Arduino网站上的教程中,例如,大多数人都运行的第一篇教程Blink

读的地方const int优于#define。为什么从一开始就不鼓励这样做,而不是从一开始就不让人们养成不良习惯?我注意到它有一段时间了,但是最近它开始激怒了我,因此成为了问题。

存储器/处理/计算明智是const intenum,或就此而言#define,比一个普通更好int,即占据较小的存储器,存储在不同的存储器(闪存,EEPROM,SRAM),更快的执行,更快的编译?


这似乎是的重复。对于常量使用#define或const int更好吗?,但我要解决的问题是人们为什么使用变量,不使用变量时性能如何提高,而不是哪种类型的常数更好。


9
因为可怕变得可怕。大多数业余爱好者不是经验丰富的程序员,因此会教给其他业余爱好者坏习惯。
伊格纳西奥·巴斯克斯

1
特别是对于引脚,基本的arduino API函数(如digitalWrite)的简化形式不鼓励正确的嵌入式设计,即,对整个端口使用掩码和单个内存地址
crasic 15'Aug

Answers:


20
const int led = 13;

那是正确的方法。甚至:

const byte led = 13;

你有几针?

一些教程并没有经过尽可能多的质量控制。

const byte与相比,使用性能会更好,int但是编译器可能足够聪明,可以实现您正在执行的操作。

您可以做的就是轻轻地鼓励人们在自己的代码中使用更有效的技术。


对评论的回应

  1. 评论者建议使用byte的不是标准C语言。这是正确的,但是这是Arduino StackExchange网站,我相信可以使用Arduino IDE提供的标准类型。

    在Arduino.h中有以下行:

    typedef uint8_t byte;

    请注意,这与并不完全相同unsigned char。参见uint8_t与无符号字符什么时候uint8_t≠无符号字符?

  2. 另一位评论者建议使用字节不一定会提高性能,因为小于的数字int将被提升为int整数(如果需要更多信息,请参阅整数提升规则)。

    但是,在const标识符的上下文中,无论如何编译器都会生成有效的代码。例如,反汇编“ blink”将以原始形式显示:

    00000086 <loop>:
      86:   8d e0           ldi r24, 0x0D   ; 13
      88:   61 e0           ldi r22, 0x01   ; 1
      8a:   1b d1           rcall   .+566       ; 0x2c2 <digitalWrite>

    实际上,无论是否13:,它都会生成相同的代码:

    • 是文字
    • 是一个 #define
    • 是一个 const int
    • 是一个 const byte

编译器知道什么时候可以将数字放入一个寄存器,什么时候不可以。但是,使用编码表明您的意图是一种好习惯。使得它清楚地表明,数量不会改变,并使它(或)清楚地表明,你期待一个小数目。constbyteuint8_t


令人困惑的错误消息

要避免的另一个主要原因#define是如果您犯了错误,则会收到错误消息。考虑这个有错误的“眨眼”草图:

#define LED = 13;

void setup() {
  pinMode(LED, OUTPUT);      // <---- line with error
}

void loop() {
  digitalWrite(LED, HIGH);   // <---- line with error 
  delay(1000);             
  digitalWrite(LED, LOW);    // <---- line with error
  delay(1000);              
}

表面上看起来不错,但是会生成以下错误消息:

Blink.ino: In function ‘void setup()’:
Blink:4: error: expected primary-expression before ‘=’ token
Blink:4: error: expected primary-expression before ‘,’ token
Blink:4: error: expected `;' before ‘)’ token
Blink.ino: In function ‘void loop()’:
Blink:8: error: expected primary-expression before ‘=’ token
Blink:8: error: expected primary-expression before ‘,’ token
Blink:8: error: expected `;' before ‘)’ token
Blink:10: error: expected primary-expression before ‘=’ token
Blink:10: error: expected primary-expression before ‘,’ token
Blink:10: error: expected `;' before ‘)’ token

您看着第一行突出显示的行(第4行),甚至没有看到 “ =”符号。另外,这条线看起来还不错。现在,这里的问题已经很明显了(= 13被代替了LED),但是当代码中的代码行向下四百行时,问题就出在LED的定义方式上。

我见过很多人跌倒(包括我自己)。


你有几针?Nick是一个很好的观点,因为大多数开发板只有几十个范围,而不是数百个(即大于255个),所以这int是一个过大的决定……也就是说,直到Arduino最终与Tera开发出来为止。 :-)
Greenonline

2
C没有byte类型。你是说unsigned char
凯文

使用byte代替不一定会带来更好的性能int,因为在大多数情况下,类型小于的整数值int会提升为int
皮特·贝克尔

1
C doesn't have a byte type. You mean unsigned char.-我的回答是在Arduino上下文中,它具有this typedef uint8_t byte;。因此,对于Arduino,使用byte就可以了。
尼克·加蒙

Performance won't necessarily be better with byte instead of int-参见修订后的帖子。
尼克·加蒙

19

正如伊格纳西奥(Ignacio)正确指出的那样,这基本上是因为他们并不了解。而且他们并不了解得更好,因为教他们的人(或者他们在学习时使用的资源)并不了解。

Arduino的许多代码和教程都是由从未接受过编程培训的人员编写的,并且大多是从自己的资源中“自学而成”的,而这些人本身就是未经编程的自学成才的。

如果我在考试中标记了我周围的许多教程代码片段(尤其是那些仅在YouTube视频中显示的片段),那将是一个失败标记。

是的,a const优于非const,甚至优于#define,因为:

  • A const(类似于a #define,与非const不同)不分配任何RAM
  • A const(类似于非const,但与a不同#define)为值提供了显式类型

第二点特别令人感兴趣。除非特别具有嵌入型铸造(否则告诉(long)3)或类型后缀(3L)或小数点(的存在3.0),一个#define数字将始终是一个整数,该值进行所有的数学将是就好像它是一个整数。在大多数情况下,这不是问题,但是当您尝试#define大于一个整数可以存储的值时,您可能会遇到一些有趣的情况,例如#define COUNT 70000然后对其他int值执行数学运算。通过使用a,const您可以告诉编译器“此值将被视为此变量类型”-因此,您将改为使用:const long count = 70000;并且所有操作均将按预期进行。

它还具有连锁效应,当在周围传递值时会检查类型。尝试将a传递const long给期望使用an的函数,int它会抱怨变差范围变窄(或者甚至完全无法根据情况进行编译)。使用a进行操作#define,它只会默默地给您带来错误的结果,并使您挠头挠头几个小时。


7
值得注意的是,取决于上下文,const变量可能需要RAM,例如,是否使用非constexpr函数的返回值初始化变量。
彼得·布卢姆菲尔德

同样,const int foo = 13; bar(&foo);肯定会要求编译器为分配实际内存foo
Ilmari Karonen

3
如果定义了一个宏,该宏扩展为一个不适合的值,则int编译器会将值视为具有适合其的最小类型(关于有符号与无符号的模数规则)。如果您使用的系统int是16位,#define count 70000则将count看起来像a long,就好像它被定义为一样const long count = 70000;。此外,如果您将这两个版本中的任何count一个传递给函数Expecting int,任何明智的编译器都将对它们进行相同的处理。
皮特·贝克尔

1
我同意@PeteBecker-这样的结构#define COUNT 70000不会截断为int,但是编译器将其视为足以容纳该数字的类型。的确,当您使用COUNT它不是整数时,它可能并不明显,但const long无论如何您都可以说同样的话。
尼克·加蒙

2
“ #define将始终是整数”,这是不正确的。您正在采用整数文字的规则,并将其应用于预处理器宏。这就像比较苹果和流行音乐。COUNT示例中的表达式在编译之前被expression替换,该表达式70000的类型由文字规则定义,就像2or 13L或or 4.0由文字规则定义。使用#define别名为这些表达式的事实是无关紧要的。#define如果愿意,可以使用C代码的任意块的别名。
与莫妮卡(Monica)进行的轻度比赛

2

作为Arduino的2周新手,我会了解Arduino被非程序员占领的一般想法。我检查过的大多数草图(包括Arduino网站上的草图)都显示出完全缺乏秩序,草图不起作用,并且几乎看不到连贯的注释。流程图不存在,并且“库”是无节制的混乱。


0

我的回答是...他们这样做是因为它有效。我很难不回答我的问题,例如“为什么一定要'错'?”


3
优秀程序员的一个标志是,代码始终可以反映出他们的意图。
伊格纳西奥·巴斯克斯

1
我们还在谈论Arduino,对吗?;)
linhartr22

3
Arduino在较大的EE社区中已经表现不佳,因为该社区推出了中等至糟糕的硬件设计。我们不应该对某事不屑一顾吗?
伊格纳西奥·巴斯克斯

2
“大多数项目都不会涉及生命或财务风险……”毫不奇怪。谁想要在整个社区都拥有Arduino的情况下参与冒险,谁愿意参与其中。
伊格纳西奥·巴斯克斯

2
这是“错误的”,不是因为它在一种特定的情况下不起作用,而是因为与“正确”的情况相比,在更多的情况下它不起作用。这使代码易碎。对代码的更改可能会导致神秘的故障,从而耗尽调试时间。编译器的类型检查和错误消息可帮助您尽早(而不是稍后)捕获这类错误。
Curt J. Sampson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.