Có 2 trường hợp xảy ra:1 Nếu vùng nhớ 0x0000ffff chưa được thằng nào dùng không có chương trình nào trong cùng máy tính dùng, thì sẽ lấy đc 1 giá trị ngẫu nhiên, lung tung nằm trong vùng
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG
Giảng viên hướng dẫn: ThS.Vũ Đức Vượng
Hà Nội, 06/2021
Trang 21.1.2 Sử dụng dereferencer con trỏ làm tốn thời gian 4
1.2 Một số câu hỏi đặc biệt 4
Phần 2: Các vấn đề lý thú liên quan đến cấp phát bộ nhớ động 5
2.1 Một số vấn đề gặp phải 5
2.1.1 Hiện tượng phân mảnh bộ nhớ 5
2.2 Một số câu hỏi đặc biệt 6
Phần 3: Các vấn đề lý thú nữa liên quan đến C/C++ 6
3.1 Dấu ngoặc chết tróc 6
3.2 Các phép toán phối hợp loạn với nhau 7
3.3 Vấn đề bộ nhớ không an toàn (Memory Unsafety) 8
Phần 4: Các vấn đề lý thú liên quan đến kỹ thuật viết code hiệu quả 8
4.1 Đừng cố gắng tối ưu code từ lúc đầu 8
4.2 Trang bị nhiều kinh nghiệm tự tối ưu hóa code 9
4.3 Đặc tính của CPU cũng giúp mang lại hiệu quả đáng kể 9
4.4 Đảm bảo sức khỏe để có thể viết code chất lượng 10
Tài liệu tham khảo 14
Trang 3Giới thiệu
Trong suốt 1 kỳ học vừa qua, mặc dù gặp phải khó khăn do dịch bệnh khiến cho việc học tập phải thay đổi liên tục, gặp bất tiện trong việc gặp thầy và các bạn để trao đổi môn học Nhưng với sự hỗ trợ của công nghệ thông tin, thầy và các bạn em cảm thấy kỳ vừa rồi em vẫn tiếp tục thu nhận được rất nhiều kiến thức bổ ích mà sau đây em sẽ viết lạitrong bài báo cáo của mình
Trang 4Phầần 1: Các vầấn đềầ lý thú liền quan đềấn con trỏ
1.1 Một số vấn đề gặp phảiNếu em tiếp tục thao tác kiểu lấy giá trị chứa trong , như là , thì chương trình của emp*p
sẽ access vào 0x0000ffff để lấy giá trị Có 2 trường hợp xảy ra:
1) Nếu vùng nhớ 0x0000ffff chưa được thằng nào dùng (không có chương trình nào trong cùng máy tính dùng), thì sẽ lấy đc 1 giá trị ngẫu nhiên, lung tung nằm trong vùng nhớ đó (sai logic)
2) Nếu vùng nhớ 0x0000ffff được 1 thằng khác dùng và chương trình của em không được quyền cao hơn (ví dụ quyền admin), thì sẽ bị crash ngay lập tức
Đây cũng là lý do vì sao có những lúc em truy cập vào 1 mảng không đc cấp phát hoặcvào index quá độ dài của mảng, nhưng có lúc thì bị crash và có lúc thì không
1.1.2 Sử dụng dereferencer con trỏ làm tốn thời gian
Thao tác gán địa chỉ vùng nhớ dữ liệu cho một con trỏ dereference tốn nhiều thời gian vàcó thể gây hậu quả nghiêm trọng nếu vùng nhớ đích chưa được cấp phát.
1.2 Một số câu hỏi đặc biệt
Sau 1 kỳ học môn Kỹ thuật Lập trình trên lớp, em có tổng kết lại một vài thắc mắc về contrỏ Nhờ có thầy, các bạn giúp đỡ cùng các nguồn tài liệu trên mạng, em đã có câu trả lời như sau.
Câu hỏi: Không thể gán 2 mảng nhưng có thể gán 2 biến cấu trúc, dù các trường của cấu
trúc có thể là 1 mảng, xâu hay 1 struct khác Nhưng nếu 1 trường của cấu trúc là con trỏ thì sao?
Trả lời: Khi trong struct có con trỏ thì không được gán trực tiếp Bản chất việc gán 2 struct là compiler sẽ copy toàn bộ vùng nhớ của struct này sang struct kia.
Nếu các biến trong struct là biến thì compiler copy giá trị của biến int này sang biến int
int kia.
Khi ta cấp phát vùng nhớ cho con trỏ thì dữ liệu được cấp phát không được copy Chỉ có địa chỉ mà con trỏ đang trỏ đến là được copy.
Trang 5Dẫn đến ta sẽ có hai struct khác nhau, nhưng con trỏ trong 2 struct đó lại cùng trỏ về một vùng nhớ Thao tác trên con trỏ của struct này sẽ thay đổi vùng nhớ của con trỏ trên struct khác Nếu ta không hiểu rõ điều này sẽ gây ra những lỗi khó hiểu và nguy hiểm
Phầần 2: Các vầấn đềầ lý thú liền quan đềấn cầấp phát b nh đ ngộớ ộ
2.1 Một số vấn đề gặp phải2.1.1 Hiện tượng phân mảnh bộ nhớ
Trong quá trình tìm hiểu em đã 1 vài lần gặp thuật ngữ "phân mảnh bộ nhớ" được sử dụng một vài lần trong trường hợp phân bổ bộ nhớ động C ++ Cùng với đó là một số câuhỏi về cách xử lý phân mảnh bộ nhớ, nhưng không thể tìm thấy câu hỏi trực tiếp liên quan đến vấn đề này Vì thế em muốn đề cập nó trong bài báo cáo này để thầy có thể xemqua và nếu sai mong thầy có thể phản hồi lại cho em
Các hiểu của em về phân mảnh bộ nhớ như sauNếu như em có một bộ nhớ trống "lớn" (32 byte):
Bây giờ, phân bổ một số trong đó (5 phân bổ):aaaabbccccccddeeee
Bây giờ giải phóng bốn phân bổ đầu tiên: eeee
Nếu ngay hiện tại phải phân bổ 16 byte nữa thì có vẻ hợp lý vì chúng to có gấp đôi số đó nhưng không thể do phân mảnh bộ nhớ
Theo em được biết trên các hệ thống có bộ nhớ ảo, sự phân mảnh ít gặp vấn đề hơn bởi vìphân bổ lớn chỉ cần được đặt liền kề trong không gian địa chỉ ảo , không phải trong không gian địa chỉ vật lý Vì vậy trong ví dụ của em, nếu tôi có bộ nhớ ảo với kích thướctrang là 2 byte thì tôi có thể thực hiện phân bổ 16 byte của mình mà không gặp vấn đề gì Bộ nhớ vật lý sẽ trông như thế này:
=> Để ngăn chặn sự phân mảnh bộ nhớ trong C ++ hoạt động bằng cách phân bổ các đối tượng từ các khu vực khác nhau theo kích thước Vì vậy, nếu bạn sẽ tạo ra nhiều đối tượng và giải phóng tất cả chúng sau này, hãy phân bổ chúng từ một nhóm bộ nhớ Bất kỳ
Trang 6phân bổ nào khác mà bạn thực hiện ở giữa chúng sẽ không nằm trong nhóm, do đó sẽ không được đặt ở giữa chúng trong bộ nhớ, do đó, bộ nhớ sẽ không bị phân mảnh.2.2 Một số câu hỏi đặc biệt
Câu hỏi: C++ chỉ có new và delete để cập nhập và giải phóng mảng, nếu muốn tái cập
nhập lại bộ nhớ cho các mảng n chiều mà vẫn muốn giữ lại các giá trị đã có của mảng cũ thì làm thế nào ?
Trả lời: Vì khi sử dụng toán tử delete không có nghĩa là delete tất cả mọi thứ bên trong vùng nhớ mà con trỏ trỏ đến Toán tử new và delete chỉ mang ý nghĩa về "quyền sử dụng" vùng nhớ Toàn bộ dãy địa chỉ trên bộ nhớ ảo được quản lý bởi một chương trình mang tên "Hệ điều hành", và hệ điều hành có quyền trao lại quyền sử dụng một vùng nhớnào đó (trên Stack hoặc trên Heap ) cho những chương trình đáng tin cậy trên máy tính.Và toán tử new dùng để làm hợp đồng sử dụng vùng nhớ trên Heap, nếu lấy vùng nhớ được cấp phát thông qua hợp đồng (make by new operator) để chương trình chạy, vậy khi sử dụng toán tử delete, đơn giản là đưa lại cho hệ điều hành Lúc này, Giá trị trên vùng nhớ đó có thể vẫn còn giữ nguyên do chưa có chương trình nào can thiệp vào.
Toán tử delete không tác động gì đến con trỏ.
Tuy nhiên vì là có thể, tức là nếu bị chương trình khác can thiệp vào trước khi chúng ta tái cập nhập bộ nhớ thì các giả trị của mảng cũ chắc chắn không còn Trong trường hợp may mắn chưa có chương trình nào can thiệp, chúng ta có thể lấy lại các giá trị đã có của mảng cũ bằng cách dùng hàm realloc
Phầần 3: Các vầấn đềầ lý thú n a liền quan đềấn C/C++ữ
3.1 Dấu ngoặc chết trócTrong ngôn ngữ C/C++, có một cái bảng gọi là…thứ tự ưu tiên của các phép toán Mà đa số lập trình viên C/C++ đều không thể nhớ nó, nhưng em nghe các anh chị khóa trên kể lại những người tuyển dụng rất thích hỏi về vấn đề này
Mặc dù gặp rất nhiều trong quá trình code nhưng trong thời gian làm bài báo cáo này tần xuất code của em đã giảm xuống đáng kể vì ôn nhiều môn khác Nên em sẽ lấy một ví dụ mà em đã gặp trên mạng:
#include <stdio.h>int main(){
int a = 10, b = 5, c = 5; int d;
d = b + c == a;
printf("%d", d); // Kết quả in ra 1 return 0;
}
Trang 7Kết quả in ra d = 1 Vì phép “ ” ưu tiên đầu tiên, nên+ b + c thực hiện trước cho ra 10
Sau đó phép “==” ưu tiên tiếp theo (10 == 10 đúng (true)) nên cuối cùng d = 1.Em còn đọc được có người comment dưới bài viết rằng “Khi đã trở thành developer, tuyệt đối không được thử trí thông minh của bản thân bằng những dòng code như thế này” Vì nó chỉ làm cho chúng ta và những người xung quan muốn trầm cảm hơn mà thôi.
3.2 Các phép toán phối hợp loạn với nhau
Tiếp đến em gặp rất nhiều crash liên quan đến các toán hạng và em đã phải mất nhiều thời gian để nhận ra nó Đó là lý do em muốn đề cập tới trong bài báo cáo này.
// Kết quả in ra trên màn hình là gì?#include <stdio.h>
int main(){
int a = 10, b = 10; if (a = 5) b ;
printf("%d, %d", a, b ); return 0;
// Kết quả in ra màn hình là gì#include <stdio.h>int main(){
int i = 10, j = 0; if (i || (j = i + 10)) printf("%d", j); return 0;}
Câu trả lời: 0
Lý do em và phần đông đều sai vì trong câu điều kiện , vế bên trái của phép “ ” là (màif||i
i ở đây đang = 10 nghĩa là true) True nó có OR với thằng nào thì cũng là true cả, nên vế bên phải sẽ không được thực hiện, và đương nhiên vẫn dữ giá trị j0
Trang 83.3 Vấn đề bộ nhớ không an toàn (Memory Unsafety)
Đây là một vấn đề em ít gặp phải Nhưng đọc các bài báo trên mạng em thấy các lỗ hổng bảo mật như Heartbleed, Zero-Day hay lỗ hổng bị khai thác bởi Ransomware như WannaCry đều có chung nguyên nhân gốc và thường gặp trong 2 ngôn ngữ lập trình phổ biến là C và C++ Một báo cáo từ Motherboard chỉ ra rằng vấn đề này xuất phát từ cái gọilà “memory unsafety” tồn tại trong trong C/C++.
Trong một chương trình có một danh sách 10 phần tử chứa các giá trị số (number) Về mặt lý thuyết, nếu trong chương trình mà truy cập vào phần tử thứ 11 thì chương trình phải hiển thị thông báo lỗi mới chuẩn Tuy nhiên trong các ngôn ngữ memory-unsafe nhưC và C++ thì chương trình sẽ tìm kiếm phần tử thứ 11 ở chỗ mà nó cho rằng phần tử đó tồn tại ở đó (mặc dù trên thực tế nó không tồn tại) và truy cập vào memory ở vị trí đó Vấn đề này được gọi là “buffer overflow” và bị khai thác trong các lỗ hổng như HeartBleed Và đó chưa phải là lỗ hổng duy nhất trong C/C++ Còn một số loại lỗ hổng “memory unsafety” khác trong C/C++ như sau:
+ Type confusion: nhầm lẫn giữa giá trị tồn tại trong bộ nhớ và kiểu dữ liệu của nó+ Use after free: truy cập bộ nhớ sau khi đã giải phóng nó
+ Use of uninitialized memory: truy cập và sử dụng bộ nhớ khi chưa khởi tạo giá trị cho nó
Các lỗ hổng này rất phổ biến trong các phần mềm được sử dụng rộng rãi như Firefox, Chrome, Windows, Android và iOS.
Phầần 4: Các vầấn đềầ lý thú liền quan đềấn kỹỹ thu t viềất code hi u quậệả
4.1 Đừng cố gắng tối ưu code từ lúc đầuViệc thực hiện tối ưu hóa code của mình từ rất sớm đôi khi chúng cũng làm cho mọi việc trở nên phức tạp hơn khi lúc nào bạn cũng phải nghĩ cách tối ưu cho code của mình Thậm chí có thể gây ra một vài vấn đề (Được em trích lại trong slide của thầy Vũ Đức Vượng):
1) Không thể xác định được những nút thắt trong chương trình trước khi chạy thử toàn bộ chương trình
2) Việc xác định quá các nút thắt trong chương trình sẽ gây ra các nút thắt mới khi chạy thử toàn bộ chương trình
Trang 93) Nếu vừa viết chương trình vừa tìm các tối ưu mã nguồn, có thể làm sai lệch mục tiêu của chương trình.
Hãy tập trung viết cho code hoạt động đúng trước, sau đó mới bắt đầu tối ưu những đoạn code.
4.2 Trang bị nhiều kinh nghiệm tự tối ưu hóa code Một vài kinh nghiệm mà em đã học được:
Viết lại các biểu thức logic cần đảm bảo các tiêu chuẩn sau:1) Không kiểm tra khi đã biết kết quả rồi
2) Sắp xếp thứ tự các phép kiểm tra theo tần xuất xảy ra kết quả đúng3) So sánh hiệu năng của các lệnh có cấu trúc tương đương4) Thay thế các biểu thức logic phức tạp bằng bảng tìm kiếm kết quảViết lại các vòng lặp hiệu quả:
Bằng các cách sau sẽ giúp vòng lặp hiệu quả hơn1) Ghép các vòng lặp với nhau
2) Giảm thiểu các phép tính toán bên trong vòng lặp nếu có thểViết lại các biểu thức và tinh chỉnh việc biến đổi dữ liệu
Còn rất nhiều các kinh nghiệm nữa mà chúng ta cần khám phá thêm nhưng bên cạnh đó hiểu thêm về phần cứng cũng giúp ích rất nhiều cho tối ưu hóa code như phần sau4.3 Đặc tính của CPU cũng giúp mang lại hiệu quả đáng kể
Để đảm bảo tốc độ truy xuất tối ưu, các bộ vi xử lý (CPU) 32-bit hiện nay yêu cầu dữ liệusắp xếp và tính toán trên bộ nhớ theo từng offset 4-byte Yêu cầu này gọi là memory alignment
Khi biên dịch một đối tượng dữ liệu có kích thước dưới 4- byte, các trình biên dịch sẽ bổsung thêm các byte trống để đảm bảo các dữ liệu được sắp xếp theo đúng quy luật Việc bổ sung này có thể làm tăng đáng kể kích thước dữ liệu, đặc biệt đối với các cấu trúc dữ liệu như structure, class…
Theo nguyên tắc alignment 4-byte (hai biến “c” và “d” có kích thước 4 byte), các biến “a” và “b” chỉ chiếm 1 byte và sau các biến này là biến int chiếm 4 byte, do đó trình biên dịch sẽ bổ sung 3 byte cho mỗi biến này Kết quả tính kích thước của lớp Test sẽ là 16 byte.
class Test {
bool a;int c;int d;bool b; };
Ta có thể sắp xếp lại các biến thành viên của lớp Test như sau theo chiều giảm dần kích thước Khi đó, hai biến “a” và “b” chiếm 2 byte, trình biên dịch chỉ cần bổ sung thêm 2
Trang 10byte sau biến “b” để đảm bảo tính sắp xếp 4-byte Kết quả tính kích thước sau khi sắp xếplại class Test sẽ là 12 byte.
class Test {
int c;int d;bool a;bool b;};
4.4 Đảm bảo sức khỏe để có thể viết code chất lượng
Theo như em biết việc ngồi nhiều giờ trước màn hình máy tính để viết code sẽ gây ảnh hưởng rất xấu đến sức khỏe và điều này khiến cho hiệu quả công việc giảm đi, code cũng vì thế mà giảm đi phần nào sự đặc sắc.
4.3.1 Ngồi lâu trước máy tính ảnh hưởng đến thị lực:
Ngoài thời gian bắt buộc phải ngồi làm việc với máy tính, chúng ta không thể phủ nhận sức hấp dẫn của các sản phẩm công nghệ, đặc biệt là smartphone, laptop Việc ngồi sử dụng chúng trong hàng giờ đồng hồ ảnh hưởng nghiêm trọng đến thị lực đôi mắt, gây mỏimắt, khô mắt, giảm thị lực, cận thị…
Nhận thấy các vấn đề đó, các bác sĩ ở bệnh viện Mayo (Mỹ) đã đưa ra quy tắc 20-20-20 Cụ thể là cứ 20 phút thì bạn nên rời mắt khỏi màn hình máy tính, nhìn ra xa khoảng 20 feet (khoảng 6m) ít nhất trong vòng 20 giây Thi thoảng bạn có thể nhắm mắt lại trong vàiphút hoặc tập các bài tập cho mắt để mắt được nghỉ ngơi và sau đó có thể bắt đầu làm việc hiệu quả hơn Nếu mắt bị khô, bạn hãy chớp mắt thường xuyên hơn và có thể sử dụng một lọ thuốc nhỏ mắt bên mình.
4.3.2 Ngồi lâu trước máy tính khiến da nhanh lão hóa
Thực tế cho thấy rằng, những người thường xuyên sử dụng máy tính và các thiết bị điện tử có dấu hiệu lão hóa da nhanh hơn hẳn những người khác kể cả khi họ đã bổ sung các thực phẩm tươi, lành mạnh
Để khắc phục tình trạng này, bạn nên hạn chế ngồi máy tính nhiều nhất có thể, bảo vệ da đúng cách và cung cấp các dưỡng chất cần thiết cho da.
4.3.3 Ngồi lâu trước máy tính khiến đau mỏi vai và lưng
Ngồi nhiều sẽ tạo ra sự hao mòn trong khớp xương của bạn, ảnh hưởng lớn đến dây chằng cột sống khi phải đặt một trọng lực lớn trên cơ vai và lưng trong thời gian dài Ngoài ra, khi ở đằng trước là máy tính thì tự nhiên bạn sẽ phải vươn cổ về phía trước trong khi tập trung, gây căng thẳng trên cổ và vai.
Trang 11Theo nghiên cứu trên Tạp chí Nghiên cứu và Phát triển Phục hồi chức năng, bạn chỉ cần 25 phút tập thể dục nhịp điệu – như chạy bộ hoặc bơi lội – có thể làm giảm 28% cơn đau lưng của bạn.
4.3.4 Ngồi máy tính quá lâu và thường xuyên sẽ khiến trí tuệ sa sút
Trong một nghiên cứu từ 1,600 người trường thành và 65 người già, các nhà nghiên cứu tìm ra được những người có gen liên quan chặt chẽ với bệnh mất trí nhớ thì khả năng pháttriển bệnh gần như gấp đôi so với những người không mang gen bệnh Nhưng đối với những người không tập thể dục thường xuyên, các nhà nghiên cứu thấy rằng tỷ lệ phát triển chứng mất trí của 2 đối tượng là tương tự nhau.
Điều này liên quan trực tiếp đối với những người ngồi lâu trước máy tính Nhà tâm lý họcngười Mỹ – ông Michael Pietrus cho biết: “Có quá nhiều yếu tố kích thích của máy tính khiến chúng ta mất tập trung và làm việc không thực sự hiệu quả” Khi bạn để những kích thích này thường xuyên “dẫn lối”, bạn sẽ dễ dàng mắc chứng hay quên, đãng trí.Để tránh những rắc rối này, bạn hãy cố gắng xác định rõ mục tiêu công việc của mình, chẳng hạn như phải hoàn thành bản báo cáo hàng tuần, đưa ra một số nguyên tắc để hạn chế việc truy cập facebook, instagram, twitter,…
Bên cạnh đó, thay vì ngồi một thời gian dài uể oải rồi dán mắt vào phim ảnh hay mạng xãhội, bạn hãy chịu khó vận động sau 1-2 giờ ngồi trước máy tính Mục đích là để cơ thể thư giãn xương khớp, lưu thông khí huyết.
4.3.5 Ngồi lâu trước máy tính ảnh hưởng đến hệ hô hấp
Ngồi lâu trước máy tính là một dạng vận động tốn ít năng lượng nhất Khi bạn ngồi xuống, tư thế ngồi khiến khoang phổi không được mở rộng để thở, làm hạn chế lượng oxicần thiết vào cơ thể của bạn Vì vậy, trong thời gian này chúng ta thường có xu hướng thởnông
Song, vì hằng ngày chúng ta phải ngồi làm việc lâu trước máy tính nên tình trạng này kéodài dẫn đến khả năng hấp thu oxy của phổi bị giảm Điều này có nghĩa là khi nồng độ oxytrong máu thấp thì lượng máu cung cấp cho việc nuôi các cơ quan trong cơ thể cũng bị ảnh hưởng khiến bạn không thể hoạt động được tối ưu Theo thống kê của viện nghiên cứu khoa học Cali (Mỹ) thì ngồi lâu trước máy tính quá 5 giờ/ngày sẽ khiến tinh thần, sứcđề kháng bệnh tật, hoạt động của tim đều bị suy giảm tối thiểu là 10%.
Để khắc phục tình trạng này, thỉnh thoảng hít một hơi thật sâu Bên cạnh đó, nếu có thể nên tập yoga để học cách điều hòa khí thở của mình một cách tốt nhất Thường xuyên vậnđộng, tập thể dục đều đặn và có chế độ ăn uống, nghỉ ngơi hợp lý.
4.3.5 Ngồi nhiều ảnh hưởng đến hệ thống tim mạch