Professional Documents
Culture Documents
Mục lục
CHƯƠNG 1: TỔNG QUAN VỀ NGÔN NGỮ C
1.1 Giới thiệu về lịch sử phát triển, sự cần thiết phải học ngôn ngữ C hiện nay
1.1.1 Giới thiệu về lịch sử phát triển ngôn ngữ C …………………….…………..…05
1.1.2 Sự cần thiết phải học ngôn ngữ C hiện nay………………………....................05
1.2 Cách khởi động và thoát chương trình
1.2.1 Cài đặt turbo C++ 3.0 .……………………………………………………….. 06
1.2.2 Cách khởi động và thóat khỏi chương trình ………………………………......07
1.2.3 Các thao tác trên lập trình trên TC ………………………………………........07
1.3 Cách sử dụng sự trợ giúp từ help file về cú pháp câu lệnh, về cú pháp hàm ……09
CHƯƠNG 4 : HÀM
4.1 Khái niệm hàm, tại sao phải xây dựng và sử dụng hàm
4.1.1 Khái niệm lập trình cấu trúc .............................................................................55
4.1.2 Khái niệm hàm ................................................................................................56
4.1.3 Tại sao phải xây dựng và sử dụng hàm ...........................................................57
4.1.4 Lập trình có cấu trúc trong ngôn ngữ C ..........................................................57
4.2 Nguyên tắc xây dựng và phân biệt các tham số của hàm
4.2.1 Khai báo và xây dựng hàm .............................................................................58
4.2.2 Phân biệt các tham số hàm .............................................................................62
4.3 Truyền tham số
4.3.1 Truyền tham số với tham số hình thức là tham trị ...........................................65
4.3.2 Truyền tham số với tham số hình thức là tham biến .....................................66
4.3.3 Tham số có gía trị mặc định ............................................................................67
4.4 Các lệnh đơn nhằm kết thúc hàm và nhận giá trị trả về cho tên hàm
4.4.1 Các lệnh đơn ....................................................................................................68
4.4.2 Các lưu ý về kiểu trong hàm và giá trị trả về.................................................. 69
CHƯƠNG 5 : MẢNG
5.1 Trình bày khái niệm mảng trong C ...........................................................................71
5.2. Cú pháp khai báo mảng và cách gán giá trị cho mảng
5.2.1 Khai báo kiểu mảng và biến mảng ..................................................................72
5. 2.2 Cách tổ chức dữ liệu mảng trên máy và truy nhập các phần tử ......................73
5.2.3 Các cách gán giá trị cho mảng .........................................................................74
5.3. Mảng và tham số của hàm
5.3.1 Các thao tác xử lý trên mảng ..........................................................................79
5.3.2 Tham số của hàm .............................................................................................86
CHƯƠNG 6 : CHUỖI KÝ TỰ
6.1 Khái niệm chuỗi ký tự ................................................................................................91
6.2 Khai báo biến chuỗi ....................................................................................................92
6.2.1 Cú pháp khai báo ............................................................................................92
6.2.2 Cách tổ chức lưu trữ chuỗi ký tự trong mảng ..................................................92
6.2.3 Mảng các chuỗi ký tự (danh sách chuỗi ký tự )...............................................93
6.3 Biến chuỗi và hằng , nhập giá trị chuỗi cho chương trình ......................................93
6.3.1 Khai báo bình thường ......................................................................................93
6.3.2 Hằng chuỗi ......................................................................................................94
6.3.3 Nhập giá trị chuỗi cho chương trình ...............................................................94
6.4 Các hàm làm việc với chuỗi .......................................................................................95
6.4.1 Các thao tác nhập xuất chuỗi ..........................................................................95
6.4.2 Các lệnh xử lý chuỗi ký tự ..............................................................................97
Mục tiêu:
- Trình bày được lịch sử phát triển của ngôn ngữ C
- Trình bày được ngôn ngữ này có những ứng dụng thực tế như thế nào
- Trình bày cách khởi động, khởi động được và thoát khỏi chương trình.
- Sử dụng được hệ thống trợ giúp từ help file
1.1 GIỚI THIỆU VỀ LỊCH SỬ PHÁT TRIỂN, SỰ CẦN THIẾT PHẢI HỌC NGÔN NGỮ
C HIỆN NAY
Ngôn ngữ C do Dennis sáng tác năm 1972 tại phòng thí nghiệm Bell Telephone với
mục đích tương đối hạn chế: Tạo ngôn ngữ để viết hệ điều hành UNIX. Song nhờ các tính
ưu việt và tính mềm dẻo nên nó đã được giới tin học nhanh chóng chấp nhận như là một
ngôn ngữ chính thống nhà nghề.
Đến năm 1978, bản in đầu tiên về ngôn ngữ C đã được in thành sách “The C
programming language” do Kernighan và Ritchie viết. C được Viện tiêu chuẩn hóa của Mĩ
làm thành tiêu chuẩn với tên gọi là “ANSI C” vào năm 1983. Tổ chức tiêu chuẩn hóa quốc
tế ISO xây dựng chuẩn C.
+ C là ngôn ngữ mạnh và mềm dẻo. Điều hạn chế đối với C chính là óc tưởng tượng của
bạn. Bản thân C không đặt điều kiện ràng buộc cho bạn. C sử dụng để viết hệ điều hành,
các trình điều khiển, soạn thảo văn bản, đồ họa, bảng tính…và thậm chí là các chương
trình dịch cho các ngôn ngữ khác.
+ C được các nhà tin học chuyên nghiệp dùng phổ biến, nhất là trong việc viết phần mềm
hệ thống. Một trong các lý do này là tính hiệu quả của chương trình dịch ra, nó có thể đạt
được 80% tính năng của chương trình đó viết bằng mã máy.
+ C là ngôn ngữ có thể chuyển dịch(portable) hay còn gọi là dễ thích nghi. Tính thích
nghi hay tính di chuyển được hiểu là một chương trình C viết ra cho máy IBM có thể đem
sang hệ thống máy khác như VAX của công ty Digital cho dịch lại vẫn chạy bình thường
sau một vài chỉnh sửa nhỏ.
+ C là một ngôn ngữ ít có từ khóa, là các từ dùng riêng cho ngôn ngữ khi viết chương
trình.
+ C là ngôn ngữ có cấu trúc modun. Đó chính là việc sử dụng các chương trình con loại
hàm (function). Các hàm sử dụng nhiều lần trong chương trình.
Lập trình căn bản (C) 5
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
Để lập trình được bằng ngôn ngữ C chúng ta phải sử dụng một chương trình, đó là
Turbo C++, qua khá nhiều phiên bản nhưng thông dụng hiện nay đó là phiên bản Turbo
C++ 3.0 hoặc Turbo C++ 4.5
Đầu tiên chúng ta phải có file nguồn cài đặt của Turbo C++3.0 đó là TC30( dung
lượng 4.03Mb)
+ Bước 1: Mở TC30 ra trong phần TC30 có file INSTALL.EXE ta kích đúp vào nó
để chạy file cài đặt.
+ Bước 2: Một cửa sổ mở ra như hình 1.1 ta ấn Enter để tiếp tục.
Thông thường trong phần này ta chọn đường dẫn để chứa TC trong phần Directories
rồi bỏ qua bước phần Options và chọn Start Installation
Bước 5: Quá trình cài đặt đã hoàn thành, ấn ESC hai lần để thoát khỏi chương trình.
Sau khi cài đặt xong thì ta thấy một thư mục TC xuất hiện trên ổ đĩa mà ta đã chọn
trong phần Directories của Bước 4 trong cài đặt. Mở thư mục TC rồi vào thư mục BIN,
trong thư mục BIN có một file TC đây chính là file chạy của chương trình, bạn chạy tập tin
tc.exe đó lên để chạy chương trình. Bạn có thể tạo Shortcut ra destop để lần sau chạy
chương trình cho nhanh. Lưu ý là khi Send To Desktop ta phải chọn file TC chứ không
nhầm lẫn sang shortcut TC có trong thư mục BIN.
- Khởi động: Kích vào biểu tượng TC của chương trình chọn Open .
- Thoát khỏi chương trình: Vào File chọn Quit hoặc ấn tổ hớp phím Alt + X.
Màn hình giao diện C như sau:
Thông thường sau khi khởi động trên màn hình sẽ có một cửa sổ màu xanh và chúng ta
viết các lệnh vào cửa sổ này, sau đây là bảng giới thiệu một số thao tác cơ bản khi viết
chương trình .
Sau đây là bảng một số lỗi hay gặp khi lập trình bằng Turbo C++
Thông báo tiếng Anh Ý nghĩa tiếng Việt
Undefined symbol ‘…’ chưa khai báo trong nháy
Unable to open include file Không mở được tệp thư viện, có thể viết sai tên thư
‘…’ viện hoặc đặt thư mục chưa đúng
Undefine symbol _main in Chưa viết chương trình chính hoặc chương trình
module c0.asm chính bị sai
Compound statement missing Thiếu dấu đóng ngoặc của khối lệnh
}
Unexpected } Thừa dấu đóng ngoặc của khối lệnh
Unterminated string or Chưa có dấu nháy kép kết thúc viết hằng chuỗi ký tự
character constant
Statement missing ; Thiếu dấu chấm phẩy kết thúc câu lệnh
Function call missing ) Thiếu dấu đóng ngoặc khi viết lệnh
1.3 CÁCH SỬ DỤNG SỰ TRỢ GIÚP TỪ HELP FILE VỀ CÚ PHÁP CÂU LỆNH, VỀ
CÚ PHÁP HÀM
Từ cửa sổ của chương trình chúng ta chọn Help, rồi chọn phần Index nó sẽ hiện ra một
bảng danh sách các từ khóa. Khi chúng ta muốn tìm hiểu về cấu trúc câu lệnh nào đó hay
hàm ta gõ vào đó thì sẽ có hướng dẫn chi tiết về câu lệnh, về hàm đó theo bên dưới đó là
các chương trình mẫu minh họa cho các câu lệnh, các hàm đó.
Hình 1.5 : Màn hình hiện thi chi tiết một lệnh được trợ giúp
Ta có thể dùng help file để tra cứu cấu trúc của các hàm như các hàm xử lý với xâu
ký tự, cách làm hoàn toàn giống như khi ta làm với câu lệnh if. Trong cửa sổ hiện ra sẽ cho
chúng ta cách sử dụng và khai báo thư viện cần cho việc xử lý hàm đó(Thư viện là nơi
chứa các hàm chuẩn của chương trình mà chúng ta sẽ được học trong các chương tiếp
theo).
Mọi ngôn ngữ lập trình đều được xây dựng từ một bộ ký tự nào đó. Các ký tự được
nhóm lại theo nhiều cách khác nhau để tạo nên các từ. Các từ lại được liên kết với nhau
theo một qui tắc để tạo nên các câu lệnh. Một chương trình bao gồm nhiều câu lệnh và thể
hiện một thuật toán để giải một bài toán nào đó. Ngôn ngữ C được xây dựng trên bộ ký tự
sau :
26 chữ cái hoa : A B C .. Z
26 chữ cái thường : a b c .. z
10 chữ số : 0 1 2 .. 9
Các ký hiệu toán học : + - * / = ( ) Ký tự gạch nối : _
Các ký tự khác : . , : ; [ ] {} ! \ & % # $ ...
Dấu cách (space) dùng để tách các từ. Ví dụ chữ V N có 3 ký tự, còn VN chỉ có 2 ký tự.
Chú ý :
Khi viết chương trình, ta không được sử dụng bất kỳ ký tự nào khác ngoài các ký tự
trên. Ví dụ như khi lập chương trình giải phương trình bậc hai ax2 +bx+c=0, ta cần tính biệt
thức Delta = b2 - 4ac, trong ngôn ngữ C không cho phép dùng ký tự , vì vậy ta phải
dùng ký hiệu khác để thay thế ví dụ delta= b2 - 4ac.
2.1.5 TỪ KHOÁ
Từ khoá là những từ được sử dụng để khai báo các kiểu dữ liệu, để viết các toán tử
và các câu lệnh. Bảng dưới đây liệt kê các từ khoá của TURBO C:
Asm break Case cdecl
Char const continue default
Do double else enum
Extern far float for
2.1.6 TÊN
Tên là một khái niệm rất quan trọng, nó dùng để xác định các đại lượng khác nhau
trong một chương trình. Chúng ta có tên hằng, tên biến, tên mảng, tên hàm, tên con trỏ, tên
tệp, tên cấu trúc, tên nhãn,...
Tên được đặt theo qui tắc sau:
Tên là một dãy các ký tự bao gồm chữ cái, số và gạch nối. Ký tự đầu tiên của tên
phải là chữ hoặc gạch nối. Tên không được trùng với khoá, không nên trùng với các tên
chuẩn. Độ dài cực đại của tên theo mặc định là 32 và có thể được đặt lại là một trong các
giá trị từ 1 tới 32 nhờ chức năng: Option-Compiler-Source-Identifier length khi dùng
TURBO C.
Ví dụ Các tên đúng: a_1 delta x1 _step GAMA
Các tên sai:
3MN Ký tự đầu tiên là số
m#2 Sử dụng ký tự #
f(x) Sử dụng các dấu ( )
do Trùng với từ khoá
te ta Sử dụng dấu trắng
Y-3 Sử dụng dấu -
Tên có thể do ngôn ngữ C sinh ra nhằm hai mục đích: thứ nhất là định danh các
thành phần có sẵn; thứ hai là viết các lệnh chương trình. Tuy nhiên tên có thể do người lập
trình tự đặt để định danh các thành phần của bản thân người lập trình tạo ra trong chương
trình.
Tên chuẩn là những tên do ngôn ngữ C đặt ra để định danh các thành phần.
Lập trình căn bản (C) 11
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
Chúng ta có thể định nghĩa dữ liệu (DATA) là tất cả những gì được máy tính xử lý,
dữ liệu như là nguyên vật liệu cho máy tính xử lý.
Các kiểu dữ liệu cần tới máy tính xử lý rất nhiều, tồn tại dưới nhiều dạng khác nhau
về bản chất, về ý nghĩa, không cứ gì là số liệu ( số liệu hiểu theo một nghĩa hẹp nào đó là
dữ liệu bằng số ) mà còn các ký tự, các mệnh đề logic… thể hiện qua các đối tượng cụ thể
cần xử lý như tiền lương, địa chỉ, tên, tuổi, văn bản, tín hiệu … Song nếu xét về phương
diện điện tử thì máy tính chỉ hiểu các thông tin biểu diễn dưới dạng mã nhị phân (tức là
bằng dãy các số nhị phân 0 và 1: bit, viết tắt của Binary digits ). Về phương diện ngôn ngữ
bậc cao thì dữ liệu đã được khái quát hóa thành các kiểu dữ liệu và ta không cần quan tâm
đến biểu diễn chi tiết trong máy tính của các kiểu dữ liệu.
Hình 2.1 Tổng quan phân loại các kiểu dữ liệu
Các kiểu số nguyên
Kiểu cơ sở Các kiểu số thực
Kiểu ký tự
Kiểu vô hướng Kiểu logic Boolean
Kiểu (kiểu đơn giản )
kê (Scalar Type) kiểu vô hướng do người
dữ lập trình định nghĩa Kiểu dữ liệu liệt
liệu ( enumerated)
Một kiểu dữ liệu (DATA TYPE) được định nghĩa với 2 điểm chính là:
- Một tập hợp các giá trị mà một biến thuộc kiểu đó có thể nhận được;
- Trên đó xác định một phép toán.
Dữ liệu chứa trong bộ nhớ máy tính với một số lượng ô nhớ nhất định, tính theo đơn vị
là byte. Thí dụ số nguyên loại int được chứa trong 2 byte bộ nhớ.
Ví dụ sau minh hoạ sự khác nhau giữa hai kiểu dữ liệu trên: Xét đoạn chương trình sau:
char ch1;
unsigned char ch2;
......
ch1=200; ch2=200;
Lập trình căn bản (C) 13
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
Hằng là một đại lượng không thay đổi giá trị trong toàn bộ chương trình.
Hằng có thể là số nguyên, số thực, ký tự hoặc chuỗi ký tự.
Hằng xâu ký tự là một dãy ký tự bất kỳ đặt trong hai dấu nháy kép.
Ví dụ: #define xau1 "Ha noi"
#define xau2 "My name is Hanh"
Xâu ký tự được lưu trữ trong máy dưới dạng một bảng có các phần tử là các ký tự
riêng biệt. Trình biên dịch tự động thêm ký tự null \0 vào cuối mỗi xâu ( ký tự \0 được xem
là dấu hiệu kết thúc của một xâu ký tự ).
Chú ý:
Cần phân biệt hai hằng 'a' và "a". 'a' là hằng ký tự được lưu trữ trong 1 byte, còn
"a" là hằng xâu ký tự được lưu trữ trong 1 mảng hai phần tử : phần tử thứ nhất chứa chữ a
còn phần tử thứ hai chứa \0.
Trong C cho phép sử dụng ba loại dữ liệu dấu phảy động, đó là float, double và long
double. Kích cỡ và phạm vi biểu diễn của chúng được chỉ ra trong bảng dưới đây:
Kiểu Phạm vi biểu diễn Số chữ số có nghĩa Kích thước
Float 3.4E-38 đến 3.4E+38 7 đến 8 4 byte
Double 1.7E-308 đến 1.7E+308 15 đến 16 8 byte
long double 3.4E-4932 đến 1.1E4932 17 đến 18 10 byte
Giải thích :
Máy tính có thể lưu trữ được các số kiểu float có giá trị tuyệt đối từ 3.4E-38 đến
3.4E+38. Các số có giá trị tuyệt đối nhỏ hơn 3.4E-38 được xem bằng 0. Phạm vi biểu diễn
của số double được hiểu theo nghĩa tương tự.
Dạng viết có phần mũ hay còn gọi dạng viết khoa học. Gồm hai phần: phần định vị và
phần mũ viết sau chữ E để biểu diễn số mũ của cơ số 10
Ví dụ:
623.12345 = 6.2312345* 10 2
Sẽ được viết lại cho máy tính là:
6.2312345E+02
Do giá trị số thực có thể biểu diễn dưới dạng có dấu chấm di động được, nên người
ta còn gọi đây là cách biểu diễn dấu chấm động (floating point ) để phân biệt với cách biểu
diễn số dưới dạng dấu chấm tĩnh là cách biểu diễn trong đó vị trí dấu chấm là cố định. Đôi
lúc ta gọi theo tiếng Việt là số dấu chấm phẩy động.
Trong ngôn ngữ C không có kiểu logic cụ thể mà sẽ xem xét số nguyên như kiểu
logic với giá trị 0 tương ứng với sai, giá trị 1 hoặc khác 0 tương ứng với đúng.
Hằng int là số nguyên có giá trị trong khoảng từ -32768 đến 32767.
Ví dụ:
#define number1 -50 Định nghiã hằng int number1 có giá trị là -50
#define sodem 2732 Định nghiã hằng int sodem có giá trị là 2732
Chú ý:
Cần phân biệt hai hằng 5056 và 5056.0: ở đây 5056 là số nguyên còn 5056.0 là hằng
thực.
Hằng long là số nguyên có giá trị trong khoảng từ -2147483648 đến 2147483647.
Hằng long được viết theo cách: 1234L hoặc 1234l (thêm L hoặc l vào đuôi )
Một số nguyên vượt ra ngoài miền xác định của int cũng được xem là long.
Ví dụ:
#define sl 8865056L Định nghiã hằng long sl có giá trị là 8865056
#define sl 8865056 Định nghiã hằng long sl có giá trị là 8865056
Biến nhớ (varible) là một đại lượng có thể thay đổi giá trị trong chương trình. Biến
nhớ được sử dụng để chứa dữ liệu của chương trình, do vậy nó rất quan trọng trong lập
trình, nếu không có biến nhớ thì chúng ta không thể lập trình được bởi vì không có chỗ nào
chứa dữ liệu cho việc tính toán và xử lý.
Biến có nhiều loại: biến toàn cục, biến cục bộ hay gọi là biến địa phương, biến thanh
ghi, biến động. Các loại biến này ta sẽ được tìm hiểu kĩ trong các chương sau.
Mỗi biến nhớ sẽ được quy định một kiểu dữ liệu, với quy định này biến nhớ chỉ có
thể chứa được các dữ liệu kiểu đó mà thôi.
Mỗi biến cần phải được khai báo trước khi đưa vào sử dụng. Việc khai báo biến
được thực hiện theo mẫu sau:
Ví dụ 2.4:
int a,b,c; Khai báo ba biến int là a,b,c
Long dai,mn; Khai báo hai biến long là dai và mn
Char kt1,kt2; Khai báo hai biến ký tự là kt1 và kt2
float x,y; Khai báo hai biến float là x và y
double canh1, canh2; Khai báo hai biến double là canh1 và canh2
Biến kiểu int chỉ nhận được các giá trị kiểu int. Các biến khác cũng có ý nghĩa tương
tự. Các biến kiểu char chỉ chứa được một ký tự. Để lưu trữ được một xâu ký tự cần sử dụng
một mảng kiểu char.
Các khai báo cần phải được đặt ngay sau dấu { đầu tiên của thân hàm và cần đứng
trước mọi câu lệnh khác. Sau đây là một ví dụ về khai báo biến sai: ( Khái niệm về hàm và
cấu trúc chương trình sẽ nghiên cứu sau này)
Ví dụ
void main()
{
int a,b,c;
a=2;
int d; /* Vị trí của khai báo sai */
.....
}
Nếu trong khai báo ngay sau tên biến ta đặt dấu = và một giá trị nào đó thì đây chính
là cách vừa khai báo vừa khởi đầu cho biến( hay còn gọi là khởi tạo giá trị ban đầu).
Mỗi biến được cấp phát một vùng nhớ gồm một số byte liên tiếp. Số hiệu của byte
đầu chính là địa chỉ của biến. Địa chỉ của biến sẽ được sử dụng trong một số hàm ta sẽ
nghiên cứu sau này (ví dụ như hàm scanf ).
Để lấy địa chỉ của một biến ta sử dụng phép toán:
Ví dụ 3:
a=5*3/2.5-4 ; sẽ yêu cầu máy tính toán vế phải và đưa kết quả sang vế trái là a
clrscr() ; sẽ yêu cầu máy xóa màn hình.
Trong C, ta có hai khái niệm về biểu thức:
Biểu thức gán.
Biểu thức điều kiện .
Biểu thức được phân loại theo kiểu giá trị: nguyên và thực. Trong các mệnh đề
logic, biểu thức được phân thành đúng ( giá trị khác 0 ) và sai ( giá trị bằng 0 ).
Biểu thức thường được dùng trong:
Vế phải của câu lệnh gán.
Làm tham số thực sự của hàm.
Làm chỉ số.
Trong các toán tử của các cấu trúc điều khiển.
Toán hạng gồm: hằng, biến, phần tử mảng và hàm. Phép toán ta như cộng, trừ, nhân
chia, phép gán, quan hệ, logic, phép tăng giảm.
* Các phép toán số học: Trong C gọi các phép toán là các toán tử operator.
Các phép toán hai ngôi số học, tức là tác động lên 2 dữ liệu để cho ra kết quả tương
ứng. Cách viết như sau :
vế_trái ký_hiệu_phép_toán vế_phải ;
Phép toán ý nghiã Ví dụ
+ Phép cộng a+b
- Phép trừ a-b
* Phép nhân a*b
/ Phép chia a/b ( Chia số nguyên sẽ chặt phần thập phân )
% Phép lấy phần dư a%b ( Cho phần dư của phép chia a cho b )
Có phép toán một ngôi - ví du -(a+b) sẽ đảo giá trị của phép cộng (a+b).
+ Dữ liệu tác động: kiểu số nguyên hoặc số thực.
+ Kết quả: Các phép toán số học cho kết quả là dữ liệu kiểu số.
Ví dụ:
11/3=3
11%3=2
-(2+6)=-8
Chú ý:
Phép toán quan hệ và logic cho ta giá trị đúng (1) hoặc giá trị sai (0). Nói cách khác,
khi các điều kiện nêu ra là đúng thì ta nhận được giá trị 1, trái lại ta nhận giá trị 0.
Các phép toán quan hệ là:
Phép toán ý nghiã Ví dụ
> So sánh lớn hơn a>b
4>5 có giá trị 0
>= So sánh lớn hơn hoặc bằng a>=b
6>=2 có giá trị 1
< So sánh nhỏ hơn a<b
6<=7 có giá trị 1
<= So sánh nhỏ hơn hoặc bằng a<=b
8<=5 có giá trị 0
== So sánh bằng nhau a==b
6==6 có giá trị 1
!= So sánh khác nhau a!=b
9!=9 có giá trị 0
+ Dữ liệu tác động: là các dữ liệu kiểu chữ hoặc kiểu số, nếu là chữ thì máy sẽ so
sánh mã ASCII của các chữ đó.
+ Kết quả: phép toán quan hệ cho kết quả là logic (đúng hoặc sai ) hay tạm gọi là 0
hoặc 1
Bốn phép toán đầu có cùng số ưu tiên, hai phép sau có cùng số thứ tự ưu tiên nhưng
thấp hơn số thứ tự của bốn phép đầu.
Các phép toán quan hệ có số thứ tự ưu tiên thấp hơn so với các phép toán số học,
cho nên biểu thức:
i<n-1 được hiểu là i<(n-1).
- Ký hiệu: &&,||,!
- Ý nghĩa: và, hoặc, phủ định
- Dữ liệu tác động: là các kiểu dữ liệu có giá trị đúng hoặc sai (kiểu logic)
- Kết quả: phép toán nối logic cho kết quả là logic (đúng hoặc sai)
E1 E2 E1&&E2 E | | E2 !E1
Đúng Đúng Đúng Đúng Sai
Đúng Sai Sai Đúng Sai
Sai Đúng Sai Đúng Đúng
Sai Sai Sai Sai Đúng
Chú ý: Phép và (&&) cho kết quả đúng khi và chỉ khi 2 toán hạng đều đúng, phép
hoặc (||) cho kết quả sai khi và chỉ khi 2 toán hạng đều sai, phép phủ định (!) cho kết quả
ngược lại.
Ví dụ:
3&&5 sẽ cho kết quả đúng vì máy hiểu 3 và 5 là giá trị đúng (khác 0)
6||0 sẽ cho kết quả đúng
!0 sẽ cho kết quả đúng vì máy hiểu sô 0 là giá trị sai
! ( 4>5) sẽ cho kết quả đúng
Các phép quan hệ có số ưu tiên nhỏ hơn so với ! nhưng lớn hơn so với && và ||, vì
vậy biểu thức như:
b)&&(c>d)
có thể viết lại thành:
&&c>d
Chú ý: Cả a và b có thể là nguyên hoặc thực.
Ngoài các phép toán cơ bản trên, trong ngôn ngữ C cung cấp một số phép toán mở rộng
giúp cho người lập trình viết chương trình thuận tiện và nhanh hơn, bao gồm
- Các phép tăng 1, giảm 1: ++ và –
- Cách viết phép toán như sau:
vế_trái ++;
và
vế_trái --;
Hoặc
++ vế_phải;
và
--vế_phai;
Phép tăng chỉ thực hiện trên các biến nhớ dữ liệu kiểu số nguyên (không thực hiện
trên hằng số hoặc số thực)
Ví dụ:
5++ sẽ báo sai hoặc
(a + 7) ++ cũng báo sai, máy tính không thực hiện được.
Ví dụ:
n=5
++n Cho ta n=6
--n Cho ta n=4
Ta có thể viết phép toán ++ và -- trước hoặc sau toán hạng như sau : ++n, n++, --n,
n--. Sự khác nhau của ++n và n++ ở chỗ: trong phép n++ thì tăng sau khi giá trị của nó đã
được sử dụng, còn trong phép ++n thì n được tăng trước khi sử dụng. Sự khác nhau giữa n-
- và --n cũng như vậy
Ví dụ:
n=5
x=++n Cho ta x=6 và n=6
x=n++ Cho ta x=5 và n=6
Phép chuyển đổi kiểu dữ liệu là việc cần chuyển một dữ liệu từ kiểu này sang kiểu
khác, khi chuyển có thể bị mất dữ liệu
(tên_kiểu_mới )dữ_liệu_cần_chuyển
Ví dụ:
A=1/2 ; sẽ cho kết quả là 0 vì hai toán hạng là số nguyên
A=(float) ½ ; sẽ cho kết quả là 0.5 vì số 1 được chuyển lên thành kiểu số thực.
Các phép toán có độ ưu tiên khác nhau, điều này có ý nghĩa trong cùng một biểu
thức sẽ có một số phép toán này được thực hiện trước một số phép toán khác.
Thứ tự ưu tiên của các phép toán được trình bày trong bảng sau :
TT Phép toán Trình tự kết hợp
1 () [] -> Trái qua phải
2 ! ~ & * - ++ -- (type ) sizeof Phải qua trái
3 * ( phép nhân ) / % Trái qua phải
4 +- Trái qua phải
5 << >> Trái qua phải
6 < <= > >= Trái qua phải
7 == != Trái qua phải
8 & Trái qua phải
9 ^ Trái qua phải
10 | Trái qua phải
11 && Trái qua phải
12 || Trái qua phải
13 ?: Phải qua trái
14 = += -= *= /= %= <<= Phải qua trái
>>= &= ^= |=
15 , Trái qua phải
Chú thích:
Các phép toán tên một dòng có cùng thứ tự ưu tiên, các phép toán ở hàng trên có số
ưu tiên cao hơn các số ở hàng dưới.
Đối với các phép toán cùng mức ưu tiên thì trình tự tính toán có thể từ trái qua phải
hay ngược lại được chỉ ra trong cột trình tự kết hợp.
Ví dụ:
*--px=*(--px) ( Phải qua trái )
8/4*6=(8/4)*6 ( Trái qua phải )
Lưu ý: Chúng ta nên sử dụng cặp dấu ngoặc đơn () để đặt quyền ưu tiên cao nhất
trong biểu thức và cặp ngoặc đơn có thể lồng nhau.
Ví dụ:
(8+3*(2-4))/2 sẽ thực hiện phép trừ trước, rồi đến phép nhân, cộng, chia và kết quả
sau khi thực hiện biểu thức là 1.
2.4.1.2 Lệnh
Để yêu cần máy thực hiện các thao tác chúng ta phải viết ra các lệnh tương
ứng, bằng cách sử dụng các tên chuẩn và từ khóa kết hợp với biểu thức. Lệnh trong chương
trình được kết thúc bằng dấu chấm phẩy ;
Ví dụ:
A=5*3/2.5-4 ; sẽ yêu cầu máy tính toán và đưa kết quả sang vế trái là a ;
clrscr() ; sẽ yêu cầu máy xóa màn hình
printf( “Chao cac ban den voi lap trinh C ”); sẽ yêu cầu máy hiện lên màn
hình dòng chữ trong dấu ngoặc.
Một câu lệnh được viết trên một dòng và trên một dòng có thể viết được nhiều câu
lệnh. Dấu chấm phẩy được dùng để ngăn cách các câu lệnh và bắt buộc phải viết vào. Nếu
không máy sẽ báo lỗi khi dịch. Tuy nhiên trong chương trình nhiều chỗ không cần phải
dùng đến dấu ; đó là các câu văn này không phải là câu lệnh của C. Thí dụ:
#include <stdio.h>
#include <math.h>
Ở đây include là các dòng hướng dẫn chương trình dịch đọc tệp header là stdio.h và
math.h. Các tệp đó chứa các hàm chuẩn như vào ra hay toán học.
Thông thường một câu lệnh chưa đủ để thực hiện một chức năng hay thao tác
nào đó được yêu cầu, do vậy chúng ta phải viết nhiều câu lệnh và tạo thành một khối lệnh.
Khối lệnh là một nhóm các lệnh được ngăn cách bởi dấu chấm phẩy (;) nhưng được
gộp trong một khối giới hạn bởi một cặp ngoặc nhọn: { và }.
Ví dụ:
{
a=5*2-3; b = a/2.5+1;
printf("\n%d", a+b );
}
TURBO C xem khối lệnh cũng như một câu lệnh riêng lẻ. Nói cách khác, chỗ nào
viết được một câu lệnh thì ở đó cũng có quyền đặt một khối lệnh.
Các khai báo biến và mảng chẳng những có thể đặt ở đầu của một hàm mà còn có
thể viết ở đầu khối lệnh:
{
int a,b,c[50];
float x,y,z,t[20][30];
a==b==3;
x=5.5; y=a*x;
z=b*x;
printf("\n y= %8.2f\n z=%8.2f",y,z);
}
Sự lồng nhau của các khối lệnh và phạm vi hoạt động của các biến và mảng:
Bên trong một khối lệnh lại có thể viết lồng khối lệnh khác. Sự lồng nhau theo cách
như vậy là không hạn chế.
Khi máy bắt đầu làm việc với một khối lệnh thì các biến và mảng khai báo bên trong
nó mới được hình thành và được hình thành và được cấp phát bộ nhớ. Các biến này chỉ tồn
tại trong thời gian máy làm việc bên trong khối lệnh và chúng lập tức biến mất ngay sau
khi máy ra khỏi khối lệnh. Vậy nên:
- Giá trị của một biến hay một mảng khai báo bên trong một khối lệnh không thể
đưa ra sử dụng ở bất kỳ chỗ nào bên ngoài khối lệnh đó.
- Ở bất kỳ chỗ nào bên ngoài một khối lệnh ta không thể can thiệp đến các biến và
các mảng được khai báo bên trong khối lệnh.
Nếu bên trong một khối ta dùng một biến hay một mảng có tên là a thì điều này
không làm thay đổi giá trị của một biến khác cũng có tên là a (nếu có) được dùng ở đâu đó
bên ngoài khối lệnh này.
Nếu có một biến đã được khai báo ở ngoài một khối lệnh và không trùng tên với các
biến khai báo bên trong khối lệnh này thì biến đó cũng có thể sử dụng cả bên trong cũng
như bên ngoài khối lệnh.
Ví dụ :
Xét đoạn chương trình sau:
{
int a=5,b=2;
{
int a=4;
b=a+b;
printf("\n a trong =%3d b=%3d",a,b);
}
printf("\n a ngoai =%3d b=%3d",a,b);
}
Khi đó đoạn chương trình sẽ in kết quả như sau:
a trong =4 b=6
a ngoài =5 b=6
Do tính chất biến a trong và ngoài khối lệnh.
Biểu thức gán có thể sử dụng trong các phép toán và các câu lệnh như các biểu thức
khác. Ví dụ như khi ta viết
a=b=5;
thì điều đó có nghĩa là gán giá trị của biểu thức b=5 cho biến a. Kết qủa là b=5 và a=5.
Hoàn toàn tương tự như:
a=b=c=d=6; gán 6 cho cả a, b, c và d
Ví dụ : z=(y=2)*(x=6); { ở đây * là phép toán nhân }
gán 2 cho y, 6 cho x và nhân hai biểu thức lại cho ta z=12.
* Các phép kết hợp với gán : +=, -=, *=, /=,%=,………….
Trong ngôn ngữ lập trình cho phép ta gộp một dãy các câu lệnh đơn giản thành một
lệnh gộp. Trong Passcal thì là trong cặp từ khóa BEGIN END, còn trong lập trình C thì
khái niệm lệnh gộp có thể hiểu như là một khối lệnh.
Ví dụ:
{
a=5*7; b=a/4.5-2;
c= a+b;
printf(“\nTong hai so la %d”,c);
}
Lệnh gộp hay được dùng trong C, tùy theo yêu cầu của bài toán và mục đích sử
dụng mà chúng ta sử dụng cho hợp lý.
2.5 THỰC THI CHƯƠNG TRÌNH, NHẬP DỮ LIỆU, NHẬN KẾT QUẢ
Giải thích :
- Phần 1 dùng để nạp các thư viện cần sử dụng trong chương trình được viết như sau
:
#include<conio.h>
#include<stdio.h>
Ở đây chúng ta chỉ nạp 2 thư viện là stdio.h và conio.h, trong những chương trình
lớn hoặc đòi hỏi thì chúng ta nạp thêm các thư viện khác sẽ được giới thiệu sau.
- Phần 2 : dùng để định nghĩa các giá trị hằng, cách thức viết là:
Ví dụ :
#define ten “Pham Cong Ly”
#define nam_sinh 1980
- Phần 3, 4, 5 và 7 sẽ được trình bày trong các chương tiếp theo.
- Phần 6 để viết chương trình chính, mỗi một chương trình có duy nhất một chương
trình chính và nó sẽ điều khiển hoạt động của toàn bộ chương trình.
Chương trình chính được viết như sau:
void main()
{
…viết các lệnh yêu cầu máy thực hiện…
}
Ở đây void là một kiểu dữ liệu không xác định, và chúng ta sử dụng nó trong
chương trình chính này.
Trong cấu trúc ở trên yêu cầu tối thiểu là phần 1 và phần 6 là phải có trong một
chương trình, do đó một chương trình đơn giản được viết như sau:
#include<stdio.h>
#include<conio.h>
void main()
{
Khai báo biến nhớ ở đầu chưong trình
…viết các lệnh vào đây…
Chú ý : Phần khai báo biến nhớ trong chương trình thường được thực hiện ở phần
đầu trong chương trình chính (hàm main), tuy nhiên trong môi trường TC3.0 có thể thực
hiện bất kỳ chỗ nào nhưng phải trước khi sử dụng chúng.
Cú pháp thực hiện:
tên_kiểu_dữ_liệu tên_biến_nhớ;
Ví dụ
int a;
float b;
char c;
- Có thể định nghĩa nhiều biến nhớ cùng kiểu dữ liệu bằng một lệnh khai báo các
biến nhớ phải cách nhau bởi dấu phẩy, ví dụ:
int a, b, c;
float x, y, z;
- Có thể sử dụng toán tử gán trong khai báo biến nhớ để gán dữ liệu ban đầu cho
biến nhớ theo cách sau:
int a=5, b=8, c=6 ;
float x=1.5, y=576 ;
2.5.1.2 Một số qui tắc cần nhớ khi viết chương trình
Sau khi lập trình xong chúng ta được một chương trình máy tính và khi thực hiện
chương trình này thì máy tính sẽ thực hiện các lệnh trong chương trình chính. Quá trình
thực hiên sẽ tuần tự từ trên xuống dưới và từ trái qua phải.
Ví dụ : Chúng ta có chương trình như sau:
#include<stdio.h>
#include<conio.h>
void main()
{
A1 ;
A2 ; B1 ;
A3 ; B2 ; B3 ;
B4 ; A4 ;
C1 ;
}
Thì máy sẽ thực hiện các lệnh theo thứ tự sau: A1, A2, B1, A3, B2, B3, B4, A4, C1.
Để sử dụng được các lệnh hiện dữ liệu lên màn hình chúng ta phải sử dụng lệnh nạp
thư viện <stdio.h>, vì vậy đó là thư viện chứa các chương trình vào ra chuẩn, là một thư
viện không thể thiếu khi viết chương trình.
Chúng ta sẽ làm việc với chế độ văn bản do đó trên màn hình sẽ được chia thành các
ô theo hàng và cột để hiển thị ký tự, chiều ngang có 80 cột được đánh số từ 1 đến 80 và
chiều dọc có 25 hàng đánh số từ 1 đến 25. Được chia thành nhiều ô như lưới có tọa độ
hàng và cột.
Mỗi một ô trên màn hình có tọa độ là cột thứ mấy và hàng thứ mấy, tại mỗi ô chỉ
hiện được một ký tự nào đó. Trên màn hình văn bản cũng có một con trỏ màn hình nhấp
nháy để xác định tạo độ của ô hiện thời.
Trong chương trình để hiện thông báo cho người sử dụng hoặc hiện các kết quả của
chương trình lên màn hình chúng ta sử dụng lệnh printf với cú pháp sau:
printf( “ điều khiển ”, các dữ liệu cần hiện );
Trong đó:
- Điều khiển: là các cặp ký tự để điều khiển để hiện dữ liệu ra màn hình phải viết
trong cặp dấu nháy kép, mỗi cặp ký tự điều khiển bao gồm dấu phần trăm % và sau đó là
một ký tự kiểu kiểu.
Cách viết Ý nghĩa
%d hiện số nguyên
%ld hiện số nguyên lớn (kiểu long)
%c hiện ký tự
%f hiện số thực
%x hiện số dạng hexa
%s hiện chuỗi ký tự
dữ liệu cần hiện: là các biểu thức dữ liệu cần hiện ra màn hình, các biểu thức này cách
nhau bởi dấu phẩy.
Ví dụ:
printf( “%c ”, 65 );
thì máy sẽ hiện ra ký tự có mã là 65 và đó là chữ A
printf( “ %d ” , 15/4+5 );
thì máy sẽ hiện ra kết quả của biểu thức dưới dạng số nguyên là 8
printf( “ %d %c %d %x ” , 65 , 66, ‘c’ , ‘D’ );
Kết quả trên màn hình của lệnh trên sẽ là: 97 b c 0x64 Ha noi
Lệnh pritnf sẽ bắt đầu hiện dữ liệu tại vị trí nhấp nháy của con trỏ, để quy định độ
rộng cho mỗi dữ liệu khi hiện ra màn hình ta viết them vào trước ký tự điều khiển ở trên.
Cách viết Ý nghĩa
%wd Hiện số nguyên
%wc Hiện ký tự
%w.pf Hiện số thực
%wx Hiện số dạng hexa
%ws Hiện chuỗi kí tự
Trong đó w là độ rộng của dữ liệu hiện ra trên màn hình, p là độ rộng của
phần lẻ sau dấu phẩy của dữ liệu số thực.
Ví dụ:
printf(“AB%5dEFG%3cTUV%5x”, (15+5)*3, 66, 255);
ta thấy dữ liệu (15+5)*3 sẽ được hiện ra 5 ô trên màn hình, 66 hiện 3 ô, 255 hiện ra 5 ô
vậy trên màn hình có:
A B 6 0 E F G B T U V 0x 0 0 F F
Cũng như lệnh hiện dữ liệu ra màn hình, muốn sử dụng lệnh nhập dữ liệu ta phải
khai báo thư viện <stdio.h> ở đầu chương trình.
Dữ liệu nhập từ bàn phím luôn luôn được đưa vào biến nhớ do đó trong lệnh nhập
phải có biến nhớ tương ứng để chứa dữ liệu được nhập.
Cú pháp như sau:
Trong đó điều khiển là các ký tự để quy định dữ liệu nhập vào dưới dạng nào, bao gồm:
2.5.4 MỘT SỐ LỆNH LÀM VIỆC VỚI MÀN HÌNH VÀ BÀN PHÍM
Thư viện <conio.h> sẽ cung cấp cho chúng ta các lệnh làm việc với màn hình và bàn
phím, do đó chúng ta phải nạp thư viện này ở đầu chương trình. Bao gồm các lệnh sau :
Viết lệnh Ý nghĩa
clrscr(); Xóa màn hình
textcolor(m); Đặt màu chữ để hiện ra màn hình
textbackground(m); Đặt màu nền cho dữ liệu hiện ra màn hình
kbhit(); Kiểm tra phím ấn
getch(); Chờ bấm một phím và không hiện lên
màn hình
getche(); Chờ bấm một phím có hiện lên màn hình
Các lệnh toán học được cung cấp bởi thư viện <math.h> do vậy phải nhạp thư viện
này ở đầu chương trình.
Viết lệnh Ý nghĩa
M_PI Hằng số PI trong toán học
sin(E); Tính sin của E(E là một biểu thức hoặc giá trị)
cos(E); Tính cos của E
asin(E); Tính arcsin của E
acos(E); Tính arcos của E
exp(E); Tính lũy thừa E
log(E); Tính logarit cơ số của E
pow(E1,E2); Tính lũy thừa E2 của E1
sqrt(E); Tính căn bậc hai
floor(E); Làm tròn xuống
ceil(E); Làm tròn lên
d) (2004/2003-15)%2+5
e) ((10+(9/1-8)/2+7)/3-6)/4+5
f) (5>2*2) && (5<=2+3)
g) (1>2) || (2<3) && (3>4) || (4<5)
Bài 3
Hãy cho biết các biểu thức sau sai ở đâu, hãy sửa lại cho đúng và cho biết kết quả.
a) 17-(8*3/4
b) 2005%2004)*(2003-3
c) (9+8(/5+4(/3+1)))
Bài 4: Viết chương trình hiện ra màn hình thông tin về bạn như họ tên, ngày sinh, quê
quán, lớp. Như hình sau đây.
Mục tiêu:
- Trình bày được và vận dụng được các lệnh cấu trúc: cấu trúc lựa chọn, cấu trúc lặp
xác định và lặp vô định.
- Trình bày được và vận dụng được các lệnh bẻ vòng lặp
- Câu lệnh đơn giản là câu lệnh không chứa các lệnh khác. Đó là phép gán, lời gọi
hàm void, là hàm không nhận giá trị quay trả lại vào tên hàm, lệnh nhảy không điều kiện
goto.
Lời gọi hàm loại void bao hàm rất nhiều quá trình xử lý khác nhau.
- Câu lệnh có cấu trúc là khối lệnh, lệnh thử, rẽ nhánh và lệnh lặp.
Lệnh hợp thành hay lệnh gép bao gồm nhiều lệnh đơn giản và có khi cả lệnh ghép
khác bên trong.
Dưới đây là cấu trúc câu lệnh có cấu trúc.
Thuật toán là một hệ thống chặt chẽ và rõ ràng các qui tắc nhằm xác định một dãy
các thao tác trên những đối tượng, sao cho sau một số hữu hạn bước thực hiện các thao tác
này, ta thu sẽ được kết quả mong muốn.
- Tính kết thúc: thuật toán phải được kết thúc sau một số hữu hạn thao tác.
- Tính rõ ràng, chặt chẽ: các thao tác được trình bày trong thuật toán phải rõ ràng,
phải thực hiện được bằng máy tính. Các bước này có thể có thứ tự nhất định, nếu thay đổi
trật tự nà thì thuật toán sẽ bị sai.
- Tính phổ dụng: thuật toán có thể áp dụng được cho các bài toán tương ứng, tức là
không chỉ giải quyết duy nhất một bài toán đã đặt ra.
- Tính hiệu quả: thể hiện trong cả không gian và thời gian. Về không gian thuật toán
phải thực hiện trong điều kiện khả năng của máy tính hiện tại, về thời gian đòi hỏi thuật
toán phải cho kết quả sớm có thể được. Đó là tối ưu thuật toán.
- Cách thứ nhất là nêu trình tự các bước từ bước 1 đến bước cuối cùng, ở mỗi bước
trình bày các thao tác làm bằng ngôn ngữ tự nhiên, càng chi tiết càng tốt.
- Cách thứ hai sử dụng sơ đồ khối: trong đó sử dụng các hình vẽ với ý nghĩa như sau
Hình vẽ Ý nghĩa
Thể hiện các thao tác nhập dữ liệu vào, đưa kết
quả dữ liệu ra
Một thuật toán được trình bày dưới dạng sơ đồ sẽ rõ ràng và tường minh hơn, người
lập trình sẽ dễ dàng triển khai chương trình từ thuật toán. Tuy nhiên nếu thuật toán lớn qua
thì việc trình bày bằng mô tả các bước sẽ đơn giản hơn, tuy nhiên các bước phải được nói
rõ chi tiết từng thao tác để người lập trình có thể nắm bắt được và triển khai chương trình
dễ dàng.
- Ví dụ 1: Thuật toán “đổi chỗ”, có hai thùng A và B, thùng A đựng thóc và thùng B
đựng ngô, hãy đổi chỗ thóc và ngô cho nhau.
(thóc ) (ngô)
Để thực hiện thuật toán này chúng ta phải sử dụng thêm một thùng trung gian thứ 3,
ta ký hiệu là C và thuật toán được trình bày theo cách 1 như sau:
Lệnh rẽ nhánh cho phép chương trình thực hiện chương trình thực hiện các khối
lệnh tùy theo trường cụ thể, tương ứng với sự lựa chọn bằng hình thoi trong cách biểu diễn
thuật toán. Ngôn ngữ C có các lệnh rẽ nhánh như sau:
3.2.1 LỆNH IF
Lệnh rẽ nhánh này cho phép rẽ hai nhánh ứng với trường hợp đúng hoặc sai của biểu
thức chọn, cách viết như sau:
Cú pháp:
if ( điều_kiện_lựa_chọn )
hành_động_1;
else
hành_động_2;
Trong đó:
- điều kiện lựa chọn: là biểu thức logic có giá trị đúng hoặc sai.
- hành động 1 và 2: là các khối lệnh để thực hiện cho trường hợp 1 và 2 tương ứng.
Chức năng: Máy sẽ thực hiện hành động 1 nếu điều kiện lựa chọn có giá trị đúng,
ngược lại máy sẽ thực hiện hành động 2.
Ví dụ:
if ( x%2 ==0 )
printf(“ So x la so chan ”);
else
printf(“ So x la so le ”);
Trong thí dụ này máy sẽ hiện thông báo cho biết số x là số chẵn hay số lẻ tùy theo
điều kiện tương ứng
Chú ý:
- Mỗi khối lệnh có thể có một hoặc nhiều lệnh, nếu có nhiều lệnh thì phải có cặp dấu
mở đóng ngoặc nhọn { và }.
- Sau mỗi khối lệnh phải có dấu chấm phẩy;
- Điều kiện lựa chọn có thể là biểu thức số nguyên, khi đó nếu biểu thức có giá trị
bằng 0 máy sẽ thực hiện hành động 2 (trường hợp sai ), ngược lại nếu biểu thức có giá trị
khác 0 thì máy sẽ thực hiện hành động 1 (trường hợp đúng).
- Lệnh if này có thể không sử dụng hành động 2 và từ khóa else, khi đó nếu biểu
thức chọn có giá trị sai thì máy sẽ bỏ qua (không làm gì cả).
Sơ đồ khối:
Lệnh switch cho phép rẽ nhiều nhánh với các trường hợp bằng xảy ra khi so sánh
biểu thức chọn với các giá trị trong từng trường hợp đó, cách viết như sau:
Cú pháp:
switch (biểu_thức_chọn)
{
Trong đó:
- biểu thức chọn: là một biểu thức dạng đếm được có thể là số nguyên hoặc ký tự,
dung để so sánh lựa chọn.
- các giá trị 1, 2, …, k : là các giá trị số nguyên hoặc ký tự để so sánh ứng với trường
hợp đó.
- các hành động 1,2,…,k : là các khối lệnh để thực hiện ứng với từng trường hợp
trên.
- từ khóa break sau mỗi hành động để kết thúc lệnh sau mỗi trường hợp xẩy ra. Có
thể có hoặc không có từ khóa break này.
Chức năng: Máy sẽ so sánh biểu thức chọn lần lượt với các giá trị 1, giá trị 2, giá trị
3,…giá trị k. Nếu gặp một giá trị i (i=1,2,…,k) nào đó bằng giá trị biểu_thức_chọn thì
máy sẽ thực hiện các hành động đứng sau giá trị i đó đến khi gặp từ khóa break hoặc hết
mọi hành động có trong lệnh switch. Nếu tất cả các trường hợp từ 1 đến không xẩy ra điều
kiện bằng thì mặc định máy sẽ thực hiện hành động X sau từ khóa default.
Ví dụ :
switch ( t )
{
case 1 : printf (“ Chu nhat ”); break;
case 2 : printf (“ Thu 2 ”); break;
case 3 : printf (“ Thu 3 ”); break;
case 4 : printf (“ Thu 4 ”); break;
case 5 : printf (“ Thu 5 ”); break;
case 6 : printf (“ Thu 6 ”); break;
case 7 : printf (“ Thu 7 ”); break;
default : printf (“ Khong phai la ngay trong tuan ”); break;
}
Trong ví dụ này máy sẽ thực hiện lên màn hình tên bằng tiếng Anh của một ngày
trong tuần theo giá trị của t từ 1 đến 7, nếu không máy sẽ hiện thông báo không phải là
ngày trong tuần.
Chú ý:
- Nếu sau mỗi trường hợp không có lệnh break để kết thúc thì máy sẽ thực hiện tất
cả các trường hợp tiếp theo mà không cần kiểm tra điều kiện giữa biểu thức chọn và giá trị
tương ứng của case.
- Lệnh switch có thể không có phần tử khóa default và khối lệnh X, khi đó máy sẽ
không làm gì trong trường hợp không xẩy ra điều kiện giữa biểu thức chọn và các giá trị
từ 1 đến k.
Sơ đồ khối:
hành_động_lặp;
Trong đó:
- các nhóm lệnh 1, biểu thức 2 và và nhóm lệnh 3 là các lệnh điều khiển trong quá trình
vòng lặp của for.
- hành động lặp là khối các lệnh để máy thực hiện lặp lại nhiều lần.
Chức năng: Máy thực hiện nhóm lệnh 1 duy nhất một lần đầu tiên, sau đó thực hiện
tính biểu thức 2 và nếu kết quả tương ứng với giá trị đúng thì máy thực hiện hành động
lặp, tiếp theo máy thực hiện nhóm lệnh 3 và lặp lại kiểm tra biểu thức 2 cho đến khi có kết
quả tương ứng với giá trị sai.
Ví dụ:
for (i=1; i<5; i++)
printf(“ \n i=%d ”, i);
sẽ hiện các số nguyên từ 1 đến 4 ra màn hình, hoặc
for (i=4; i>=1 ; i-- )
printf(“ \n i=%d ” , i);
sẽ hiện các số nguyên từ 4 xuống 1 ra màn hình.
Chú ý:
- Trong hành động lặp có thể có một lệnh hoặc nhiều lệnh, nếu có nhiều lệnh thì
phải có cặp dấu mở đóng ngoặc để tạo khối { và }.
- Trong lệnh này chỉ có nhóm lệnh 1 được thực hiện một lần đầu tiên, còn lại các
biểu thức 2 và nhóm lệnh 3 và hành động sẽ được thực hiện nhiều lần trong quá trình lặp.
- Các nhóm lệnh 1, 3 và biểu thức 2 có thể không xuất hiện trong lệnh for nhưng
phải có dấu chấm phẩy (;), khi đó nếu không có biểu thức 2 thì máy sẽ xem như điều kiện
lặp luôn đúng và sẽ lặp vô tận.
- Trong mỗi nhóm lệnh 1 và 3 có thể có nhiều lệnh và cách nhau bởi dấu phẩy (,) ,
đối với biểu thức 2 có thể thay thế bằng một nhóm lệnh và khi đó thì máy sẽ lấy kết quả của
lệnh cuối cùng làm điều kiện lặp hoặc dừng.
Sơ đồ khối:
Cú pháp:
while (điều_kiện_lặp)
hành_động_lặp;
Trong đó:
- điều kiện lặp: là một biểu thức so sánh hoặc biểu thức nguyên
- hành động lặp: là các lệnh sẽ được thực hiện nhiều lần trong quá trình lặp, nếu có
nhiều lệnh phải có dấu { và } để đóng khối.
Chức năng: Trong khi điều kiện lặp vẫn có giá trị đúng thì máy sẽ lặp lại thực hiện
khối lệnh và sẽ dừng lặp nếu điều kiện sai.
Ví dụ:
x=1; y=10;
while (x<y)
{
printf(“ \n x=%d va y=%d”);
x+=2 ; y-- ;
}
Chú ý:
- Nếu điều kiện lặp sai ngay từ đầu thì máy sẽ không thực hiện hành động lặp chỉ
một lần.
Sơ đồ khối:
hành_động_lặp;
}while ( điều_kiện_lặp);
Trong đó: hành động lặp và điều kiện lặp giống với lệnh while ở trên.
Chức năng: máy sẽ thực hiện hành động lặp nhiều lần cho đến khi điều kiện lặp có
giá trị sai.
Ví dụ:
x=1; y=10;
do {
printf(“ \n x=%d va y= %d ” , x,y );
x+=2 ; y-- ;
} while (x<y);
Chú ý:
- Hành động lặp ít nhất được thực hiện một lần cho dù điều kiện lặp luôn có giá trị
sai vì máy kiểm tra điều kiện lặp sau khi thực hiện hành động lặp, điều này khác với lệnh
while.
- Trong điều kiện lặp của cả lệnh while và do-while có thể nhiều lệnh và cách nhau
bởi dấu phẩy (,), khi đó máy sẽ lấy kết quả của lệnh cuối cùng làm điều kiện lặp.
- Các lệnh rẽ nhánh và lặp có thể lồng nhau, có nghĩa là trong hành động của lệnh
này lại chứa các lệnh khác.
Sơ đồ khối:
3.4 CÁC LỆNH ĐƠN NHẰM KẾT THÚC SỚM VÒNG LẶP
3.4.1 CÂU LỆNH BREAK
Câu lệnh break có tác dụng dừng vòng lặp (for, while, do while ) trong mọi trường
hợp, nó cho phép thoáy ra khỏi các chu trình với các toán tử for, while và switch. Khi có
nhiều chu trình lồng nhau, câu lệnh break sẽ đưa máy ra khỏi chu trình bên trong nhất chứa
nó không cần điều kiện gì. Mọi câu lệnh break có thể thay bằng câu lệnh goto với nhãn
thích hợp.
Ví dụ:
Biết số nguyên dương n sẽ là số nguyên tố nếu nó không chia hết cho các số nguyên
trong khoảng từ 2 đến căn bậc hai của n. Viết đoạn chương trình đọc vào số nguyên dương
n, xem n có là số nguyên tố.
# include "stdio.h"
# include "math.h"
unsigned int n;
main()
{
int i,nt=1;
printf("\n cho n=");
scanf("%d",&n);
for (i=2;i<=sqrt(n);++i)
if ((n % i)==0)
{
nt=0;
break;
}
if (nt)
printf("\n %d la so nguyen to",n);
else
printf("\n %d khong la so nguyen to",n);
}
Lệnh continue có tác dụng lặp lại việc kiểm tra điều kiện lặp để có tiếp tục lặp hay
không.
Trái với câu lệnh break, lệnh continue dùng để bắt đầu một vòng mới của chu trình
chứa nó. Trong while và do while, lệnh continue chuyển điều khiển về thực hiện ngay phần
kiểm tra, còn trong for điều khiển được chuyển về bước khởi đầu lại ( tức là bước : tính
biểu thức 3, sau đó quay lại bước 2 để bắt đầu một vòng mới của chu trình).
Chú ý:
Lập trình căn bản (C) 48
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
Lệnh continue chỉ áp dụng cho chu trình chứ không áp dụng cho switch.
Ví dụ:
for (i=0;i<10;i++){
printf(“ i=%d ”, i);
if (i>5) continue;
i++;
}
Ví dụ: Viết chương trình để từ một nhập một ma trận a sau đó:
Tính tổng các phần tử dương của a.
Xác định số phần tử dương của a.
Tìm cực đại trong các phần tử dương của a.
#include "stdio.h"
float a[3[4];
main()
{
int i,j,soptd=0;
float tongduong=0,cucdai=0,phu;
for (i=0;i<3;++i)
for (j=0;i<4;++j)
{
printf("\n a[%d][%d]=",i,j );
scanf("%f",&phu);
a[i][j]=phu;
if (a[i][j]<=0) continue;
tongduong+=a[i][j];
if (cucdai<a[i][j]) cucdai=a[i][j];
++soptd;
}
printf("\n So phan tu duong la : %d",soptd);
printf("\n Tong cac phan tu duong la : %8.2f",tongduong);
printf("\n Cuc dai phan tu duong la : %8.2f",cucdai);
getch() ;
Nhãn có cùng dạng như tên biến và có dấu: đứng ở phía sau. Nhãn có thể được gán
cho bất kỳ câu lệnh nào trong chương trình.
Ví dụ :
ts : s=s++;
thì ở đây ts là nhãn của câu lệnh gán s=s++.
Toán tử goto có dạng:
goto nhãn;
Khi gặp toán tử này máy sẽ nhảy tới câu lệnh có nhãn viết sau từ khoá goto.
Khi dùng toán tử goto cần chú ý:
- Câu lệnh goto và nhãn cần nằm trong một hàm, có nghĩa là toán tử goto chỉ cho
phép nhảy từ vị trí này đến vị trí khác trong thân một hàm và không thể dùng để nhảy từ
một hàm này sang một hàm khác.
- Không cho phép dùng toán tử goto để nhảy từ ngoài vào trong một khối lệnh. Tuy
nhiên việc nhảy từ trong một khối lệnh ra ngoài là hoàn toàn hợp lệ. Ví dụ như đoạn
chương trình sau là sai.
goto n1;
.......
{ .....
n1: printf("\n Gia tri cua N la: ");
.....
}
Ví dụ:
Tính tổng s=1+2+3+....+10
#include "stdio.h"
main()
{
int s,i;
i=s=0;
tong:
++i;
s=s+i;
if (i<10) goto tong;
printf("\n tong s=%d",s);
}
- Điểm văn:
- Điểm toán:
- Điểm ngoại ngữ:
- Tổng số điểm:
Bạn đã trúng tuyển ( hoặc Bạn đã không trúng tuyển ) với điều kiện Tổng số điểm >=
15 hay ngược lại.
Bài 7:
Viết chương trình nhập hai số thực. Sau đó hỏi phép tính cần thực hiện và in kết quả
của phép tính đó.
Nếu là “+”, in kết quả của tổng lên màn hình.
Nếu là “-” , in kết quả của hiệu lên màn hình.
Nếu là “/” , in kết quả của thương lên màn hình.
Nếu là “*”, in kết quả của tích lên màn hình. Nếu là “+”, in kết quả của tổng lên màn
hình.
Nếu là “+”, in kết quả của tổng lên màn hình.
Bài 8:
Giải phương trình bậc hai ax2 + bx + c = 0
Bài 9:
Giải và biện luận phương trình:
x2 + ( m – 2 ) x + 1 = 0 ở đây m là tham số thực tuỳ ý .
Bài 10:
Viết chương trình nhập hai số tự nhiên N, M và thông báo ‘Dung‘ nếu N, M cùng tính
chẵn lẽ, trong trường hợp ngược lại thì thông báo ‘Sai‘.
Bài 11:
Lập trình tính tích các số tự nhiên từ 1 tới 10 .
Bài 12:
Viết chương trình đếm số lần xuất hiện của các kí tự thuộc bảng chữ cái trong 50 lần gõ
kí tự bằng bàn phím (không phân biệt a với A, b với B …, dùng hàm Upcase để chuyển đổi
chữ thường với chữ hoa).
Bài 13:
Cho số tự nhiên n , hãy lập trình để tính các tổng sau:
a. 1 + 1/22 + 1/32 + … + 1/n2
Bài 14:
Tính giá trị của biểu thức sau:
( 1 + 1/12 ) ( 1 + 1/22 ) … ( 1 + 1/n2 ) Sử dụng lệnh While
Bài 15:
Lập trình tính tổng:
A = 1 + 1/2 + 1/3 + … + 1/n (ở đây n là số tự nhiên được nhập vào từ bàn phím .)
Bài 16:
Tính hàm lũy thừa an , ở đây a thực và n tự nhiên được nhập vào từ bàn phím ..
Bài 17:
Chuyển một số tự nhiên n cho trước sang hệ cơ số 2 .
Bài 18:
Viết chương trình giải phương trình bậc hai nhưng cho phép có tùy chọn nếu nhập số 1
thì tiếp tục, còn số 0 thì dừng chương trình không giải phương trình bậc hai nữa .
CHƯƠNG 4: HÀM
Mục tiêu:
- Trình bày được khái niệm hàm
- Trình bày được qui tắc xây dụng hàm và vận dụng được khi thiết kế xây dựng chương
trình.
- Trình bày được nguyên tắc xây dựng hàm, thế nào là tham số, tham trị
- Trình bày được cách truyền tham số đúng cho hàm và truyền được tham số cho hàm.
Sử dụng được các lệnh kết thúc và lấy giá trị trả về của hàm.
4.1 KHÁI NIỆM HÀM, TẠI SAO PHẢI XÂY DỰNG VÀ SỬ DỤNG HÀM
4.1.1 KHÁI NIỆM LẬP TRÌNH CẤU TRÚC
Lập trình có cấu trúc tức là chúng ta chia một chương trình lớn thành nhiều đơn vị
chương trình nhỏ hơn, mỗi đơn vị chương trình như vậy thường để thực hiện một thao tác
cụ thể phục vụ cho chương trình lớn, ta gọi là chương trình con.
Cũng như trong thực tiễn chúng ta thường làm chia một công việc cần thực hiện
thành nhiều phần việc và giao cho mỗi phần việc một người thực hiện, sau đó tổng hợp kết
quả cho công việc cần thực hiện.
Có thể minh họa như sau:
Công việc A
Phần việc A1
Phần việc A2 Phần việc A3
Để lập một chương trình giải quyết một bài toán lớn chúng ta thường chia nhỏ bài
toán đó thành nhiều bài toán con lại được chia tiếp cho đến khi thu được bài toán đơn giản
nhất và có thể lập trình được dễ dàng, phương pháp này được gọi là ‘‘chia để trị’’ hoặc gọi
là phương pháp phân tích ‘‘top- down’’ để giải quyết một bài toán.
Một vấn đề quan trọng trong phương pháp ‘‘chia để trị’’ là chúng ta sẽ chia nhỏ một
việc thành các việc nhỏ hơn khi nào, quá trình chia nhỏ phải làm thế nào để việc tổng hợp
kết quả dễ dàng và hiệu quả. Để có một cách nhìn tổng thể và chi tiết hơn chúng ta nên
tham khảo các tài liệu liên quan về thiết kế hệ thống.
Hình dưới đây mô tả việc chia để xây dựng và quản lý như thế nào
Công việc A
Chương trình
thực hiện việc A Chia nhỏ
chương trình
Hàm là một chương trình con thực hiện một khối công việc được lặp đi lặp lại nhiều
lần trong khi chạy chương trình hoặc dùng tách một khối công việc cụ thể để chương trình
đỡ phức tạp. Có thể hiểu hàm là mô-đun chương trình độc lập, hoạt động dưới sự điều
khiển của mô-đun chương trình khác.
Một chương trình con ngoài tính độc lập còn phải có tính chất phổ dụng, tức là có
thể sử dụng cho hàng loạt công việc cùng loại. Muốn vậy phải có danh sách biến hình thức
(coi như đầu vào của mô-đun) và các đầu ra để liên kết với mô-đun điều khiển của nó.
Chương trình con thực hiện các công việc nhỏ tạo thành một mô-đun để giải quyết chương
trình lớn. Khi đó nhiệm vụ chương trình chính chỉ là cung cấp dữ liệu đầu vào cho các mô-
đun để hoàn thành công việc của mình. Một chương trình viết theo cách này gọi là chương
trình cấu trúc.
Như vậy chương trình con sẽ gắn với công việc mà nó thực hiện, chúng ta cần phải
đưa dữ liệu vào cho chương trình con để thực hiện công việc tưng ứng và sau đó trả về kết
quả. Chương trình
con thực hiện
công việc A
Để nhận các dữ liệu đưa vào cho chương trình con và nếu cần có thể chứa dữ liệu
kết quả ra chúng ta phải sử dụng tham số (parameters) của chương trình.
Tham số tồn tại dưới hai hình thức, đó là tham số hình thức và tham số thực. Tham
số hình thức là tham số để khai báo và xây dựng chương trình con, còn tham số thực để xác
định dữ liệu đưa vào khi gọi chương trình con.
+ Lý do thứ nhất: Trong khi lập chương trình chúng ta thường gặp đoạn chương
trình được lặp đi lặp lại ở nhiều lần, ở những chỗ khác nhau. Để tránh rườm rà, những đoạn
chương trình này được thay thế bằng các chương trình con tương ứng và khi cần, ta chỉ
việc làm thủ tục gọi chương trình con đó ra (với các tham số tương ứng cần thiết ) mà
không phải viết lại cả khúc chương trình đó. Thí dụ: khi cần làm các bài toán lượng giác,
thường xuyên ta cần tính SIN của một giá trị hay một biến x nào đó. Như vậy ta cần lập
một chương trình con có tên là SIN và tham số cần thiết là x. Những chương trình con
thông dụng đã được lập sẵn và để trong‘‘thư viện chương trình con mẫu’’ và được chương
trình dịch C quản lý, vì vậy còn được gọi là các chương trình con chuẩn. Trong Turbo C,
các chương trình con chuẩn này được phân loại và chứa trong các đơn vị chương trình khác
nhau dưới dạng các tệp tiêu đề (header) như stdio.h, conio.h, math.h …
+ Lý do thứ hai: để xây chương trình con là: một vấn đề lớn và phức tạp sẽ tương
ứng với một chương trình có thể rất lớn, rất dài. Do đó việc nhìn tổng quan cả chương trình
cũng như việc gỡ rối, hiệu chỉnh sẽ rất khó khăn. Ta có thể phân tách vấn đề phức tạp đó ra
thành các vấn đề nhỏ hơn (tương ứng với các chương trình con, những khối, những modul
) để dễ kiểm tra, gỡ rối từng khối một và sau đó ghép lại thành chương trình lớn. Việc này
tương ứng trong dây chuyền sản suất công nghiệp, người ta có thể lắp ráp sản phẩm từ các
bán thành phẩm, từ các modul đã được chế tạo sẵn từ nơi khác chuyển đến. Đó cũng là ý
tưởng cơ bản của lập trình có cấu trúc. Cần lưu ý là chương trình con khi này có khi chỉ
được dùng đúng một lần song khi này nó vẫn có tác dụng làm sáng sủa vấn đề trong khi lập
trình. Việc chia nhỏ chương trình thành các modul có thể ví như nguyên tắc ‘‘Chia để trị,
chia để dễ điều khiển ’’.
Một số ngôn ngữ lập trình cung cấp hai loại chương trình riêng biệt đó là hàm
(funtion) và thủ tục (procedures) như Pascal. Trong ngôn ngữ C chỉ cung cấp một loại đó là
hàm. Hàm có hoặc không có giá trị trở về.
Chương trình chính của C cũng là một hàm gọi là hàm main(), trong đó gồm dãy
lệnh theo thuật toán nào đấy sao cho sau khi máy thực hiện xong dãy lệnh này ta thu được
kết quả mong muốn.
Nhiều lệnh trong C là lệnh thực hiện hàm mẫu hoặc chương trình con. Điều đó có
nghĩa là chương trình C là một chương trình viết theo cấu trúc gồm các modun do chương
trình con thực hiện. Chúng ta cần biết cách viết các chương trình con thực hiện các modun,
cách sử dụng chương trình con, cấu trúc một chương trình C có sử dụng chương trình con
do ta viết.
- Sau khi thực hiện xong có cần trả về một dữ liệu hay nhiều dữ liệu không?
Trả lời được các câu hỏi trên thì chúng ta sẽ chuyển sang thực hiện khai báo và xây
dựng hàm.
4.2 NGUYÊN TẮC XÂY DỰNG VÀ PHÂN BIỆT CÁC THAM SỐ CỦA HÀM
Để tạo một hàm chúng ta phải làm hai việc: thứ nhất là khai báo hàm; thứ hai là viết
lệnh cho hàm (xây dựng hàm).
Khai báo hàm được thực hiện ở phần đầu chương trình(sau khai báo nạp thư viện),
khai báo để chỉ cho máy biết các thông tin về hàm bao gồm: kiểu dữ liệu trả về có hay
không, tên hàm, các tham số bao gồm kiểu và tên của từng tham số.
Có điều cần chú ý là các chương trình con trong C đều gọi là hàm, cấu trúc chương
trình con cả hai loại trong C chỉ khác nhau ở khai báo tên kiểu trả về.
Cú pháp:
Trong đó:
- tên kiểu trả về: là một tên kiểu dữ liệu quy định cho kết quả trả về.
- tên hàm: Tự đặt theo quy định của C
- kiểu 1& tên tham số 1: Xác định của tham số thứ nhất và kiểu của tham số đó,….
Lập trình căn bản (C) 57
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
Ví dụ:
int sum(int a, int b) ;
float max( float x, float y );
Nếu hàm không có dữ liệu trả về thì viết tên kiểu trả về là void, nếu không có tham
số thì bỏ trống và phải có cặp dấu mở đóng ngoặc () sau tên hàm.
Ví dụ:
void PrintMe ();
Sau khi khai báo xong hàm chúng ta có thể viết lệnh thực hiện công việc đặt ra cho
chương trình con, thông thường viết lệnh cho hàm đặt ở sau chương trình chính (sau hàm
main).
Cú pháp:
return giá-trị-dữ-liệu-trả-về;
Ví dụ:
int sum (int a, int b)
{
return a+b;
}
Hoặc:
float max ( float x, float y)
{
if (x>y)
return x;
else
return y;
}
Đối với hàm có kiểu void thì chỉ cần dung lệnh return, ví dụ:
void PrintMe ()
{
printf( “ Họ và tên : Nguyễn Kim Bằng ” );
printf( “ Ngày sinh : 01/09/1976”);
printf(“ Quê quán : Hà Tĩnh ”);
return;
}
tuy nhiên có thể không cần lệnh return trong hàm kiểu void này.
Thông thường với những chương trình đơn giản có ít chương trình con thì người ta viết
lệnh cho hàm ngay tại nơi khai báo, vì thế không cần viết lệnh sau hàm main nữa.
Khi thực hiện một chương trình máy chỉ thực hiện các lệnh trong chương trình chính
(hàm main), do đó để yêu cầu máy thực hiện công việc tương ứng với chương trình con ta
phải viết lệnh gọi chương trình con đó như sau:
Lời gọi hàm có thể là một câu lệnh độc lập hoặc đặt trong các biểu thức, nếu đặt
trong biểu thức thì hàm đó phải có giá trị trả về thực hiện tính toán biểu thức đó.
Ví dụ:
printf ( “Tổng hai số 5 và 19 là : %d ”, sum(5,19) );
sẽ thực hiện gọi hàm sum và truyền tham số thực vào tương ứng là giá trị 5 và 19 .
float m= max( a, b) ;
sẽ gọi hàm max và truyền tham số thực là giá trị trong 2 biến nhớ a và b.
PrintMe();
là lời gọi hàm với một câu lệnh độc lập.
Thông thường lời gọi hàm được viết trong chương trình chính, tuy nhiên có thể viết
trong trong một chương trình con khác. Các chương trình con có thể gọi lẫn nhau, nhưng
chúng ta phải chú ý đến vấn đề “ đệ qui ” sẽ được xét ở phần sau nếu không sẽ dẫn đến treo
máy.
Ví dụ:
int max( int a, int b )
{
if ( a>b) return a;
else return b;
}
int sum ( int a, int b , int c)
{
return max(a,b) +c;
}
void Print()
{
printf(“ Kết quả là : %d ”, sum(5,2,29) + max(32,12) );
}
Ví dụ trên cho thấy trong hàm Print() gọi đến hàm sum(…) và max (…), trong hàm
sum(…) sẽ gọi đếm hàm max(…). Kết quả hiện ra màn hình khi thực hiện hàm Print() sẽ
là:
Kết quả là : 66
Chú ý :
- Các chương trình con có thể viết trước (phía trên ) chương trình chính, khi đó
trong chương trình chính không cần khai báo chương trình con.
- Không được phép viết chương trình con trong thân chương trình chính hoặc trong
thân một chương trình con khác.
- Một chương trình con có thể có các chương trình con riêng của nó. Khi đó bản
thân chương trình con này cũng là một chương trình cấu trúc, cách viết cũng tương tự
chương trình cấu trúc đã nêu ở trên.
Biến toàn cục là biến nhớ được khai báo ở ngoài mọi hàm ( thường được khai báo ở
phía trên cùng sau khai báo thư viện ), có tác dụng đến toàn bộ chương trình cả chương
trình chính đến chương trình con.
Biến cục bộ là biến nhớ được khai báo bên trong một chương trình con, chỉ có tác
dụng ở trong chương trình con đó. Bên ngoài chương trình con thì biến đó không sử dụng
được nữa.
Ta nói biến toàn cục có phạm vi tác động đến toàn bộ chương trình, biến cục bộ chỉ
có phạm vi hoạt động trong chương trình con chứa nó mà thôi.
Có thể minh họa như sau:
Ngoài ra biến toàn cục có thể được sử dụng trong các tệp chương trình khác nếu có
liên kết với chương trình hiện tại. Để thực hiện điều này trong tệp chương trình đó phải
khai báo biến ngoài dạng toàn cục theo cú pháp:
Khi đó ta gọi biến nhớ được khai báo này là biến ngoài toàn cục.
Ví dụ minh họa:
Ví dụ về chương trình
#include<conio.h>
#include<stdio.h>
int a, b;
int sum();
int max();
void main()
{
clrscr();
printf( “Nhập số a= ” );scanf(“%d”,&a);
printf( “Nhập số b= ” );scanf(“%d”,&b);
printf(“\n Tổng hai số là : %d” , sum() );
printf(“\n Số lớn hơn là : %d” , max() );
getch() ;
}
int sum()
{
int c=a+b;
return c;
}
int max()
{
int c=((a>b)? a : b);
return c;
}
Trong ví dụ trên hai biến a & b là biến toàn cục được sử dụng trong cả ba hàm sum,
max và main. Trong hàm sum và max đều có biến cục bộ là c tuy nhiên c trong hàm sum sẽ
độc lập và riêng biệt so với c trong hàm max.
Biến toàn cục còn gọi là biến chung và biến cục bộ gọi là biến riêng.
Chúng ta sử dụng biến chung để chuyển dữ liệu giữa các chương trình con, qua ví
dụ trên ta thấy dữ liệu nhập vào a & b thực hiện trong hàm main, hàm sum lấy dữ liệu đó
để tính tổng và hàm max tìm số lớn cũng trên a & b chung.
Việc này trông vẻ đơn giản nhưng không an toàn dữ liệu và khó kiểm soát giá trị của
biến bởi vì nó có thể bị thay đổi bởi bất kỳ một hàm nào trong chương trình.
Việc giải pháp tốt hơn là chúng ta sẽ sử dụng tham số của chương trình con để
chuyển dữ liệu từ hàm này sang hàm khác thay thế cho biến chung.
Trong lập trình C có 2 loại tham số đó là tham số hình thức và tham số thực.
Tham số hình thức là tham số được khai báo khi xây dựng hàm, nó mang ý nghĩa
hình thức để nhận các dữ liệu đầu vào cho một hàm.
Tham số thực là tham số được xác đinh khi chúng ta gọi hàm, đó là các dữ liệu được
truyền vào cho hàm thực hiện tính toán.
Ví dụ:
int sum ( int a, int b)
{
return (a+b);
}
void main()
{
int x=4;
printf(“ Tổng hai số là : ”, sum(5, x*2-1));
}
Trong ví dụ trên tham số a & b trong khai báo hàm sum là tham số hình thức, còn
lời gọi hàm sum(5,x*2-1) thì 5 & x *2-1 là tham số thực, đó là các hằng số, biểu thức.
Một hàm có thể không có tham số hình thức và khi gọi hàm đó không cần đưa vào
tham số thực, ví dụ như hàm PrintMe() ở ví dụ trước.
Tham số hình thức của chương trình con hàm có thể dưới dạng biền nhớ gọi là tham
trị hoặc dưới dạng tham chiếu gọi là tham biến.
Một tham số hình thức là tham trị thì tham số đó sẽ được khai báo như biến nhớ:
Tên-kiểu-dữ-liệu tên-tham-số
Tham số hình thức là tham biến sẽ được khai báo như sau:
Tên-kiểu-dữ-liệu * tên-tham-số
Khi truyền tham số dưới dạng tham số biến chúng ta đang truyền bản thân biến đó
và bất kì sự thay đổi nào mà chúng ta thực hiện với tham số đó bên trong hàm sẽ ảnh
hưởng trực tiếp đến biến đó có dấu * ở trước tên tham số.
Ví dụ:
void ChuongTrinh( int x, float *y) ;
thì tham số x sẽ là tham trị còn tham số y là tham biến.
Tham biến sẽ tác động lên dữ liệu của tham số thực khi có sự thay đổi tham số hình
thức trong hàm. Tức là mọi sự thay đổi dữ liệu của tham số hình thức cũng sẽ là sự thay
đổi tương tứng với tham số thực truyền vào khi gọi hàm.
Khi một tham số là dạng tham chiếu (có dấu *) thì việc sử dụng nó trong hàm cũng
phải sử dụng dấu * để tính toán và xử lý.
Tham số thực tương ứng với tham biến phải luôn là một biến nhớ, khi đó ta phải sử
dụng dấu & vào trước biến nhớ để truyền vào cho hàm ứng với tham biến đó.
Ví dụ như trên:
void ChuongTrinh( int x, float y)
{
x=x*2 ; *y=*y/2 ;
pintf(" \n Hai số trong hàm : %d, %5.2f ", x, y);
}
void main()
{
int a=5; float b=7.5;
printf(“\n Hai số trước khi gọi hàm : %d, %5.2f”,a , b);
ChuongTrinh(a, &b);
printf(“\n Hai số sau khi gọi hàm là : %d , %5.2”, a, b);
}
Trong hàm ChuongTrinh() sẽ tăng x lên 2 lần và giảm y đi một nửa, lời gọi hàm này
trong hàm main() truyền hai tham số thực là a & b nên giá trị của b sẽ thay đổi theo tham
số hình thức y (vì y là dạng tham biến ). Kết quả khi chạy chương trình sẽ là:
Hai số trước khi gọi hàm : 5, 7.5
Hai số trong hàm : 10, 3.75
Hai số sau khi gọi hàm : 5, 3.75
Giá trị của a trước và sau khi gọi hàm không thay đổi ,nhưng giá trị của b sẽ thay đổi
theo sự thay đổi của hàm đó.
Minh họa theo sơ đồ về tham biến và tham trị
Với x & y là hai tham số hình thức, x là tham trị và y là tham biến, a&b là hai tham
số thực truyền vào cho x & y tương ứng.
Chú ý:
- Tham biến được khái báo phải sử dụng dấu * ở trước tên của nó.
- Tham số thực truyền vào cho một tham số hình thức dưới dạng tham trị có thể là :
một hằng, một biến, một biểu thức.
- Tham số thực truyền vào cho một tham số hình thức dưới dạng tham biến là một
biến nhớ và có sử dụng dấu &.
Đối với mỗi tham số hình thức khai báo trong hàm chúng ta có đặt giá trị mặc định
cho chúng, sử dụng toán tử “=” và viết dữ liếu sau tham số:
tên-kiểu-dữ-liệu tên-tham-số=giá-trị-dữ-liệu-mặc-định
Ví dụ:
void A( int x=6, float y=7.5);
thì hàm A sẽ có hai tham số hình thức là x & y, x có giá trị mặc định là 6 và y có giá trị
mặc định là 7.5
Chúng ta có thể bỏ qua việc đặt giá trị mặc định cho một số tham số hình thức ở đầu,
nếu có một tham số hình thức có đặt giá trị mặc định thì tất cả các tham số sau nó đều phải
được đặt, vị trí là hợp lệ:
Khi đặt giá trị mặc định cho tham số hình thức thì lời gọi hàm tương ứng có thể bỏ
qua việc truyền tham số thực cho tham số hình thức đó, ví dụ:
A( 5, 7);
Hoặc
A(5);
lệnh gọi hàm A ở sau chỉ có một tham số thực là 5 vì vậy 5 sẽ được truyền vào cho x, còn
y không có tham số thực và nó sẽ nhận giá trị mặc định 7.5 để tính toán.
4.4 CÁC LỆNH ĐƠN NHẰM KẾT THÚC HÀM VÀ NHẬN GÍA TRỊ TRẢ VỀ CHO
TÊN HÀM
Sau khi tính toán xong giá trị của hàm, một giá trị nào đó, ta phải trả lại nó cho tên
hàm trong C ta dùng câu lệnh return. Câu lệnh này được viết vào cuối hàm khi chuẩn bị kết
thúc hàm để trả lại giá trị cho hàm. Đối với hàm không đối thi chỉ co return để kết thúc
hàm và không có giá trị trả về.
Cách dùng return trả về giá trị cho hàm:
return giá-trị-dữ-liệu-trả-về;
Sau khi ta kết thúc việc tính toán thì sẽ được một kết quả nào đó, kết quả này sẽ
mang một kiểu dữ liệu thì giá-trị-dữ-liệu-trả về chính là kiểu dữ liệu mà ta xây dựng hàm
cần khai báo.
Ví dụ:
Hàm sum nếu là tổng 2 số nguyên kiểu int thì kiểu trả về là int lên kiểu là int
int sum( int a , int b)
{
return a+b;
}
Nếu cộng 2 số thực
float sum(float a, float b)
{
return a+b;
}
Bài tập chương 4
Bài 1:
Dùng hàm chuyển một số tự nhiên n cho trước sang hệ cơ số 2 .
Bài 2:
Dùng hàm giải phương trình bậc hai ax2 + bx + c = 0
Có tùy chọn cho phép có tiếp tục chương trình hay không ?
Bài 3:
Hãy viết hàm Insert đối với một chuỗi kí tự cho trước tùy ý .
Bài 4:
Viết chương trình thực hiện lần lượt các công việc sau:
- Lập hàm nhập ba số thực dương a, b,c từ bàn phím .
- Lập hàm kiểm tra xem ba số trên có lập thành ba cạnh của tam giác hay không ?
- Viết hàm tính diện tích của tam giác .
- Viết hàm tính các trung tuyến của tam giác .
- Viết hoàn thiện chương trình chính .
Bài 5:
Viết chương trình hoàn chỉnh thực hiện các công việc của thực đơn sau:
1. Nhập dữ liệu ( nhập số tự nhiên n ) .
Lập trình căn bản (C) 68
Trường TCN Cơ Điện Đông Nam Bộ Khoa Công nghệ thông tin
2. Phân tích ra thừa số nguyên tố ( phân tích n thành tích các số nguyên tố ) .
Bài 6 : Viết chương trình hoàn chỉnh công việc giải phương trình bậc 2:
1. Viết hàm nhập dữ liệu cho các biến a,b,c.
2. Viết hàm kiểm tra xem a=0 hay không ?
3. Viết hàm không có giá trị trả về khi delta<0
4. Viết hàm có giá trị trở về khi delta > 0
5. Viết hàm có giá trị trả về khi delta =0
Viết chương trình chính hoàn thiện công việc trên.
( Sau đó viết chương trình với trường hợp không có giá trị trả về cho trường hợp delta >0
và delta =0)
Bài 7 : Viết chương trình
1. Viết hàm tính diện tích hình tam giác
2. Viết hàm tính diện tích hình chữ nhật
3. Viết hàm tính diện tích hình vuông
Viết chương trình chính với tùy chọn cho phép chọn cho phép người sử dụng có thể tính
diện tích một trong các hình dựa vào nhập số 1,2,3 tương ứng.
CHƯƠNG 5 : MẢNG
Mục tiêu:
- Trình bày được khái niệm mảng
- Khai báo được mảng một chiều, mảng hai chiều, mảng nhiều chiều
- Trình bày được cách gán giá trị cho mảng trực tiếp, gián tiếp và gán được giá trị
- Vận dụng được mảng làm tham số cho hàm.
- Sắp xếp được mảng theo thứ tự tăng dần hoặc giảm dần.
5.1 TRÌNH BÀY KHÁI NIỆM MẢNG TRONG C
Mảng là cấu trúc dữ liệu cho phép quản lý một danh sách hữu hạn các dữ liệu cùng
kiểu và có thứ tự. Mảng được cung cấp hầu hết trong các ngôn ngữ lập trình.
Các dữ liệu lưu ở trong mảng phải có cùng một kiểu (số nguyên, số thực, ký tự).
Để phân biệt các dữ liệu chứa trong mảng ta sử dụng khái niệm phần tử, có thể hình
dung mỗi phần tử là một ô để chứa một dữ liệu trong mảng như minh họa ở trên.
Mảng để chứa dãy dữ liệu nhu trên là mảng một chiều, chúng ta có thể sử dụng để
chứa bảng các dữ liệu.
Ví dụ một mảng 2 chiều :
5 34 83 … …
45 2 4 … …
45 4 65 … …
34 5 23 … …
… … … … …
Mảng 2 chiều sẽ là một bảng các dữ liệu được xếp theo hàng và cột, mỗi dữ liệu sẽ
chứa trong một ô được xác định bằng cách chỉ ra ở hàng nào và cột nào.
Mảng lớn hơn hai chiều (3, 4, 5 ,…..) không thể minh họa bằng mặt phẳng nhưng
các bạn có thể hình dung mảng 3 chiều là xếp nhiều mảng 2 chiều lại với nhau, ….
5.2. CÚ PHÁP KHAI BÁO MẢNG VÀ CÁCH GÁN GIÁ TRỊ CHO MẢNG
Chúng ta có thể thực hiện khai báo kiểu mảng bằng 2 cách, đó là :
Cách 1 : Khai báo biến nhớ mảng trực tiếp
Trong đó: tên kiểu dữ liệu phần tử qui định mảng có thể chứa được các dữ liệu có
kiểu như thế nào(int, float, char ,…), tên mảng do chúng ta tự đặt theo qui định đặt tên của
ngôn ngữ C và chúng ta phải xác định mảng sẽ chứa được tối đa là bao nhiêu dữ liệu.
Ví dụ :
int a[] ;
float b[100] ;
Cách 2 : Khai báo một kiểu dữ liệu mới là mảng
Trong đó : type_name là tên kiểu mảng sẽ được tạo ra, data_type là kiểu dữ liệu
cho phần tử của mảng đó, max_item là số phần tử tối đa.
Sử dụng kiểu mảng ở trên để khai báo biến như sau :
tên-kiểu-mảng tên-biến-mảng;
Ví dụ :
typedef mang1 int[20] ;
mang1 a;
Thông thường ta sử dụng cách đơn giản hơn và dễ hiểu hơn, tuy nhiên trong một số
trường hợp phải sử dụng cách 2 mới thực hiện được trong chương trình(sẽ trình bày sau)
Để khai báo mảng hai chiều hoặc nhiều chiều ta chỉ cần thêm kích thước(số phần tử)
ở chiều tiếp theo.
Cách khai báo mảng 2 chiều :
tên-kiểu-dữ-liệu tên-mảng[số-hàng][số-cột] ;
Ví dụ :
int a[10][15];
float b[100][100];
5.2.2 CÁCH TỔ CHỨC DỮ LIỆU MẢNG TRÊN MÁY VÀ TRUY NHẬP CÁC PHẦN
TỬ
Đối với mảng một chiều các phần tử chứa dữ liệu được sắp xếp lien tục trong bộ nhớ
của máy và mỗi phần tử sẽ có một chỉ số(là số thứ tự) của phần tử đó trong mảng.
Phần tử đầu tiên có chỉ số là 0, tiếp theo là 1 và tiếp tục cho đến hết, vậy sẽ dừng lại
ở chỉ số bằng số lượng phần tử -1.
Mảng 10 25 5 28 45 … … … …
Chỉ số 0 1 2 3 4 5 6 7 …
Phần tử 1 2 3 4 5 6 7 8 …
Kích thước của mảng(tính theo byte) sẽ là : số phần tử của mảng nhân với kích
thước của mỗi chứa trong dữ liệu trong mảng.
Ví dụ :
int a[20];
thì mảng a sẽ chiếm kích thước bộ nhớ là 20x2 (kích thước số nguyên )=40 bytes.
Để truy nhập đến các phần tử ta sử dụng tên mảng và chỉ số phần tử đó theo cú pháp
sau :
tên-mảng[chỉ-số-phần-tử-cần-truy-nhập]
Ví dụ :
a[0] = 5;
sẽ truy nhập đến phần tử có chỉ số 0 (phần tử đầu tiên ) và gán bằng 5
Nếu là mảng hai chiều thì các dữ liệu của mảng được lưu trữ tuần tự theo chiều từ
trên xuống dưới theo quy tắc ưu tiên hàng ( lưu trữ hết hàng này tới hàng khác ).
Các hàng và cột được tính chỉ số bắt đầu từ 0 theo chiều từ trên xuống theo hàng và
từ trái sang phải theo cột.
Cột 0 1 2 …
Hàng
0 5 34 83 …
1 45 2 4
2 45 4 65 …
3 34 5 23 …
… … … … …
Để truy nhập các phần tử của mảng hai chiều ta sử dụng tên mảng và chỉ số hàng
cùng với chỉ số cột theo cú pháp sau :
Ví dụ :
a[2][1] = 6;
sẽ truy nhập đến phần tử ở hàng có chỉ số 2 và cột có chỉ số 1.
Kích thước vùng nhớ tăng rất nhanh khi tăng số chiều của mảng. Thông thường
người ta chỉ dung mảng một chiều (véc-tơ) hoặc mảng 2 chiều ( ma trận).
Chú ý :
- Số lượng dữ liệu chúng ta lưu trong mảng có thể ít hơn số lượng phần tử đã khai
báo, nhưng không vượt quá số lượng phần tử đã khai báo đó. Thông thường sử dụng mảng
sẽ có một số nguyên n để lưu số lượng dữ liệu được lưu trong mảng tương ứng.
- Danh sách các dữ liệu lưu trong mảng phải liên tục nhau để quản lý và xử lý một
danh sách dễ dàng. Tuy nhiên chúng ta có thể lưu các dữ liệu trên mảng rời rạc, các phần
tử lưu dữ liệu không liên tục nhau nhưng khi đó việc quản lý và xử lý trở lên vất vả và
phức tạp.
Các mảng không được khởi tạo tự động, trừ khi mỗi phần tử mảng được gán một giá
trị riêng lẻ. Không nên dùng các mảng trước khi có sự khởi tạo thích hợp. Điều này là bởi
vì không gian lưu trữ của mảng không được khởi tạo tự động, do đó dễ gây ra kết quả
không lường trước được. Mỗi khi các phần tử của một mảng chưa khởi tạo được sử dụng
trong các biểu thức toán học, các giá trị đã tồn tại sẵn trong ô nhớ sẽ được sử dụng, các
giá trị này không đảm bảo rằng có cùng kiểu như khai báo của mảng, trừ khi các phần tử
của mảng được khởi tạo một cách rõ ràng. Điều này đúng không chỉ cho các mảng mà còn
cho các biến thông thường.
Trong đoạn mã lệnh sau, các phần tử của mảng được gán giá trị bằng các dùng vòng
lặp for.
int a[20], i;
for(i=0; i<20; i++)
a[i] = 0;
Khởi tạo một mảng sử dụng vòng lặp for có thể được thực hiện với một hằng giá trị,
hoặc các giá trị được sinh ra từ một cấp số cộng.
Một vòng lặp for cũng có thể được sử dụng để khởi tạo một mảng các ký tự như sau:
Ví dụ
#include <stdio.h>
void main()
{
char alpha[26];
int i, j;
for(i = 65, j = 0; i < 91; i++, j++)
{
alpha[j] = i;
printf(“Ki tu bay gio gan la %c\n”, alpha[j]);
}
getchar();
}
Một phần kết quả của chương trình trên như sau:
Ki tu bay gio gan la A
Ki tu bay gio gan la B
Ki tu bay gio gan la C
.
.
.
Chương trình trên gán các mã ký tự ASCII cho các phần tử của mảng alpha. Kết quả
là khi in với định dạng %c, một chuỗi các ký tự được xuất ra màn hình. Các mảng cũng có
thể được khởi tạo khi khai báo. Điều này được thực hiện bằng việc gán tên mảng với một
danh sách các giá trị phân cách nhau bằng dấu phẩy (,) đặt trong cặp dấu ngoặc nhọn {}.
Các giá trị trong cặp dấu ngoặc nhọn {} được gán cho các phần tử trong mảng theo đúng
thứ tự xuất hiện.
Ví dụ:
int so[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static float nhip_do[4] = {0.0, -2.5, 13.75, 18.0};
char Nha_May[5] = {‘A’, ‘P’, ‘P’, ‘L’, ‘E’};
int diem[100] = {5, 3, 10, 9}
Các giá trị khởi tạo của mảng phải là các hằng, không thể là biến hoặc các biểu thức.
Một vài phần tử đầu tiên của mảng sẽ được khởi tạo nếu số lượng giá trị khởi tạo là ít hơn
số phần tử mảng được khai báo. Các phần tử còn lại sẽ được khởi tạo giá trị 0. Ví dụ: trong
mảng marks sau khi có sự khởi tạo như trên, bốn phần tử đầu tiên (từ 0 đến 3) tương ứng
được khởi tạo là 5, 3, 10 và 9. Các phần tử còn lại có giá trị 0. Không thể chỉ khởi tạo các
phần tử từ 1 đến 4, hoặc từ 2 đến 4, hay từ 2 đến 5 khi sự khởi tạo được thực hiện tại thời
điểm khai báo. Trong C không có khả năng lặp lại sự khởi tạo giá trị.
Trong trường hợp sự khởi tạo là tường minh, lớp extern hoặc static, các phần tử của
mảng được đảm bảo khởi tạo là 0 (không giống lớp auto).
Không cần thiết khai báo kích thước của mảng đang được khởi tạo. Nếu kích thước
của mảng được bỏ qua khi khai báo, trình biên dịch sẽ xác định kích thước của mảng bằng
cách đếm các giá trị đang được khởi tạo. Ví dụ, sự khai báo mảng external sau đây sẽ chỉ
định kích thước của mảng a là 5 vì có 5 giá trị khởi tạo.
int a[] = {1, 2, 3, 4, 5};
Khai báo mảng đa chiều có thể kết hợp với việc gán các giá trị khởi tạo. Cần phải
cẩn thận lưu ý đến thứ tự các giá trị khởi tạo được gán cho các phần tử của mảng (chỉ có
mảng external và static có thể được khởi tạo). Các phần tử trong dòng đầu tiên của mảng
hai chiều sẽ được gán giá trị trước, sau đó đến các phần tử của dòng thứ hai … Hãy xem sự
khai báo mảng sau:
int a[3][4] ={1,2,3,4,5,6,7,8,9,10,11,12};
Kết quả của phép khai báo trên sẽ như sau:
a[0][0] = 1 a[0][1] = 2 a[0][2] = 3 a[0][3]= 4
a[1][0] = 5 a[1][1] = 6 a[1][2] = 7 a[1][3] = 8
a[2][0] = 9 a[2][1] = 10 a[2][2] = 11 a[2][3] = 12
Chú ý rằng chỉ số thứ 1 chạy từ 0 đến 2 và chỉ số thứ hai chạy tử 0 đến 3. Một điểm
cần nhớ là các phần tử của mảng sẽ được lưu trữ ở những vị trí kế tiếp nhau trong bộ nhớ.
Mảng a ở trên có thể xem như là một mảng của 3 phần tử, mỗi phần tử là một mảng của 4
số nguyên, và sẽ xuất hiện như sau:
Như chúng ta đã biết ở phần trước, một chuỗi có thể được biểu diễn bằng mảng một
chiều, kiểu ký tự. Mỗi ký tự trong chuỗi được lưu trữ trong một phần tử của mảng. Mảng
của chuỗi có thể được tạo bằng cách sử dụng mảng ký tự hai chiều. Chỉ số bên trái xác
định số lượng chuỗi và chỉ số bên phải xác định chiều dài tối đa của mỗi chuỗi. Ví dụ bên
dưới khai báo một mảng chứa 25 chuỗi và mỗi chuỗi có độ dài tối đa 80 ký tự kể cả ký tự
null.
char str_a[25][80];
Ví dụ bên dưới minh hoạ cách dùng của mảng hai chiều như các chuỗi.
Xét bài toán tổ chức một danh sách tên theo thứ tự bảng chữ cái. Ví dụ sau đây nhập
một danh sách các tên và sau đó sắp xếp chúng theo thứ tự bảng chữ cái.
Ví dụ
#include <stdio.h>
#include <string.h>
#include <conio.h>
void main()
{
int i, n = 0;
int item;
char x[10][12];
char temp[12];
clrscr();
printf(“Vao chuoi ban muon tim kiem \n\n”);
printf(“Go ‘END’ khi ban muon dung \n\n”);
/* Đọc trong danh sách của chuỗi */
do
{
printf(“Chuoi %d: ”, n + 1);
scanf(“%s”, x[n]);
} while (strcmp(x[n++], “END”));
/*Bản ghi danh sách của những chuỗi */
n = n – 1;
for(item = 0; item < n - 1; ++item)
{
for(i = item + 1; i < n; ++i)
{
if(strcmp(x[item], x[i]) > 0)
{
/*Trao đổi 2 chuỗi*/
strcpy(temp, x[item]);
strcpy(x[item], x[i]);
strcpy(x[i], temp);
}
}
}
/* Hiển thị chuỗi */
printf(“Danh sach cac ban ghi cua chuoi : \n”);
Sau đây sẽ trình bày dưới dạng chương trình một số thao tác trên dữ liệu mảng, giả
sử chúng ta có mảng tên là a và n là biến chứa số lượng phần tử dữ liệu được lưu trong
mảng:
Ví dụ :
int a[100];
int n,i,j;
int tg;
Sơ đồ thuật toán:
Sơ đồ thuật toán :
Kết quả khi thực hiện đoạn chương trình hiện mảng trên
Nếu muốn hiển thị các phần tử mảng ra màn hình theo một điều kiện nào đó ta chỉ
cần thêm điều kiện vào sau lệnh for, ví dụ để thể hiện các số nguyên lẻ thì chương trình sẽ
là :
printf(‘‘\n Du lieu trong mang la \n’’) ;
for ( i=0 ;i<n ;i++ )
if ( a[i]%2!=0) // Điều kiện để hiện phần tử
{
printf(‘‘%5d’’,a[i]) ;
}
Kết quả chạy đoạn chương trình hiện mảng có điều kiện :
Ví dụ
/* Chương trình nhập và hiện các số vào một mảng hai chiều. */
#include <stdio.h>
void main()
{
int arr[2][3];
int cot, hang;
for(hang = 0; hang < 2; hang++)
{
for(cot = 0; cot < 3; cot++)
{
printf(“\nNhap so tai [%d][%d]: ”, hang, cot);
scanf(“%d”, &arr[hang][cot]);
}
}
for(hang = 0; hang < 2; hang++)
{
for(cot = 0; cot < 3; cot++)
{
printf(“\n So tai [%d][%d] là %d”,
hang, cot, arr[hang][cot]);
}
}
}
Một ví dụ về kết quả thực thi chương trình trên như sau:
So tai [0][0] la 10
So tai [0][1] la 100
So tai [0][2] la 45
So tai [1][0] la 67
So tai [1][1] la 45
So tai [1][2] la 230
Sắp xếp các phần tử dữ liệu trên mảng theo tiêu chuẩn không giảm hoặc không tăng,
có khá nhiều thuật toán nhưng để cho đơn giản ta sử dụng thuật toán sau.
Sơ đồ thuật toán sắp xếp không giảm (tăng dần)
tg=a[i];
a[i]=a[j];
a[j]=tg;
}
Nếu chuyển thành sắp xếp không ( giảm dần ) ta sẽ thay điều kiện sắp xếp thành a[i]<a[j].
Đây được xem như là bài tập dành cho các bạn.
5.3.1.4 Tính tổng và trung bình cộng
Sơ đồ tính tổng :
int tong=0;
tong=tong+a[i];
float t_binh=0 ;
t_binh =t_binh+a[i] ;
t_binh=t_binh/n;
Ta có thể xem xét các bài toán trung bình mà thỏa mãn một điều kiện nào đó.
Để tính toán các phần tử thỏa mãn một điều kiện ta chỉ cần thêm lệnh if vào trước
khối lệnh tính, ví dụ tính tổng các số dương sẽ là :
int tong=0;
tong=tong+a[i];
Chúng ta có khai báo tham số hình thức dưới dạng mảng, khi đó có thể truyền tham
số thực là một mảng cho chương trình con. Khai báo như sau :
hoặc có thể không cần số phần tử của mảng nhưng phải có cặp dấu [],
Ví dụ :
int sum( int a[100], int n) ;
int max( int a[], int n) ;
tham số hình thức a là tham số dưới dạng mảng được khai báo theo 2 cách ở trong hai hàm.
Tham số là mảng thì nó luôn luôn tồn tại dưới dạng tham biến, tức là mọi thay đổi
trên tham số hình thức mảng trong hàm sẽ có tác động đến tham số thực kho truyền vào
trong lời gọi hàm.
Trong lời gọi hàm để truyền tham số thực vào tham số hình thức mảng ta chỉ cần
viết tên mảng cần truyền, ví dụ :
int n=5, a[5] = {1, 4, 3, 5, 2} ;
s = sum( a, n) ;
m = max ( a, n) ;
thực hiện gọi hàm sum và hàm max với tham số thực truyền vào.
#include <stdio.h>
#include <conio.h>
#define MAX 20
//Khai bao prototype
int max(int, int);
int input(int);
//ham tim phan tu lon nhat trong mang 1 chieu
int max(int ia[], int in)
{
int i, imax;
imax = ia[0]; //cho phan tu dau tien la max
for (i = 1; i < in; i++)
if (max < ia[i]) //neu so dang xet > max
max = ia[i]; //gan so nay cho max
return imax; //tra ve ket qua so lon nhat
}
//ham nhap lieu vao mang 1 chieu
int input(int ia[])
{
int i = 0;
do
{
printf("Nhap vao mot so: ");
scanf("%d", &ia[i]);
} while (ia[i++] != 0);
i--;
return i;
}
void main(void)
{
int ia[MAX], ib[MAX], ic[MAX];
int inum1, inum2, inum3;
printf("Nhap lieu cho mang a: \n");
Giải thích chương trình : Hàm input có kiểu trả về là int thông qua biến i (cho biết
số lượng phần tử đã nhập vào) và 1 tham số là mảng 1 chiều kiểu int. Dòng 41, 43, 45 lần
lượt gọi hàm input với các tham số là mảng a, b, c. Khi hàm input thực hiện việc nhập liệu
thì các phần tử trong mảng cũng được cập nhật theo.
CHƯƠNG 6 : CHUỖI KÝ TỰ
Mục tiêu:
- Trình bày được thế nào là chuỗi kí tự
- Khai báo được biến chuỗi
- Trình bày cách nhập vào một chuỗi ký tự cho chương trình trước và sau khi runtime.
Nhập được một chuỗi ký tự vào cho chương trình.
- Trình bày được và áp dụng được các phép toán trên chuỗi.
- Vận dụng được các hàm xử lý chuỗi để xử lý.
Chuỗi là tập hợp các ký tự. Xâu ký tự trong C được xây dựng như là một mảng các
ký tự và độ dài xâu kết thúc bằng ký tự ‘\0’ hay còn gọi tên là ký tự NULL trong bảng
ASCII.
Các hàm xử lý xâu ký tự nằm trong thư viện #include<string.h>
Trong C không có kiểu xâu, do vậy ngôn ngữ C sử dụng mảng để lưu trữ chuỗi ký
tự, mỗi phần tử của mảng sẽ là một dữ liệu kiểu char.
char biến-xâu[n];
với n là số nguyên dương bất kỳ (<64kb) , chỉ số tối đa các ký tự của xâu.
Ví dụ:
char s[100];
char t[200];
sẽ khai báo 2 mảng là s và t để lưu chuỗi ký tự.
Các ký tự sẽ được lưu theo thứ tự từ trái sang phải của chuỗi và bắt đầu từ phần tử
có chỉ số 0 cho đến hết chuỗi.
Kết thúc một chuỗi ( ở sau ký tự cuối cùng ) sẽ có dấu hiệu đặc biệt để kết thúc đó là
ký tự ‘\0’. Và đó cũng là để xác định đọ dài thực của chuỗi ký tự có trong mảng.
Vì xâu trong C là mảng nên số ký tự có thể rất nhiều ( <65536)
Để truy nhập đến từng ký tự của xâu, ta viết
Giả sử trong xâu s lưu chuỗi ký tự “Ha noi Viet Nam” sẽ được tổ chức như hình
trên, các ký tự có chỉ số từ 0 đến hết trên mảng Ss
Và nếu có 3 lệnh sau :
s[3]= ‘t’;
s[]= ‘a’;
s[]= ‘y’;
thì sẽ thay đổi chữ “nội” thành chữ “tây”.
Để lưu một danh sách các chuỗi ta sử dụng mảng hai chiều, trong đó chiều hàng sẽ
lưu từng chuỗi, chiều cột sữ lưu các ký tự trong chuỗi.
Khai báo mảng các chuỗi ký tự (mảng 2 chiều ):
char tên-biên[n][m];
trong đó n là số hàng ( số chuỗi có thể lưu trong mảng ), m là số cột (số ký tự tối đa
của mỗi hàng ).
Ví dụ :
char s[100][50];
sẽ khai báo mảng s hai chiều để lưu trữ các chuỗi, tối đa có thể lưu trữ 100 chuỗi
và mỗi chuỗi có độ dại tối đa 99 ký tự.
Minh họa bằng hình vẽ mảng 2 chiều để lưu nhiều chuỗi như sau :
trong mảng 2 chiều trên có 3 chuỗi được lưu đó là : “Hanoi Vietnam”, “Hello
WORLD” và “Happy New Year” tương ứng trên 3 phần tử s[0], s[1], s[2] của mảng s như
đã khai báo ở trên.
Đê truy nhập đến chuỗi thứ i trong mảng ta chỉ cần viết :
s[i]
s[i][j]
Ví dụ : Nếu viết
s[0]
sẽ truy nhập đến chuỗi thứ 0 và có nội dung là “Hanoi Vietnam”, còn nếu viết
s[0][5]
6.3 BIẾN CHUỖI VÀ HẰNG , NHẬP GIÁ TRỊ CHUỖI CHO CHƯƠNG TRÌNH
Biến chuỗi được được khai báo như ở trên và có thể khởi gán ;
Cách khai báo
Vd char line[80];
rồi dùng lệnh gets(line) nhập dữ liệu vào
+ Khởi gán giá trị khi khởi tạo
Ví dụ :
char string[]= “Day la xau ki tu”;
char str[40]= “Day la xau ki tu ”;
“WELL DONE”
Một hằng chuỗi là một dãy các ký tự nằm trong dấu nháy kép. Mỗi ký tự trong một
chuỗi được lưu trữ như là một phần tử của mảng. Trong bộ nhớ, chuỗi được lưu trữ như
sau:
‘W’ ‘E’ ‘L’ ‘L’ ‘’ ‘D’ ‘O’ ‘N’ ‘E’ ‘\0’
Ký tự ‘\0’ (null) được tự động thêm vào trong cách biểu diễn bên trong của chuỗi để
đánh dấu điểm kết thúc chuỗi. Vì vậy , khi khai báo một chuỗi,phải tăng kých thước của nó
thêm một phần tử để chứa ký hiệu kết thúc null.
Nhập giá trị chuỗi cho chương trình hoàn toàn bình thường ta chỉ cần lưu ý là độ dài
tối đa ký tự được phép bao giờ nhỏ hơn 1 so với độ lớn mà ta khai báo, đồng thời cần để ý
rằng mỗi ký tự ở đây là bao gồm các các dấu cách.
Cách đọc xâu ký tự có 2 hàm
scanf(“%s”,str);
gets(str);
Vì 2 hàm này có sự khác nhau rõ rệt lên khi dùng ta phải nhập cho đúng
scanf(“%s”,str); tuy đọc là xâu ký tự nhưng phải là xâu ký tự không chứa dấu cách, dấu
tab…Nếu ta gõ trên bàn phím xâu ký tự sau
Toi day
thì str chỉ nhận có Toi
gets(str) đọc các ký tự xâu str cho đến khi ấn Enter. Vì vậy str khi này chứa cả
dấu cách, tab…Nếu bạn đã học Passcal thì thì gets(str) tương đương với Readln(str) ;
Nếu ta gõ Enter luôn, không cho ký tự nào thì thì str rỗng
if(str[0]== ‘\0’) printf(‘‘ Rỗng ’’) ;
Hoặc dùng hàm so sánh:
if(strcmp(str, ‘‘’’));
Ta có thể viết hàm gets như sau
Ví dụ : Mô phỏng hàm gets
gets()
char s[];
{
int c, i=0;
while ((c=getchar())!= ‘\n’)
s[i++] =c;
s[i] = ‘\0’;
return;
}
Sử dụng hàm gets() là cách đơn gián nhất để nhập một chuỗi thông qua thiết bị nhập
chuẩn. Các ký tự sẽ được nhập vào cho đến khi nhấn phím Enter. Hàm gets() thay thế ký tự
kết thúc trở về đầu dòng ‘\n’ bằng ký tự ‘\0’. Cú pháp hàm này như sau:
gets(str);
puts(str);
Trong đó str là một mảng ký tự đã được khai báo và khởi tạo. Chương trình sau đây
nhận vào một
Tên và hiển thị một thông báo.
Ví dụ 1:
#include <stdio.h>
void main()
{
char name[20];
/* Tên được khai báo như mảng đơn */
clrscr(); /* Xóa màn hình */
puts("Nhap ten ban :"); /* Displays a message */
gets(name); /* Nhận dữ liệu */
puts("Chao ban : ");
puts(name); /* Hiển thị dữ liệu */
getch();
}
Nếu tên Lisa được nhập vào, chương trình trên cho ra kết quả:
Nhap ten ban :
Lisa
Chao ban :
Lisa
6.4.1.2 Các thao tác Nhập/Xuất chuỗi có định dạng
Có thể sử đụng các hàm scanf() và printf() để nhập và hiển thị các giá trị chuỗi. Các
hàm này được dùng để nhập và hiển thị các kiểu dữ liệu hỗn hợp trong một câu lệnh duy
nhất. Cú pháp để nhập một chuỗi như sau:
scanf(“%s”, str);
Trong đó ký hiệu định dạng %s cho biết rằng một giá trị chuỗi sẽ được nhập vào. str
là một mảng ký tự đã được khai báo. Tương tự, để hiển thị chuỗi, cú pháp sẽ là:
printf(“%s”, str);
Trong đó ký hiệu định dạng %s cho biết rằng một giá trị chuỗi sẽ được hiển thị và str
là một mảng ký tự đã được khai báo và khởi tạo. Hàm printf() có thể dung để hiển thị ra
các thông báo mà không cần ký tự chuyển dạng.
Có thể sửa đổi chương trình bên trên để nhập vào và hiển thị một tên, sử dụng hàm
scanf() và printf().
Ví dụ 2
#include <stdio.h>
void main()
{
char name[20];
/* Tên khai báo như mảng đơn */
- Lệnh cho biết độ dài thực (số ký tự ) của chuỗi ký tự lưu trong mảng,
Ví dụ :
char s[100] = “Ha noi Viet nam”;
int a=strlen(s);
sẽ trả về độ dài thực của xâu s cho biến a, cụ thể a sẽ bằng 15.
- Lệnh gán chuỗi từ xâu này vào xâu khác :
strcpy(s1, s2);
strcat(s1, s2);
Ví dụ :
char s[100]= “Ha noi” ;
char s2[100]= “Viet nam”;
strcat (s1, s2);
- Lệnh chuyển xâu lên chữ hoa
strupr( s);
strlwr( s );
Ví dụ :
char s[100] = “hA NOi Viet nAm ”;
Sẽ cho kết quả là xâu s được chuyển lên chữ hoa đó là s=“HA NOI VIET NAM”,
Hoặc
strlwr ( s );
sẽ thực hiện chuyển xâu s về chữ thường
- Lệnh chuyển chuỗi thành số
Ví dụ :
char s[100] = “1976”;
int x=atoi ( s );
float y=atof ( “176.142” );
printf( s, “%d va %7.3f ”, x , y ) ;
khi đó x sẽ gán bằng 1976, y sẽ gán bằng 176.142 và s sẽ có nội dung là ‘‘1976’’
và 176.142
2. Giáo trình kỹ thuật lập trình C - Nguyễn Linh Giang, Nguyễn Xuân Thực, Lê Văn
Thái – Nhà xuất bản giáo dục – Năm 2005
3. Giáo trình Bài tập Giải thuật & Lập trình.- Chủ biên Thạc sĩ Dương Thăng Long –
Khoa Công nghệ thông tin – Viện Đại học mở Hà Nội