Tại sao sử dụng biến int cho mã pin khi const int, enum hoặc #define có ý nghĩa hơn nhiều


24

Tại sao mọi người sử dụng một biến để chỉ định số pin khi mã pin không có khả năng thay đổi trong suốt quá trình thực thi mã?

Nhiều lần tôi thấy một intđược sử dụng cho một định nghĩa pin,

int led = 13;

khi sử dụng một const int

const int led = 13;

hoặc enum, hoặc#define

#define LED 13

có ý nghĩa hơn nhiều

Nó thậm chí còn trong các hướng dẫn trên trang web Arduino, ví dụ, hướng dẫn đầu tiên mà hầu hết mọi người chạy, Blink .

Tôi đọc ở đâu đó const intđược ưa thích hơn #define. Tại sao điều này không được khuyến khích ngay từ đầu, thay vì cho phép mọi người phát triển thói quen xấu ngay từ đầu? Tôi nhận thấy nó một thời gian trước, nhưng gần đây nó đã bắt đầu làm tôi khó chịu, do đó câu hỏi.

Bộ nhớ / xử lý / tính toán khôn ngoan là một const int, enumhoặc cho vấn đề đó #define, tốt hơn so với đồng bằng int, tức là chiếm ít bộ nhớ, được lưu trữ trong bộ nhớ khác nhau (Flash, EEPROM, SRAM), thực thi nhanh hơn, biên dịch nhanh hơn?


Điều này có vẻ là một bản sao của Sử dụng #define hoặc const int cho các hằng số? , nhưng tôi đang giải quyết câu hỏi tại sao mọi người sử dụng các biến và hiệu suất cải thiện như thế nào khi họ không sử dụng, thay vì loại hằng số nào tốt hơn.


9
Vì khủng bố kinh khủng. Hầu hết những người có sở thích không phải là lập trình viên dày dạn và vì vậy dạy cho những người có sở thích xấu khác.
Ignacio Vazquez-Abrams

1
Đặc biệt với các chân, hình thức đơn giản của các hàm API arduino cơ bản như digitalWrite không khuyến khích thiết kế nhúng phù hợp, tức là sử dụng mặt nạ và một địa chỉ bộ nhớ duy nhất cho toàn bộ cổng
crasic

Câu trả lời:


20
const int led = 13;

Đó là phương pháp đúng. Hoặc thậm chí:

const byte led = 13;

Bạn có bao nhiêu chân?

Một số hướng dẫn không hoàn toàn kiểm soát chất lượng như họ có thể có.

Hiệu suất sẽ tốt hơn khi sử dụng const byte, so với inttuy nhiên trình biên dịch có thể đủ thông minh để nhận ra những gì bạn đang làm.

Những gì bạn có thể làm là nhẹ nhàng khuyến khích mọi người sử dụng các kỹ thuật hiệu quả hơn bằng cách sử dụng chúng trong mã của riêng bạn.


Phản hồi ý kiến

  1. Một nhà bình luận đã cho rằng đó bytekhông phải là tiêu chuẩn C. Điều này là chính xác, tuy nhiên đây là trang web Arduino StackExchange và tôi tin rằng việc sử dụng các loại tiêu chuẩn được cung cấp bởi Arduino IDE là chấp nhận được.

    Trong Arduino.h có dòng này:

    typedef uint8_t byte;

    Lưu ý rằng điều này không hoàn toàn giống như unsigned char. Xem uint8_t vs uns uns charKhi nào uint8_t unsign char? .

  2. Một nhà bình luận khác đã gợi ý rằng việc sử dụng byte sẽ không nhất thiết phải cải thiện hiệu suất, bởi vì các số nhỏ hơn intsẽ được thăng cấp lên int(xem Quy tắc quảng cáo số nguyên nếu bạn muốn biết thêm về điều này).

    Tuy nhiên, trong bối cảnh của một định danh const , trình biên dịch sẽ tạo mã hiệu quả trong mọi trường hợp. Ví dụ: tháo rời "nháy mắt" đưa ra điều này ở dạng ban đầu:

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

    Trong thực tế, nó tạo ra cùng một mã cho dù 13:

    • Là một nghĩa đen
    • Là một #define
    • Là một const int
    • Là một const byte

Trình biên dịch biết khi nào nó có thể khớp một số vào một thanh ghi và khi nào thì không thể. Tuy nhiên, đó là cách thực hành tốt để sử dụng mã hóa cho biết ý định của bạn . Làm constcho nó rõ ràng rằng số sẽ không thay đổi, và làm cho nó byte(hoặc uint8_t) làm rõ rằng bạn đang mong đợi một số nhỏ.


