Translate

Hiển thị các bài đăng có nhãn Embedded programming. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Embedded programming. Hiển thị tất cả bài đăng

16 tháng 4, 2020

[100daysTIL] Day 6 - Coding Style

Bản thân mình không phải là một người hoạt động chuyên sâu về lĩnh vực lập trình, nhưng mình cũng có tìm hiểu về một vài điều căn bản như là Coding Style. Và chủ đề ngày hôm nay, mình sẽ chia sẻ những kiến thức về khía cạnh này mà mình đã tìm hiểu được. Bài viết hôm nay sẽ là phần tiếp theo của cuốn sách "Lập trình nhúng: Chuyện chưa kể" của tác giả Nguyễn Thành Công. 

Ảnh được lấy từ trang web này
Hầu hết các trang mình đọc khi nhắc về coding style, họ sẽ đưa ra các quy chuẩn về cách đặt tên biến, vị trí của dấu chấm, dấu phẩy, dấu ngoặc nhọn,  . . . Nhưng tác giả của cuốn sách này lại có phương thức tiếp cận hoàn toàn khác. 

Đầu tiên thì, theo bạn coding style là gì? Có lẽ bạn sẽ ý thức sâu sắc hơn về việc này sau khi: bạn thấy code của mình rối tung rối mù, viết xong một thời gian sau quay lại đọc thì . . . tuyệt vời, chả hiểu mô tê gì sất; lấy code của thằng bạn đọc để fix bug cho nó mà đọc muốn rớt con mắt ra ngoài; hay khi thêm một tính năng mới thì toang, phải bỏ hết đống code cũ và làm lại từ đầu. Và coding style giúp bạn giải quyết mấy chuyện đó, thật tuyệt vời !!! Những bậc cao nhân trong nghiệp lập trình chỉ cần nhìn những dòng code của họ thôi là chúng ta đã thấy phê rồi. 

Có 3 yêu cầu chính mà mấy gã lập trình viên cần đạt được là: Chạy được, dễ thay đổi, dễ hiểu.
  • Chạy được:  Đây là ưu tiên số một rồi, viết xong mà không chạy được thì cũng vứt đi thôi =))
  • Dễ thay đổi: Trên thực tế, các yếu tố đầu ra luôn thay đổi không ngừng. Giống như việc khách hàng muốn cái này, rồi thấy cái kia hay quá lại đòi thêm vào a,b,c thứ nữa; hoặc ông sếp của bạn tìm được phương án ngon cơm hơn cái hiện tại thế là bạn buộc phải bẻ lái giữa đường, . . .
    Ví dụ cụ thể như việc chúng ta thiết kế một cái xe nhỏ nhỏ chạy theo đường kẻ sẵn, làm xong rồi lại muốn gắn thêm cái camera cho nó chạy, sau khi xong rồi lại muốn gắn thêm cái đèn nhấp nháy cho vui mắt chẳng hạn. Hoặc mục tiêu ban đầu thực hiện xong rồi mà bạn thấy nó chạy cứ bị delay, tác vụ trễ lên trễ xuống, muốn viết lại xíu để nâng cấp firmware lên nhưng nhìn đi nhìn lại thấy đống code của mình nó bùng nhùng ghê gớm. Chẳng nhẽ phải đập đi xây lại à???
  • Dễ hiểu: Trong thực tế, bạn sẽ không phải làm các dự án một mình mà sẽ có một team để support lẫn nhau. Bạn sử dụng code của người khác để viết thêm, và người sau sẽ sử dụng code của bạn để phát triển các tính năng mới, hoặc bảo trì chẳng hạn. Hay khi bạn đọc lại code của mình sau một thời gian dài không sử dụng, lúc đấy lại lắc đầu thở dài: "không biết ngày xưa mình vẽ hươu vẽ vượn gì trong đây nữa?"
    Do đó, bạn phải viết code để người khác có thể hiểu được.
Và coding style là con đường giúp bạn chinh phục được các mục tiêu trên. Nhiều bậc cao nhân, cũng như các lập trình viên lão luyện đã khẳng định thế này: "Lập trình không có khó, cái củ chuối nhất là ngồi fix bug kìa". Thế nên việc hình thành một coding style sẽ giúp cuộc sống của bạn trở nên dễ thở hơn rất nhiều đấy. 
Ngoài việc chịu khó ngồi code, bạn còn phải chăm đọc sách hoặc thỉnh giáo các sư phụ, để có thể làm việc hiệu quả hơn. Rồi tới một ngày không xa, bạn sẽ nhận ra một điều là viết code cũng giống như viết văn xuôi vậy.

