Chào bạn! Lại là tôi Xuân Quỳnh đây. Bạn vẫn háo hức về những kiến thức cực kỳ cơ bản trong loạt bài lập trình C cơ bản này chứ? :))
OK, bài trước các bạn đã làm quen với 1 lệnh cơ bản là printf, thuộc thư viện stdio.h. Rất hay dùng để in ra màn hình các đoạn text, các con số và nhiều thứ khác nữa. Tôi đã hứa là làm 1 hàm printf tương tự, nhưng nó thuần Việt 😀 Aha, tôi việt hóa hàm này cho bạn biết nha. Nhắc lại chương trình cũ của chúng ta như sau:
#include <stdio.h>
void main()
{
printf(“Chao em C xinh dep”);
}
OK, bây giờ chúng ta sẽ thay thế hàm printf ở trên bằng hàm, tôi đặt tên nó là inramanhinh(“In cái gì đó trong này”) :))
Bây giờ tôi giới thiệu khái niệm mới là thủ tục. Nếu bạn nào hồi cấp 3 mà cày Pascal thì biết thủ tục là như nào rồi. Tôi nhắc lại theo cách bình dân như sau:
Thủ tục là việc nhóm các câu lệnh lại để xử lý 1 việc nào đó theo mong muốn của lập trình viên. Ở đây là tôi nhóm việc in ra màn hình thành 1 thủ tục có tên như trên. Rồi, từ khóa cho việc dịnh nghĩa là void <Tên thủ tục>. Ở đây là:
void inramanhinh(char* caicaninra)
Bạn choáng ngợp chưa? Đừng sợ, tôi sẽ chỉ cho bạn hết sợ nha. Ở trên tôi có từ khóa void. void là gì vậy trời? Ồ đó là 1 từ khóa của C, quy định cho việc viết thủ tục. Sau đó là 1 cái tên, bạn muốn đặt như nào cũng được, nhưng nhớ cho tôi 1 số quy tắc đặt tên như sau:
- Tên thì không có dấu cách ở giữa. Chẳng hạn:
void in ra man hinh(char* caicaninra)
là sai! chứa dấu cách là em C nó không chơi đâu dấy, em ấy cần sự liên tục, anh em mà cứ đứt đoạn là em ấy chê yếu ngay :))
- Tên không được trùng với các từ khóa của C. Từ khóa của C thì nhiều lắm, tôi copy pase cho bạn đọc nè:
auto | double | int | struct |
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
continue | for | signed | void |
do | if | static | while |
default | goto | sizeof | volatile |
const | float | short | unsigned |
Đó, nhưng bạn thích đọc thì đọc, không thích thì thôi 😀 Lúc nào cần cái nào thì quay ra đọc thì học nó dễ mà đỡ nhớ nhiều nhức đầu nạ :3
Bạn đặt tên thủ tục thì tránh mấy cái từ này cho tôi là ok 😀
- Tên không được bắt đầu bằng ký tự đặc biệt ((, #..) mấy cái đó tránh cái nà.
Rồi còn nhiều quy tắc lắm nhưng mà bạn cũng không cần mất quá nhiều thời gian cho việc này. Bạn chỉ cần nhớ đặt tên theo cách của bạn, 1 cái tên của bạn và tên đó nên gợi nhớ cái thủ tục của bạn làm cái gì.
Nhắc lại hàm đang nghiên cứu:
void inramanhinh(char* caicaninra)
bên trong 2 cái dấu ngoặc là cái gì mà hoa bay bướm lượn vậy nhỉ? bạn kéo lên keyword và xem từ khóa char. Vậy char là gì? Tiếng anh tin học char là viết tắt của character: ký tự 😀
Là kiểu dữ liệu thôi chứ không có chi mô nà. Ngoài char ra tôi nói thêm:
int = integer = số nguyên
float = số thực
long: số nguyên nhưng phạm vi lớn hơn int
double: số thực nhưng phạm vi lớn hơn float. Nó được định nghĩa kiểu dữ liệu có mũ.(cái này là cái gì nghiên cứu sau nha=)))
Kiể dữ liệu là việc bạn chọn lựa để sử dụng trong chương trình. Ví dụ chương trình của bạn đếm đếm cây, đếm vịt gà, (đếm được) thì dùng kiểu nguyên là int. đếm nhiều quá thì cho hẳn cái long cho nó máu. Tuy nhiên cái gì cũng có giới hạn cả, máy tính cũng đếm được bao nhiêu đó rồi nó chịu 😀 Còn giửa sử giải phương trình bậc 2 thì bạn cần dùng kiểu float hoặc double, vì phương trình bậc 2 giải cho cả số thực mà. Tôi lấy ví dụ như vậy nhằm nhắc bạn 1 điều. Tùy thuộc vào bài toán mà chọn kiểu dữ liệu, chứ không phải cứ thích int thì cho int, thích float thì cho float đâu nha 😀 Nhớ nha, kỹ năng cơ bản đó. Không đến lúc sai kết quả lại bảo C ngâu :))
Vậy còn sau char tôi thêm 1 dấu * để làm cái gì rứa? Aha, nó còn được gọi là con trỏ. Con trỏ là cái gì thế? Ồ, con trỏ là con trỏ :)) Con trỏ không phải là con trỏ chuột bạn di di trên màn hình đâu nha. Hồi mới học C tôi cứ nghĩ thế mới tài :)) Con trỏ có gì hay vậy? Tôi vinh hạnh giới thiệu cho bạn, con trỏ là đặc quyền của C/C++ mà không một ngôn ngữ nào có được đặc quyền này. Cùng lắm sau này có thằng objective-c được phát triển từ C cũng có thôi.
Vậy con trỏ thì ta nên hiểu như nào cho đúng?
Ở đây nếu tôi không dùng dấu * thì nó hiểu đó là kiểu char bình thường như cân đường hộp sữa. Bạn gặp * bạn hiểu đó là kiểu dữ liệu con trỏ. Con trỏ hơn ở biến thông thường điểm nào, tôi sẽ làm rõ như sau:
Giả sử bạn cần sử dụng biến để nhập ký tự, bạn dùng char. 1 ký tự bạn khai báo như sau:
char < tên biến >
Ví dụ:
char a;
tên biến nó cũng có quy tắc đặt tên, nhưng bạn nên đặt tên gợi nhớ với cái bạn cần. Bạn có thể đặt tên:
char conlon;
char contrau;
char hihi;
thoải mái, dài bao nhiêu cũng được, nhưng mấy cái tên cần nhắc nhớ cho bạn nó để làm gì. Đằng sau bạn thêm cho tôi 1 dấu ; nhằm mục đích kết thúc quá trình đặt tên biến.
Vì nó là lưu 1 ký tự cho nên bạn có thể gán giá trị cho biến này. Giả sử tôi có đoạn chương trình:
char c;
c = 'm';
Tôi khai báo 1 biến c. sau đó tôi gán biến c là ký tự m. Bạn để ký tự m trong 2 dấu nháy đơn nhé.
Bây giờ để in ra ký tự c này tôi viết:
printf("c = %c", c);
Chương trình đầy đủ:
#include <stdio.h>
void main()
{
char c;
c = 'm';
printf("c = %c", c);
}
Kết quả trên màn hình:
Đấy nó in ra c = m 😀
Bạn nào chưa biết gõ lệnh thì vui lòng xem bài 1 nha.
Bạn quay lại câu lệnh này tôi giải thích:
printf("c = %c", c);
%c là cái gì thế? %c là 1 cái nhãn, bạn hiểu nó sẽ thay thế ký tự vào đó. c = character. Nó sẽ đưa giá trị của biến c vào %c này, nên nó mới in ra như trên :)) em C em ấy quy định như vậy thì mình phải theo thôi =))
Ngoài %c ta còn có 1 số nhãn khác:
%d = để in ra số nguyên (d = double)
%f = để in ra số thực.(f = float)
Nếu bạn muốn in ra 2 số thập phân sau dấu phẩy thì thêm:
%2.2f: đằng trước bạn đặt bao nhiêu cũng được, đằng sau viết số 2 thì nó ra 2 số sau dấy phẩy. Tương tự %d cũng vậy nha :))
ví dụ: printf(“so thuc = %2.2f”, biensothuc);
%p = in ra địa chỉ con trỏ (p = pointer)
Ví dụ: printf(“dia chi cua p = %p”, pa);
=)) Bạn thấy mệt chưa? tôi khuyên bạn 1 câu như này, bạn đọc tới đâu trong bài của tôi thì bạn gõ code tới đó nhé 😀 học lập trình thì phải lập trình, như vậy mới nhớ dai được. Gõ lại mấy đoạn code đầy đủ của tôi bạn nhé. Đọc qua thì nó nhanh quên lắm. Ok, hít 1 hơi và tiếp tục.
Tôi quay lại với vấn đề con trỏ. Vậy là với biến thông thường, bạn khai báo theo kiểu dữ liệu bạn muốn + tên biến bạn nghĩ ra. Thì lúc đó, máy tính của bạn sẽ cấp cho bạn 1 vùng nhớ để lưu. Hoàn toàn tự động. Bạn chỉ thấy được biến và giá trị của nó thôi. Thế còn bây giờ, bạn muốn xem biến đó nằm ở đâu trong máy tính của bạn? Bạn dùng cách này. Bạn them dấu & trước biến của bạn. Ví dụ &a thì nó lấy địa chỉ của bạn. Chương trình thí dụ:
#include <stdio.h>
void main()
{
char c;
c = 'm';
printf("Dia chi cua c = %p", &c);// ban viet lien dau & nhe, thang wp ngau qua
Ở trên, bạn thay %c bằng %p để in ra địa chỉ. Bạn thêm dấu & trước c để lấy địa chỉ.
Kết quả như sau:
Bạn thấy gì không? kết quả là 0028FF2F :)) Trông hoa mắt chưa. Vừa số vừa chữ cơ :v
Chữ cái F trên nghĩa là gì? Nó đang in ra hệ 16 bạn nhé. Hệ 10 bạn học từ hồi cấp 1 là các số từ 0 tới 9. Lên cấp 2, cấp 3 bạn học thêm hệ 16 với các số từ 0 tới 9 và các chữ cái: A để mô tả 10, B = 11, C= 12, D = 13, E = 14, F = 15. 😀
Ai quan tâm tới cách đổi cơ số 10 sang cơ số 16 không? Rảnh tôi sẽ hướng dẫn, tạm thời bài này bạn hiểu địa chỉ đó cũng là 1 cái số, số to quá nên dùng cơ số 16 để in cho tiện.
Vậy ví dụ trên, bạn hiểu biến c lưu giá trị m và nằm tại địa chỉ 002FF2F. Trên máy bạn địa chỉ này chắc chắn không giống tôi đâu :))
Vậy, bạn hiểu à hóa ra biến mà mình sử dụng được máy tính cấp cho 1 cái địa chỉ, 1 ô nhớ, 1 con số nằm trên vùng nhớ máy tính. Và thường thì ta mới học lập trình không quan tâm mấy tới địa chỉ này. Tuy nhiên học sâu về C lại hay chơi với các địa chỉ này, nhảy qua địa chỉ này tới địa chỉ khác. Ví dụ:
#include < stdio.h >
void main()
{
char c1;
char c2;
char* pc;
c1 = 'm';
c2 = 'n';
printf("Dia chi cua c1 = %p \n", &c1);
printf("Dia chi cua c2 = %p \n", &c2);
pc = &c1;
printf("Dia chi cua pc = %p \n", pc);
pc = &c2;
printf("Dia chi cua pc = %p \n", pc);
}
Bạn vui lòng viết từng đoạn lệnh trên vào notepad của bạn và build theo hướng dẫn các bài trước. Nhớ là viết từng đoạn 1 cho tôi nhé :)) Bắt buộc đấy. Để các bạn nhớ dai hơn.
Ở trên tôi có thêm \n để xuống dòng in cho dễ đọc thôi nạ.
Kết quả như sau:
Rồi bây giờ ta phân tích từng đoạn 1:
[code language="cpp"] char c1; char c2; char* pc; [/code]
Tôi khai báo 2 biến thường và 1 biến con trỏ để lưu ký tự.
char c1;
char c2;
char* pc;
Tôi gán c1 cho ký tự m, c2 cho ký tự n.
c1 = 'm';
c2 = 'n';
Tôi in địa chỉ của từng thằng ra.
printf("Dia chi cua c1 = %p \n", &c1);
printf("Dia chi cua c2 = %p \n", &c2);
Tôi gán pc cho địa chỉ c1, rồi tôi in ra.
Tương tự tôi làm với c2. Bây giờ bạn kéo lên xem lại kết quả.
Rõ ràng bạn thấy thằng pc nó là con trỏ và nhảy qua lại giữa 2 địa chỉ biến thường c1 và c2. Vậy rõ ràng biến con trỏ rất chi là hiếu động, ổng nhảy tự do lung tung đi đâu cũng được. Bạn dùng câu lệnh sau để in ra giá trị của ông pc. Chương trình full thêm 2 dòng:
#include <stdio.h>
void main()
{
char c1;
char c2;
char* pc;
c1 = 'm';
c2 = 'n';
printf("Dia chi cua c1 = %p \n", &c1);
printf("Dia chi cua c2 = %p \n", &c2);
pc = & c1; //viet lien dau & va c1 nhe
printf("Dia chi cua pc = %p \n", pc);
printf("Gia tri cua pc = %c \n", *pc);
pc = & c2;
printf("Dia chi cua pc = %p \n", pc);
printf("Gia tri cua pc = %c \n", *pc);
}
Kết quả như sau:
Bạn xem kết quả và ngẫm xem nào. Ngẫm nào ngẫm nào…
Bạn thấy đó, bạn tưởng tượng các giá trị c1 c2 là 2 vùng nhớ khác nhau trong máy tính. Còn cái ông pc là 1 ông thích trỏ tới đâu cũng được. Ông trỏ vào đâu thì coi như ông có luôn biến thường đó, ổng muốn hấp diêm hay làm gì biến đó cũng được =))
Bạn đã thấy con trỏ mạnh mẽ chưa? Chưa đâu. Càng sau bạn mới thấy được cơ.
Tôi viết cũng hơi mệt rồi. Thôi ta kết thúc bài học luôn.
Quay lại với chương trình đầu tiên, như cái tiêu đề. Ta sẽ tiếp phần 2 vào bài sau nha các tình yêu 😀
2 thoughts on “Lập trình C – Bài 4: Tự tạo một hàm printf thay thế cho hàm printf mặc định trong thư viện stdio.h(phần 1)”