Thông báo lỗi khó hiểu

Một lý do chính khác cần tránh #definelà các thông báo lỗi bạn nhận được nếu bạn mắc lỗi. Xem xét bản phác thảo "nháy mắt" này có lỗi:

#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);              
}

Nhìn bề ngoài có vẻ ổn, nhưng nó tạo ra các thông báo lỗi sau:

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

Bạn nhìn vào dòng được tô sáng đầu tiên (dòng 4) và thậm chí không thấy biểu tượng "=". Thêm vào đó, dòng trông ổn. Bây giờ khá rõ ràng vấn đề ở đây là gì ( = 13đang được thay thế LED), nhưng khi dòng này nằm sâu hơn 400 dòng trong mã, thì rõ ràng vấn đề không nằm ở cách xác định đèn LED.

Tôi đã thấy mọi người rơi vào điều này nhiều lần (bao gồm cả bản thân tôi).


Bạn có bao nhiêu chân? Nick là một điểm rất tốt, vì hầu hết các bảng chỉ có trong phạm vi hàng chục chứ không phải hàng trăm (tức là lớn hơn 255), do đó, intquá mức cần thiết ... đó là, cho đến khi Arduino cuối cùng xuất hiện với bảng Tera ... :-)
Greenonline

2
C không có byteloại . Bạn có ý nghĩa unsigned char.
Kevin

Hiệu suất sẽ không nhất thiết phải tốt hơn bytethay vì int, trong hầu hết các bối cảnh, giá trị nguyên với các loại nhỏ hơn intđược quảng bá int.
Pete Becker

1
C doesn't have a byte type. You mean unsigned char.- Câu trả lời của tôi là trong bối cảnh Arduino, trong đó có cái này typedef uint8_t byte;. Vì vậy, đối với một Arduino, sử dụng bytelà OK.
Nick Gammon

Performance won't necessarily be better with byte instead of int- xem bài sửa đổi.
Nick Gammon

19

Vì Ignacio đã tuyên bố đúng, về cơ bản là vì họ không biết rõ hơn. Và họ không biết rõ hơn vì những người dạy họ (hoặc tài nguyên họ sử dụng khi học) không biết rõ hơn.

Phần lớn mã Arduino và hướng dẫn được viết bởi những người chưa từng được đào tạo về lập trình và rất "tự học" từ các tài nguyên bởi những người mà bản thân họ rất tự học mà không được đào tạo về lập trình.

Nhiều đoạn mã hướng dẫn tôi thấy ở khắp nơi (và đặc biệt là những đoạn chỉ có trong video YouTube --- urgh) sẽ là một dấu hiệu thất bại nếu tôi đánh dấu chúng trong một bài kiểm tra.