1) Module hóa

Mọi hệ thống lớn đều được cấu thành từ các module nhỏ, nên là bạn hãy chia nhỏ chương trình của mình thành các module, các file có chức năng khác nhau để dễ quản lý hơn nhé =)))

Module hóa trong trình biên dịch Keil C
Trong trình biên dịch Keil C các file có chức năng tương đương nhau được gộp vào các chung một thư mục. Dựa vào các thư mục riêng biệt chúng ta có thể kiểm soát code của mình một cách tốt hơn. 


Nguyên tắc chia module: Module A có thể biết module B làm được cái gì nhưng nó không biết module B làm điều đó như thế nào. Ví dụ bạn sử dụng hàm truyền dữ liệu Serial.wire() trong Arduino chẳng hạn, chắc chắn hàm sẽ truyền dữ liệu đi cho bạn, nhưng bạn không cần biết nó chuyển dữ liệu ấy đi như thế nào. A giao việc cho B theo một phương thức quy định sẵn và chấm hết. B sẽ báo về cho A rằng công việc ấy có thành công hay không, hoặc nếu thất bại thì thất bại vì lý do gì. 

Điều này giúp làm tăng tính độc lập giữa các module với nhau, làm cho cả chương trình trở nên rõ ràng hơn. Ví dụ khi lỗi xảy ra bạn có thể biết được ở A hay B gây ra lỗi, giúp bạn có thể khoanh vùng phạm vi lỗi của chương trình một cách tốt hơn. Điều này giống như việc khi bạn nâng cấp thư viện mà chương trình chính của bạn vẫn hoạt động bình thường.

2) Cấu trúc chương trình chính và cách gọi hàm

Trong một ứng dụng nhúng, hàm main thường cấu thành bởi các hàm khởi tạo và vòng lặp chương trình.
--------------------------------------------------------------
int main(void)
{
  system_init()// Hàm khởi tạo
  while(1)       // Vòng lặp chương trình
  {
    //Do what you want
  }
}
--------------------------------------------------------------
Trên đây là cấu trúc cơ bản của một chương trình nhúng. Ngoài ra còn một khái niệm không kém quan trọng nữa đó là mức trừu tượng. Chúng ta cùng xem xét ví dụ đọc cảm biến sau:
--------------------------------------------------------------
void read_sensor(void)
{
  request_sensor_data();
  wait_for_sensor_data();
  save_sensor_data();
}
--------------------------------------------------------------
Việc đầu tiên sau khi gọi hàm read_sensor() là gửi một yêu cầu tới cảm biến, sau đó đợi phản hồi và lưu lại giá trị phản hồi vào đâu đó trong bộ nhớ. Bạn có thể thấy mức trừu tượng của hàm read_sensor() sẽ cao hơn các hàm:
request_sensor_data();
wait_for_sensor_data();
save_sensor_data(); 
Vì nó bao gồm các hàm này.

Tóm lại là, hãy phân thứ bậc cho các hàm (giống mô hình đa cấp ý :). Bạn có thể quan sát hình vẽ dưới đây để có thể hình dung vấn đề một cách dễ dàng hơn :D

Mô hình đa cấp
Nhớ là không phải mình làm màu mà viết tên hàm tiếng Anh đâu, vì tiếng Việt không dấu nó viết như teen_code vậy đọc không nổi, mà nhiều khi hiểu lộn nghĩa nữa :)

3) Hàm số 

Việc phát minh ra hàm con là phát minh vĩ đại giống như phát minh ra cái máy tính vậy. Nếu không có hàm con thì bạn thử tưởng tượng viết chương trình một lèo từ đầu tới cuối xem :v  
Dưới đây là một số lưu ý khi sử dụng hàm:

a) Đặt tên hàm
Tên hàm được đặt ra để xác định cái hàm con đó nó làm cái gì. Ví dụ như read_sensor() chẳng hạn, dù bạn chẳng cần biết cảm biến loại gì và cách đọc nó ra sao nhưng bạn cũng hiểu sơ qua về tác dụng của cái hàm này. 
Thêm nữa, mỗi hàm chỉ nên thực hiện một chức năng duy nhất. Nó chỉ làm những nhiệm vụ mà tên hàm nói đến, không hơn không kém. Trong khi viết code thì mọi thứ nên được viết tường minh ra. ví dụ như hàm send_data() để gửi dữ liệu, nếu gửi dữ liệu xong mà muốn xóa dữ liệu cũ đi thì gọi hàm delete_data()

