Tải bản đầy đủ (.pdf) (14 trang)

đề tài sưu tầm tìm hiểu về các vấn đề lý thú liên quan đến c kỹ thuật lập trình

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (667.5 KB, 14 trang )

<span class="text_page_counter">Trang 1</span><div class="page_container" data-page="1">

TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI

VIỆN CÔNG NGHỆ THÔNG TIN VÀ TRUYỀN THÔNG

<b> Giảng viên hướng dẫn: ThS.Vũ Đức Vượng</b>

Hà Nội, 06/2021

</div><span class="text_page_counter">Trang 2</span><div class="page_container" data-page="2">

1.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 tố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

<b>Tài liệu tham khảo...14</b>

</div><span class="text_page_counter">Trang 3</span><div class="page_container" data-page="3">

Giớ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.

</div><span class="text_page_counter">Trang 4</span><div class="page_container" data-page="4">

Phầần 1: Các vầấn đềầ lý thú liền quan đềấn con trỏ

1.1 Một số vấn đề gặp phải

Nế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 em<b>p*p</b>

sẽ access vào <b>0x0000ffff</b> để lấy giá trị. Có 2 trường hợp xảy ra:

<small>1)</small> Nếu vùng nhớ <b>0x0000ffff</b> 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)

<small>2)</small> Nếu vùng nhớ <b>0x0000ffff</b> đượ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.

<b>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 </b>

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.

</div><span class="text_page_counter">Trang 5</span><div class="page_container" data-page="5">

Dẫ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ải

2.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ỳ

</div><span class="text_page_counter">Trang 6</span><div class="page_container" data-page="6">

phâ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

<b>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 </b>

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 tố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à tố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 tố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ữ ngun do chưa có chương trình nào can thiệp vào.

Tố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óc

Trong 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:

<small>#include <stdio.h>int main(){</small>

<small> int a = 10, b = 5, c = 5; int d;</small>

<small> d = b + c == a;</small>

<small> printf("%d", d); // Kết quả in ra 1 return 0;</small>

<small>}</small>

</div><span class="text_page_counter">Trang 7</span><div class="page_container" data-page="7">

Kết quả in ra<b> d = 1</b>. Vì phép “ ” ưu tiên đầu tiên, nên<b>+ b + c</b> thực hiện trước cho ra . <b>10</b>

Sau đó phép “<b>==</b>” ưu tiên tiếp theo (<b>10 == 10 đúng (true)</b>) nên cuối cùng<b> d = 1</b>.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 tố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.

<small>// Kết quả in ra trên màn hình là gì?#include <stdio.h></small>

<small>int main(){</small>

<small> int a = 10, b = 10; if (a = 5) b--;</small>

<small> printf("%d, %d", a, b--); return 0;</small>

<small>// Kết quả in ra màn hình là gì#include <stdio.h>int main(){</small>

<small> int i = 10, j = 0; if (i || (j = i + 10)) printf("%d", j); return 0;}</small>

<b>Câu trả lời: 0</b>

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à<b>if||i</b>

i ở đây đang <b>= 10</b> nghĩa là <b>true</b>). True nó có <b>OR</b> 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ị . <b>j0</b>

</div><span class="text_page_counter">Trang 8</span><div class="page_container" data-page="8">

<b>3.3 Vấn đề bộ nhớ không an toàn (Memory Unsafety) </b>

Đâ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 đầu

Việ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ử tồn bộ chương trình

2) Việc xác định q 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ử tồn bộ chương trình

</div><span class="text_page_counter">Trang 9</span><div class="page_container" data-page="9">

3) 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 tố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 tố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

</div><span class="text_page_counter">Trang 10</span><div class="page_container" data-page="10">

byte 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:

Ngồ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.

</div><span class="text_page_counter">Trang 11</span><div class="page_container" data-page="11">

Theo 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ó q 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 hồ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 q 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

</div>

×