Có, a constđược ưa thích hơn một không phải, và thậm chí hơn một #define, bởi vì:

  • A const(như a #define, không giống như không phải) không phân bổ bất kỳ RAM nào
  • A const(giống như không phải là hằng, nhưng không giống như a #define) cung cấp cho giá trị một kiểu rõ ràng

Điểm thứ hai có mối quan tâm đặc biệt. Trừ khi được nói cụ thể bằng cách đúc kiểu nhúng ( (long)3) hoặc hậu tố kiểu ( 3L) hoặc sự hiện diện của dấu thập phân ( 3.0), #definemột số sẽ luôn là một số nguyên và tất cả toán học được thực hiện trên giá trị đó sẽ như thể nó là một số nguyên. Hầu hết thời gian không phải là vấn đề, nhưng bạn có thể gặp phải các tình huống thú vị khi bạn cố gắng với #definemột giá trị lớn hơn số nguyên có thể lưu trữ, chẳng hạn như #define COUNT 70000và sau đó thực hiện một phép toán với các intgiá trị khác trên đó. Bằng cách sử dụng một constbạn có thể nói với trình biên dịch "Giá trị này được coi là loại biến này" - vì vậy thay vào đó bạn sẽ sử dụng: const long count = 70000;và tất cả sẽ hoạt động như mong đợi.

Nó cũng có hiệu ứng gõ cửa mà nó kiểm tra loại khi truyền giá trị xung quanh địa điểm. Hãy thử chuyển một hàm const longđến một hàm mong đợi intvà nó sẽ phàn nàn về việc thu hẹp phạm vi biến (hoặc thậm chí hoàn toàn không biên dịch tùy thuộc vào kịch bản). Làm điều đó với một #definevà nó sẽ chỉ âm thầm tiếp tục cho bạn kết quả sai và khiến bạn gãi đầu trong nhiều giờ.


7
Cần lưu ý rằng một constbiến có thể yêu cầu RAM, tùy thuộc vào ngữ cảnh, ví dụ: nếu nó được khởi tạo bằng cách sử dụng giá trị trả về từ hàm không phải là constexpr.
Peter Bloomfield

Tương tự, const int foo = 13; bar(&foo);chắc chắn sẽ yêu cầu trình biên dịch phân bổ bộ nhớ thực tế cho foo.
Ilmari Karonen

3
Nếu bạn xác định một macro mở rộng đến một giá trị không phù hợp trong inttrình biên dịch sẽ coi giá trị đó là loại nhỏ nhất mà nó sẽ phù hợp (quy tắc modulo về đã ký so với không dấu). Nếu bạn đang ở trên một hệ thống có int16 bit, #define count 70000sẽ dẫn đến counttrông giống như một long, giống như nó đã được xác định là const long count = 70000;. Hơn nữa, nếu bạn chuyển một trong hai phiên bản đó countcho một hàm mong đợi int, bất kỳ trình biên dịch lành mạnh nào cũng sẽ xử lý chúng như nhau.
Pete Becker

1
Tôi đồng ý với @PeteBecker - một cấu trúc như #define COUNT 70000không cắt ngắn thành một int, nhưng trình biên dịch coi nó như một kiểu đủ lớn để chứa số đó. Đúng là nó có thể không rõ ràng khi bạn sử dụng COUNTnó không phải là một int, nhưng bạn có thể nói điều tương tự về một cách const longnào đó.
Nick Gammon

2
"Một #define sẽ luôn là số nguyên" Điều đó không đúng. Bạn đang sử dụng các quy tắc của số nguyên và áp dụng chúng cho các macro tiền xử lý. Nó giống như so sánh táo và nhạc pop. Các biểu hiện COUNTtrong ví dụ của bạn được thay thế trước khi biên dịch với các biểu hiện 70000, trong đó có một loại được xác định bởi các quy tắc của chữ, giống như 2hoặc 13Lhoặc 4.0được định nghĩa bởi các quy tắc của chữ. Việc bạn sử dụng #defineđể bí danh những biểu thức đó là không liên quan. Bạn có thể sử dụng #defineđể bí danh các đoạn mã C tùy ý, nếu bạn muốn.
Cuộc đua nhẹ nhàng với Monica

2

Là một người mới làm quen với Arduino 2 tuần, tôi sẽ hiểu ý tưởng chung về việc Arduino bị chiếm bởi những người không phải là lập trình viên. Hầu hết các bản phác thảo mà tôi đã kiểm tra, bao gồm cả những bản phác thảo trên trang Arduino, cho thấy hoàn toàn thiếu trật tự, với các bản phác thảo không hoạt động và hầu như không có nhận xét mạch lạc trong tầm nhìn. Biểu đồ dòng chảy là không tồn tại và "Thư viện" là một mớ bòng bong không được kiểm duyệt.


0

Câu trả lời của tôi là ... họ làm điều đó bởi vì nó hoạt động. Tôi đang gặp khó khăn khi không đặt câu hỏi trong câu trả lời của mình, chẳng hạn như "tại sao nó phải 'sai'?"


3
Một đặc điểm của một lập trình viên giỏi là mã luôn phản ánh ý định của họ.
Ignacio Vazquez-Abrams

1
Chúng ta vẫn đang nói về Arduinos, phải không? ;)
linhartr22

3
Arduino đã có một đại diện xấu trong cộng đồng EE lớn hơn vì các thiết kế phần cứng tầm thường đến khủng khiếp được đưa ra bởi cộng đồng. Chúng ta không nên cố gắng đưa ra một cái gì đó chứ?
Ignacio Vazquez-Abrams

2
"Hầu hết các dự án sẽ không liên quan đến rủi ro về tính mạng hoặc tài chính ..." Không có gì ngạc nhiên ở đó. Ai sẽ muốn liên quan đến Arduino khi có bất kỳ rủi ro nào sau khi nhìn vào cộng đồng nói chung.
Ignacio Vazquez-Abrams

2
Điều đó 'sai' không phải vì nó không hoạt động trong một tình huống cụ thể mà bởi vì, so với việc làm 'đúng', có nhiều tình huống hơn trong đó nó không hoạt động. Điều này làm cho mã dễ vỡ; thay đổi mã có thể gây ra lỗi thất bại làm mất thời gian gỡ lỗi. Các thông báo lỗi và kiểm tra loại của trình biên dịch có mặt để giúp bạn nắm bắt các loại lỗi đó sớm hơn là sau này.
Curt J. Sampson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.