Không nên nhét hàm delete_data() vào bên trong hàm send_data() như muốn hiểu ngầm "gửi xong rồi thì xóa đi chứ còn giữ lại làm gì". Vì nhiều khi có việc cần dùng đến nó. Viết tường minh và tránh những chỗ hiểu ngầm sẽ giúp người đọc nắm đủ các bước thực hiện của bạn. 
Hàm được thực thi càng chính xác với cái tên của nó bao nhiêu thì code của bạn càng dễ đọc bấy nhiêu. Và đặc biệt, tránh kiểu viết trẻ trâu thích thể hiện, viết đoạn code thật ngầu, thật nhỏ xíu mà chạy vẫn ngon. Sau này chỉ khổ cho mấy người phải đọc lại nó.
b) Độ dài của hàm
Nguyên tắc để code dễ đọc là viết ngắn thôi, với lại tên hàm phải đặt đúng với yêu cầu.

Trong một số trường hợp, hàm chỉ hoạt động tốt khi nó đủ dài, cắt ngắn lại thành ra dở. Tuy nhiên, đa số các trường hợp chúng ta có thể cắt một hàm dài bất tận ra thành những hàm nhỏ, chia giai đoạn ra rồi đặt các hàm con theo từng giai đoạn của chương trình. Cũng có rất nhiều trường hợp, hàm viết ra để gọi có một lần. Tuy không có tác dụng là tránh lặp lại code như mục đích của những hàm thông thường, nhưng lại làm cho chương trình của bạn trở nên gọn gàng và dễ đọc hơn rất nhiều.

Theo các bậc cao nhân thì độ dài dưới 10 dòng là okie nhất, trên 20 dòng thì nên cắt nó đi.

c) Truyền tham số
Tham số là nơi giao tiếp giữa hàm này với hàm kia. Nó là nơi giao thương nên giống như cái chợ vậy, rất nhiều vấn đề phát sinh. Nên một hàm cần hạn chế tối đa các đầu vào, chỉ dữ lại những tham số tối quan trọng. Nếu phải viết một hàm với 5 thông số đầu vào thì làm sao bạn nhớ được thứ tự của các tham số đó mà truyền cho đúng đây?

d) Bảo vệ hàm số
Giả sử bạn viết một hàm chia a cho b như sau:
--------------------------------------------------------------
float a_divide_b(int a, int b)
{
  return (float)a/b;
}
--------------------------------------------------------------
Rồi bỗng một ngày đẹp trời, có thanh niên lấy hàm bạn viết ra và sài như sau:
float result = a_divide_b(2, 0);
Biên dịch thì không báo lỗi nhưng chạy thì xịt khói tứ tung. Đây là lỗi chia cho 0 nên máy nó không tính được và thế là cứ chạy loạn xạ. 

Bởi vậy cho nên mỗi chương trình có truyền tham số vào, hãy nhớ kiểm tra xem nó có hợp lệ hay không, nếu tính toán thì tham số đưa vào có thuộc tập xác định hay không . . . 
Nguyên tắc kiểm tra là: Không tin cha con thằng nào cả. Đừng hy vọng ai đó sẽ truyền vào than số hợp lệ, thường thì họ chả quan tâm đâu. Nên hãy nhớ kiểm tra đầu vào hợp lệ trước khi thực thi chương trình nhé.

4) Các biến số

Tất nhiên là khi lập trình bạn đều phải khai báo biến, rồi sử dụng các biến đó để lưu trữ, tính toán. Thế nên việc sử dụng cẩn thận các biến là điều rất quan trọng để chương trình có thể chạy được và ít phát sinh lỗi.
a) Đặt tên biến
Cũng giống như đặt tên hàm, tên biến nói lên ý nghĩa sử dụng của chính nó. Hồi mới học code, chúng ta vẫy hay sử dụng các chữ cái như: a, b, c ... hoặc x1, x2 ... để đặt tên biến. Nhưng mà tin mình đi, mấy cái đó đọc nhức mắt kinh khủng, nhiều khi trong lúc code bạn cũng không nhớ khai báo nó để lưu cái gì nữa. Tai hại hơn là bạn lại lưu vào x1 giá trị mà đáng lí ra phải lưu vào x2.

Theo thường lệ thì các chữ cái i, j, ... được dùng trong vòng lặp cho tiện. Nhưng còn các loại biến khác thì hãy đặt cho nó cái tên để gắn liền với chức năng của nó.

b) Các lưu ý khi khai báo biến
  • Tránh sử dụng biến toàn cục (Global variable): Biến toàn cục được khai báo bên ngoài hàm, là nơi mà bất kỳ hàm nào cũng có thể truy cập và chỉnh sửa nó.

    Hãy tưởng tượng là trong chương trình của bạn sử dụng biến toàn cục, và có khoảng 20 hàm được khai báo. Hàm nào cũng có quyền truy cập vào biến toàn cục này, nên bất cứ lúc nào nó cũng có thể bị thay đổi. Tệ hơn nữa nếu file code này không chỉ có một mình bạn viết, mà lại có nhiều người viết chung; việc không hiểu ý mà tự tiện truy cập vào biến toàn cục là chuyện bình thường.

    Thế nên hãy hạn chế sử dụng biến toàn cục hết mức có thể, và tốt nhất là không khai báo kiểu biến này càng tốt. Khi nhất thiết phải sử dụng biến toàn cục thì nên chú thích rõ ràng, nơi nào được đọc, ghi biến này để tránh việc thay đổi giá trị của biến mà không biết trước.

    Biến toàn cục là đồ công cộng. Ai cũng đụng chạm nó được. Mà đồ công cộng thì không an toàn. Chỉ vậy thôi =))))
  • Hãy khởi tạo giá trị cho biến. Khi khai báo biến mà không khởi tạo giá trị cho nó, CPU sẽ chỉ định vị trí biến này trong bộ nhớ điều này có thể gây lỗi khi bạn sử dụng các phép toán cho nó. Ví dụ như phép OR chẳng hạn, kết quả lúc này sẽ là bạt ngàn các con số khác nhau nhé :v
  • Luôn chắc chắn rằng biến của bạn chứa đủ giá trị mà bạn muốn lưu trong nó. Ví dụ như biến được khai báo kiểu uint_8 thì khoảng giá trị của nó sẽ nằm trong 0 - 255. Nếu bạn tình cờ ghi vào trong biến này một giá trị lớn hơn 255 thì bạn biết điều gì sẽ xảy ra rồi đấy, nó sẽ xịt khói tùm lum cho mà coi :v

5) Chú thích

Chú thích (Comment) Tưởng đơn giản nhưng lại là thành phần hại não nhất trong khi viết chương trình. Trong ngôn ngữ C, chú thích thường theo sau dấu // hoặc /* */. Vậy thì nó hại não ở chỗ nào được nhỉ? 

Nếu chú thích đúng cách thì chương trình sẽ rất dễ đọc, và người khác (ngay cả bạn nữa) cũng nắm được đại ý nhanh chóng. Nhưng khổ cái là chương trình thay đổi liên tục, mà hễ thay đổi thì chỉnh sửa lại code thôi, chứ không ai chịu sửa lại chú thích làm chi (vì vốn dĩ sửa code cũng đã đuối lắm rồi). Nên làm cho người đọc tưởng rằng mình đã hiểu, nhưng một hồi đọc tới code thì thấy nó sai sai :v

Thế nên cách chú thích tốt nhất là hạn chế nó hết mức có thể. Sử dụng tên hàm, tên biến như là một loại chú thích thì sẽ hiệu quả hơn nhiều. 
Tất nhiên là không nên bỏ hẳn nó đi, vì nếu được sử dụng đúng cách thì nó sẽ đem lại hiệu quả rất nhiều. Ví dụ như các chương trình thường đặt chú thích lên đầu trang để giải thích khái quát về ý nghĩa sử dụng của nó. Hoặc trong file .h, các chú thích được đặt lên trước tên hàm để giải thích công dụng của hàm đó, để người sử dụng có thể sử dụng luôn thư viện mà không cần phải đọc tới file code. Đôi khi những đoạn code khó đọc, tính logic cao thì nên có chú thích để giải thích xem ý tưởng đằng sau đoạn code đó là gì, nó giải quyết vấn đề gì.
Ui ui, sau mấy tiếng đồng hồ ngồi gõ thì bây giờ cũng đã 3h44 phút sáng mất rồi. Mình phải đi ngủ đây, hẹn gặp lại các bạn vào ngày mai nhé. Nhân tiện thì chúc một ngày mới tốt lành ^^

P/s: Các bạn có thể tham khảo rõ hơn ở trong cuốn CLEAN CODE của tác giả Robert C. Martin.

14 tháng 4, 2020

[100daysTIL] Day 4 - About C Embedded Language

Logo của group DÂN KỸ THUẬT
Cách đây vài năm, mình có tham gia một group rất hay trên facebook có tên là DÂN KỸ THUẬT. Mục tiêu của mình khi tham gia group này là để nâng cao kiến thức, kỹ năng cũng như học hỏi thêm những điều mới lạ từ các vị tiền bối đi trước. Và may mắn thay, ở đây mình đã tìm được một cuốn sách thật hay về lập trình nhúng. Và mình quý nó như là báu vật vậy, nên bạn bè thân thích gì là mình chia sẻ hết cả. Cuốn sách có tựa đề là "Lập trình nhúng: Chuyện chưa kể" của tác giả Nguyễn Thành Công, xuất bản ngày 15 tháng 7 năm 2018. Hôm nay mình sẽ nói về cuốn sách này, và những điều mình đã học được từ nội dung của cuốn sách. 
Đầu tiên là văn phong của cuốn sách thật sự gần gũi và thân thuộc với độc giả, phù hợp với những cậu sinh viên mới vào nghề như mình. Lối viết dí dỏm và lôi cuốn, thú vị lắm các bạn ạ ^^ Trước hết là khái niệm lập trình nhúng, nghe qua thì có vẻ xa xôi và lạ hoắc nhưng bản chất của nó thì chỉ có một: 
Lập trình nhúng là việc viết các chương chình điều khiển phần cứng, bạn vừa phải biết lập trình, lại vừa phải giỏi về phần cứng. 
Đó là điều kiện cần và đủ để bắt đầu với cái nghề này :v Ngoài ra, trong lập trình nhúng có rất nhiều ngôn ngữ khác nhau như: C, C++, Java và thậm chí có cả Python nữa. Nhưng ngôn ngữ C thì có khả năng tương tác mạnh hơn cả, và nó cũng cung cấp những giải pháp tối ưu về tốc độ cho các ứng dụng nhúng. Hơn nữa C cũng là ngôn ngữ mà tác giả đề cập trong nội dung cuốn sách này. Về cú pháp của C thì nó quanh đi quẩn lại chỉ là khai báo biến, rồi mấy vòng lặp for, while hoặc rẽ nhánh if, else chẳng hạn, nhưng kỹ năng sử dụng C để giải quyết một vấn đề thì cần nhiều bài tập để trau dồi. Giống như tác giả đã nói: 
Bạn không thể đọc sách học bơi là biết bơi, không thể đọc kiếm phổ là thành cao thủ.

 Ngôn ngữ C trong lập trình nhúng

1) Cơ bản về chương trình
Ý nghĩa của việc lập trình là chỉ cho cái máy biết, bạn muốn nó làm cái gì. Để hiểu một chương trình hoạt động ra sao, có 3 thứ mà bạn cần đặc biệt lưu tâm đó là ROM, RAM và CPU. Còn một khái niệm nữa không kém phần quan trọng đó là trình biên dịch, đây là nơi bạn viết và thực thi các dòng lệnh của mình bằng một ngôn ngữ lập trình nhất định. Trình biên dịch sẽ chuyển các dòng lệnh (người hiểu được) sang ngôn ngữ máy (máy hiểu được), và nạp vào ROM. Khi khởi chạy chương trình, CPU sẽ đọc các dữ liệu được lưu trong ROM và thực thi. ROM giống như ông trùm vậy, ra lệnh thế nào thì CPU phải làm thế đó và không được cãi lời. Còn RAM là bộ nhớ được sử dụng để thực hiện chương trình, nên phải đọc và ghi liên tục. CPU được toàn quyền sử dụng bộ nhớ này, giống như việc CPU là trưởng phòng thì RAM là nhân viên ý. 

Tóm lại: 
- ROM (Read only memory): Bộ nhớ chỉ cho phép đọc, khi mất điện dữ liệu vẫn còn đó.
- RAM (Random access memory): Bộ nhớ truy cập ngẫu nhiên, khi mất điện dữ liệu bị xóa hoàn toàn.
- CPU (Control processing unit): Là bộ xử lý chung tâm, chịu trách nhiệm thực thi chương trình.

2) Cách tổ chức bộ nhớ
Thông thường thì đơn vị nhỏ nhất của bộ nhớ là Byte (hay còn gọi là 1 ô nhớ), và ứng với mỗi byte sẽ có một địa chỉ riêng biệt. Mỗi ô nhớ sẽ có 2 thông số mà chúng ta cần quan tâm đó là:  
- Địa chỉ (nó ở đâu, địa chỉ có thể là số 8-bit, 16-bit, 32-bit...)
- Giá trị được lưu (là bao nhiêu, chỉ là số 8-bit (1 byte) thôi)
Dữ liệu trong bộ nhớ

3) Khai báo biến
Các kiểu biến thông thường khi lập trình C là char, int, long, float, double. Nhưng trong lập trình nhúng, tài nguyên bộ nhớ hạn chế nên việc bạn biết các biến chiếm bao nhiêu ô nhớ là điều rất quan trọng. Thông thường, các biến được khai báo dưới dạng uint8_t, int8_t, uint16_t, int16_t... để sử dụng thì bạn cần #include <stdin.h>. Một điểm đặc biệt nữa là kiểu uint8_t thường được dùng để đại diện cho một ô nhớ (8-bit), uint16_t sẽ yêu cầu bộ nhớ cấp 2 ô nhớ liền kề (16 bits), . . . Hãy thường sử dụng các kiểu dữ liệu với bộ nhớ tường minh trên để kiểm soát bộ nhớ chặt chẽ hơn nhé.

4) Kiểu dữ liệu tự định nghĩa (typedef)
Ngôn ngữ C cung cấp cơ chế tự định nghĩa kiểu dữ liệu để việc truy xuất dữ liệu được thuận tiện. Chúng ta sẽ cùng nhau xem xét ví dụ sau để hiểu hơn về vấn đề này nhé :v
typedef struct { 
  int8_t   temp; 
  uint8_t  humi; 
  uint16_t lux ; 
}env_t
Khi bạn tự định nghĩa một kiểu dữ liệu như trên, thực chất là bạn đang tương tác với một vùng nhớ cho trước. Nếu bạn khai báo một biến env_t evn; thì ngay lập tức, nó sẽ cung cấp cho bạn 4 ô nhớ liền nhau. Nếu bạn in địa chỉ của biến evn ra nó sẽ hiển thị địa chỉ ô nhớ đầu tiên của dãy 4 ô nhớ đó. Và kiểu env_t sẽ cho máy tính biết cách truy cập tới 4 ô nhớ đó như thế nào.
Truy cập biến kiểu evn_t
Một điểm cần lưu ý là các máy tính thường có cơ chế làm tròn biên kiểu dữ liệu (data structure alignment). Nếu chúng ta khai báo như sau:
typedef struct { 
  int8_t   temp;
  uint16_t lux ; 
  uint8_t  humi; 
}env_t
Biến lux khai báo ở giữa, thì kiểu dữ liệu env_t giờ đây có độ dài là 6 byte chứ không phải 4!!!. Lúc này cấu trúc của kiểu biến env_t sẽ có dạng như sau:
Truy cập biến kiểu evn_t
Hai ô nhớ tên padding được thêm vào để tăng hiệu suất việc đọc ghi dữ liệu trong máy tính hiện đại. Nếu các bạn quan tâm thì có thể tìm hiểu sâu hơn nhé.

5) Con trỏ
Có thể nói con trỏ là công cụ lợi hại nhất của C, bạn khó mà giỏi C nếu bỏ qua con trỏ được. Bản chất của con trỏ (chưa nói đến con trỏ hàm) là trỏ tới một vùng nhớ nào đó và tương tác với vùng nhớ đó. Một con trỏ cần 2 thông tin sau để có thể hoạt động được: địa chỉkiểu dữ liệu nó sẽ trỏ tới. 2 yếu tố trên giúp bạn có thể đi đến vùng nhớ mà bạn quan tâm sau đó có thể truy cập vùng nhớ đó theo cách bạn muốn. Một lưu ý nữa là ngôn ngữ C đồng nhất giữa mảng và con trỏ. Ví dụ mình khai báo mảng: uint32_t arr[4]; Lúc này ta sẽ có arr là địa chỉ của phần tử đầu tiên của mảng, hay arr = &arr[0] (cùng 1 địa chỉ),  *arr = arr[0] (cùng 1 giá trị).

Trên đây là một số tóm lược của mình về nội dung cuốn sách, cảm ơn các bạn đã đón đọc.