Biến toàn cục là những biến được sử dụng mọi nơi trong chương trình. Còn biến
địa phương chỉ có giá trị trong thời gian hoạt động, sau khi hàm kết thúc thì những biến
khai báo bên trong hàm đó cũng như các tham số của nó cũng kết thúc.
5.1. Biến toàn cục
Ví dụ:
Dòng File Edit Search Run Compile Debug Project Option Window Help
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <conio.h> // khai báo prototype void oddeven(); void negative();
//khai báo biến toàn cục int inum;
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 {
printf("Nhap vao 1 so nguyen : "); scanf("%d", &inum); oddeven(); negative(); getch(); } // hàm kiểm tra chẳn lẻ void oddeven() { if (inum % 2)
printf("%d la so le.\n", inum); else
printf("%d la sochan.\n", inum); }
//Hàm kiểm tra số âm void negative()
{
if (inum < 0)
printf("%d la so am.\n", inum); else
printf("%d la so duong.\n", inum); }
F1 Help Alt-F8 Next Msg Alt-F7Prev Msg Alt - F9 Compile F9 Make F10 Menu
Kết quả in ra màn hình
Giải thích chương trình
Chương trình trên gồm 2 hàm oddeven và negative, 2 hàm này ta thấy không có tham sốđể truyền biến inum vào xửlý nhưng vẫn cho kết quả đúng. Do chương trình
trình mỗi khi gọi và sử dụng nó. Xét tình huống sau: Giả sử trong hàm negative ta khai báo biến inum có kiểu int như sau:
void negative() {
int inum;
….
}
Khi đó chương trình sẽ cho kết quả sai! Do các câu lệnh trong hàm negative sử
dụng biến inum sẽ sử dụng biến inum khai báo trong hàm negative và lúc này biến inum toàn cục không có tác dụng đối với các câu lệnh trong hàm này. Biến inum khai báo trong hàm negative chỉ có ảnh hưởng trong phạm vi hàm và chu trình sống của nó bắt
đầu từ lúc gọi hàm đến khi thực hiện xong.
5.2. Biến địa phương
Các biến trong hàm là biến địa phương. Biến địa phương chỉ tồn tại trong thời
gian hàm đó hoạt động.
Tầm tác dụng của biến địa phương chỉ hạn chếtrong hàm mà nó được khai báo. Các biến địa phương cũng sẽ không có mối liên hệ nào với biến toàn cục có cùng tên và cùng kiểu. Ví dụ int n; main() { int p; … } ham_thi_du() { int p int n; …. }
- Các biến địa phương không lưu trữ kết quả cho các lần sau. Lần nào biết lần
ấy, các biến địa phương được sinh ra rồi chết luôn khi ra khỏi hàm.
- Các tham sốcũng là biến địa phương. Nghĩa là cũng chỉ hoạt động khi hàm
BÀI TẬP
Lý thuyết
1. Hàm là gì? Tại sao phải sử dụng hàm? 2. Trình bày các thành phần của hàm.
3. Trình bày tóm tắt các cách truyền tham số? Phân biệt sự khác nhau giữa các cách này. Cho ví dụ minh họa.
Thực hành
4. Viết hàm nhập một số nguyên bất kỳ. Hãy đọc giá trị của sốnguyên đó nếu nó có giá trị từ0 đến 9, ngược lại thông báo không đọc được.
5. Viết hàm hập 3 số nguyên a, b và c từ bàn phím. Hãy tìm số có giá trị nhỏ nhất ( hoặc lớn nhất).
6. Viết hàm tính tiền đi taxi từ số km nhập vào. Biết: - 1 km đầu giá 15000đ
- Từ km thứ2 đến km thứ5 giá 13500đ
- Từ km thứ 6 trởđi giá 11000đ
- Nếu đi hơn 120 km sẽđược giảm 10% trên tổng số tiền. 7. Viết hàm nhập vào tháng, in ra tháng đó có bao nhiêu ngày.
Hướng dẫn: Nhập vào tháng
Nếu là tháng 1, 3, 5, 7,8, 10, 12 thì có 30 ngày Nếu là tháng 4, 6, 9, 11 thì có 31 ngày
Nếu là tháng 2 và là năm nhuận thì có 29 ngày ngược lại 28 ngày
(Năm nhuận là năm chia chẵn cho 4)
8. Viết chương trình nhập vào 2 số x, y và 1 trong 4 toán tử +, -, *, /. Nếu là + thì in ra kết quả x + y, nếu là – thì in ra x – y, nếu là * thì in ra x * y, nếu là / thì in ra x / y (nếu
y = 0 thì thông báo không chia được)
9. Viết hàm nhận vào một sốnguyên dương n và thực hiện: a. Có phải là sốđối xứng? Là số nghịch đảo bằng chính nó. Ví dụ: 121, … b. Có phải là sốchính phương? Là số bằng bình phương số khác. Ví dụ: 4, 9, … c. Có phải là số nguyên tố? Là số lớn hơn 1 và chỉcó 2 ước số là 1 và nó. Ví dụ: 2, 3, 5, 7, 11, 13, … d. Chữ số lớn nhất và nhỏ nhất? Ví dụ: số 1706, nhỏ nhất 0 và lớn nhất 7 e. Các chữ sốcó tăng dần hay giảm dần không? Ví dụ: 12245, 156, 442, 941, …
10. Viết hàm nhận vào một sốnguyên dương n và thực hiện:
a. S = 1 + 2 + … + n b. S = 12 + 22 + … + n2
c. S = 1 + 1/2 + … + 1/n d. S = 1! + 2! + … + n!
11. Viết hàm trả về USCLN của 2 số nguyên. 12. Viết hàm in ra n phần tử của dãy Fibonacy
CHƯƠNG 5: DỮ LIỆU KIỂU MẢNG (ARRAY),
CHUỖI KÝ TỰ (STRING) VÀ BẢN CHI (STRUCT)
Mục tiêu
− Trình bày được khái niệm mảng, chuỗi ký tự và bản ghi;
− Thực hiện cách khai báo, gán giá trị cho mảng, chuỗi ký tự và bản ghi; − Thực hiện các thao tác và truy xuất cho mảng, chuỗi ký tự và bản ghi; − Thực hiện các thao tác an toàn với máy tính.
Nội dung
1. Dữ liệu kiểu mảng
1.1. Khái niệm
Mảng là một tổ chức kiểu dữ liệu có cấu trúc bao gồm một số cốđịnh các thành phần có cùng kiểu. Mỗi thành phần của mảng được truy xuất thông qua các chỉ số mô tả vị
trí của thành phần đó trong mảng.
1.2. Khai báo mảng
2.2.1. Khai báo kiểu mảng
Cú pháp:
typedef <kiểu cơ sở> <tên kiểu mảng>[<số phần tử>];
Trong đó:
- <tên kiểu mảng>: tên kiểu mảng theo quy định đặt tên/định danh. - <kiểu cơ sở> : kiểu dữ liệu của các thành phần trong mảng. - <số phần tử> : sốlượng phần tử của mảng.
Ví dụ: khai báo kiểu dữ liệu mảng một chiều có tên MangInt (gồm 20 phần tử kiểu số
nguyên) và MangFloat (gồm 30 phần tử kiểu số thực).
typedef int MangInt[20]; typedef float MangFloat[30];
2.2.2. Khai báo biến mảng
Biến mảng được khai báo trực tiếp (tường minh) như sau:
Cú pháp:
<kiểu cơ sở> <tên biến mảng>[<số phần tử>];
2.2.3. Khởi tạo giá trị cho mảng khi khai báo
Ta có thể khởi tạo giá trị cho mảng một chiều thông qua các cách sau: - Khởi tạo giá trị cho mọi phần tử của mảng.
int a[4] = {2912, 1706, 1506, 1904};
Mảng a gồm 4 phần tử 2912, 1706, 1506, 1904
- Khởi tạo giá trị cho một số phần tửđầu tiên của mảng. Các phần tử còn lại sẽ có giá trị 0. int a[4] = {2912, 1706}; Mảng a gồm 4 phần tử 2912, 1706, 0, 0 - Khởi tạo giá trị 0 cho tất cả các phần tử của mảng, ta chỉ cần khởi tạo phần tử đầu bằng 0. int a[100] = {0};
- Trình biên dịch tựxác định số phần tử của mảng thông qua danh sách khởi tạo nếu số phần tửkhông được chỉ rõ.
int a[] = {2912, 1706, 1506, 1904};
Mảng a sẽ gồm 4 phần tử 2912, 1706, 1506, 1904.
1.3. Gán giá trị cho mảng
1.3.1. Truy xuất dữ liệu kiểu mảng
Truy xuất đến từng phần tử của mảng thông qua cú pháp sau:
Cú pháp:
<biến mảng>[<gt cs1>][<gt cs2>]…[<gt csn>]
Trong đó <gt cs1>, <gt cs1>, ..., <gt csn>: là các giá trị cụ thể của phần tử trong mảng muốn truy xuất.
Lưu ý, mảng a có n phần tử thì chỉ số của mảng sẽ từ0 đến n–1. Tức mà các phần tử của mảng là a[0], a[1], …, a[n-1].
Ví dụ, cho hai mảng sau:
int MangSo[100]; float DiemHK[100][2];
Lúc này, phần tử thứ 3 của mảng có chỉ sốlà 2: MangSo[2], điểm thi môn thứ
nhất của sinh viên thứ2: DiemHK[1, 0]…
Không thể nhập xuất trực tiếp biến kiểu mảng mà phải thông qua từng thành phần của mảng đó.
Ví dụ:
printf(“%d”, MangSo); // Sai
printf(“%d”, MangSo[2]); // Dung printf(“%f”, DiemHK); // Sai
printf(“%f”, DiemHK[1][0]); // Dung
Không thể dùng lệnh gán thông thường để gán dữ liệu giữa hai kiểu mảng cùng loại. Thay vào đó, ta sẽ gán dữ liệu trực tiếp giữa từng phần tửtương ứng.
Gán trực tiếp từng phần tử
<biến mảng đích>[<chỉ số thứ i>] =
<biến mảng nguồn>[<chỉ số thứ i>];
Ví dụ:
typedef int MangSo[3]; MangSo a, b = {1, 2, 3};
a = b; // Sai
for (int i=0; i<3; i++) // Dung a[i] = b[i];
1.3.2. Truyền mảng cho hàm
Tham số kiểu mảng trong khai báo hàm giống khai báo mảng.
void SapXepTang(int a[100]);
Tham số kiểu mảng được truyền cho hàm chính là địa chỉ của phần tửđầu tiên của mảng do đó sốlượng phần tử trong tham số mảng có thể bỏ trống hoặc ta khai báo dạng con trỏ.
void SapXepTang(int a[]); // Cach 1 void SapXepTang(int *a); // Cach 2
Do ta chỉ truyền địa chỉ của phần tửđầu tiên của mảng nên số thành phần thực sựđược sử dụng phải được truyền cho hàm thông qua một tham số khác.
// n la so phan tu thuc su duoc su dung void SapXepTang(int a[], int n);
void SapXepTang(int *a, int n);
Do biến mảng chính là địa chỉ của phần tửđầu tiên của mảng đó nên khi gọi hàm, ta chỉ việc truyền tên biến mảng cho hàm. Lưu ý, nội dung của mảng có thểthay đổi khi kết thúc hàm.
void NhapMang(int a[], &n); void SapXepTang(int a[], int n); void XuatMang(int a[], n);
void main(){
int a[100]; int n;
NhapMang(a, n); // Goi ham NhapMang SapXepTang(a, n); // Goi ham SapXepTang XuatMang(a, n); // Goi ham XuatMang }
1.4. Một số bài toán trên mảng
Mảng được xét là mảng các số nguyên và số phần tử tối đa của mảng là MAX = 100.
#define MAX 100
1.4.2. Nhập mảng
Yêu cầu
Viết thủ tục nhập mảng cho phép người sử dụng nhập sốlượng phần tử n thực tế
của mảng a và lần lượt nhập vào giá trị cho từng phần tử trong mảng này.
Ý tưởng
- Nhập sốlượng phần tử thực tế n của mảng.
- Lần lượt nhập giá trị cho n phần tử của mảng từ chỉ số0 đến chỉ số n – 1.
Cài đặt
Thủ tục nhập mảng
Đầu vào : mảng a, sốlượng phần tử n (tham chiếu)
Đầu ra : không có
void NhapMang(int a[], int &n){
// Nhap so luong phan tu n cua mang
printf(“Nhap so phan tu n: ”); scanf(“%d”, &n);
// Nhap gia tri cho n phan tu cua mang for (int i = 0; i < n; i++){
printf(“Nhap phan phan tu thu %d: ”, i); scanf(“%d”, &a[i]);
} }
Lưu ý
- Tham số n (sốlượng phần tử) phải là tham chiếu (có dấu &) vì nội dung sẽ thay
đổi sau khi thực hàm.
- Vì sốlượng phần tử lớn nhất là MAX nên khi cần có thể kiểm tra xem n có vượt quá MAX hay không trước khi cho nhập mảng.
1.4.3. Xuất mảng
Yêu cầu
Viết thủ tục xuất mảng cho phép người sử dụng xuất nội dung mảng a cho trước với sốlượng phần tử là n.
Ý tưởng
Cài đặt
Thủ tục xuất mảng
Đầu vào : mảng a, sốlượng phần tử n
Đầu ra : không có
void XuatMang(int a[], int n){
// Xuat gia tri cua N phan tu trong mang
printf(“Noi dung cua mang la: ”);
for (int i = 0; i < n; i++)
printf(“%d ”, a[i]); printf(“\n”);
}
Lưu ý:
Tham số n không cần thiết phải là tham chiếu (không cần &) vì nội dung sẽ không thay
đổi sau khi thực hiện hàm.
1.5. Mảng nhiều chiều
1.5.1. Khái niệm
1.5.2. Khai báo kiểu mảng 2 chiều
Cú pháp:
typedef <kiểu cơ sở> <tên kiểu>[<N1>][<N2>];
N1, N2: sốlượng phần tử mỗi chiều
Ví dụ:
typedef int MaTran[3][4];
Cú pháp (tường minh)
<kiểu cơ sở> <tên biến> [<N1>][<N2>];
Cú pháp (không tường minh – thông qua kiểu)
Typedef <kiểu cơ sở> <tên kiểu> [<N1>][<N2>]; <tên kiểu> <tên biến>;
<tên kiểu> <tên biến 1>, <tên biến 2>;
Ví dụ: (tường minh)
int a[10][20], b[10][20]; int c[5][10];
int d[10][20];
Ví dụ: (không tường minh – thông qua kiểu) typedef int MaTran10x20[10][20]; typedef int MaTran5x10[5][10]; MaTran10x20 a, b;
MaTran11x11 c; MaTran10x20 d;
1.5.3. Truy xuất đến một phần tử
Thông qua chỉ số
Ví dụ: Cho mảng 2 chiều như sau: int a[3][4];
Các truy xuất
Hợp lệ: a[0][0], a[0][1], ..., a[2][2], a[2][3]
Không hợp lệ: a[-1][0], a[2][4], a[3][3]
Gán dữ liệu kiểu mảng
Không được sử dụng phép gán thông thường mà phải gán trực tiếp giữa các phần tử.
<biến mảng đích> = <biến mảng nguồn>; // sai
<biến mảng đích>[<giá trị cs1>][<giá trị cơ sở 2>] = <giá trị>
Ví dụ: int a[5][10], b[5][10]; b = a; //sai int i, j; for (i = 0; i < 5; i++) for (j = 0; j< 10; j++ ) b[i][j] = a[i][j];
Truyền tham số cho hàm
- Tham số kiểu mảng trong khai báo hàm giống như khai báo biến mảng
void NhapMaTran(int a[50][100]);
- Tham số kiểu mảng truyền cho hàm chính là địa chỉ của phần tửđầu tiên của mảng.
• Có thể bỏ sốlượng phần tử chiều thứ 2 hoặc con trỏ.
• Mảng có thểthay đổi nội dung sau khi thực hiện hàm.
void NhapMaTran(int a[][100]); void NhapMaTran(int (*a)[100]);
Truyền mảng cho hàm
- Sốlượng phần tử thực sự truyền qua biến khác.
void XuatMaTran(int a[50][100], int m, int n); void XuatMaTran(int a[][100], int m, int n); void XuatMaTran(int (*a)[100], int m, int n);
- Lời gọi hàm
void XuatMaTran(int a[][100], int &m, int &n); void XuatMaTran(int a[][100], int m, int n); void main(){
int a[50][100], m, n; NhapMaTran(a, m , n); XuatMaTran(a, m, n); }
1.5.4. Một sốbài toán cơ bản Một sốqui ước Kiểu dữ liệu: #define MAXD 50 #define MAXC 50 Nhập ma trận Yêu cầu: Cho phép nhập mảng a, m dòng, n cột. Ý tưởng:
- Cho trước một mảng 2 chiều có dòng tối đa là MAXD, số cột tối đa là MAXC.
- Nhập sốlượng phần tử thực sự m, n của mỗi chiều. - Nhập từng phần tử từ[0][0] đến [m-1][n-1].
Hàm nhập ma trận
void NhapMaTran(int a[][MaxC], int &m, int &n){
printf(“Nhap so dong, so cot cua ma tran: ”); scanf(“%d%d”,&m, &n);
int i, j;
for (i = 0; i<m; i++)
for (j = 0; j<n; j++){
printf(“Nhap a[%d][%d]: ”,i,j); scanf(“%d”,&a[i][j]);
} }
Xuất ma trận
Yêu cầu: cho phép nhập mảng a, m dòng, n cột.
Ý tưởng: xuất giá trị từng phần tử của mảng 2 chiều từ dòng có 0 đến dòng m -1, mỗi dòng xuất giá trị của cột 0 đến cột n-1 trên dòng đó.
Hàm xuất ma trận:
void XuatMaTran(int a[][MAXC], int m, int n){ int i, j;
for (i = 0; i<m; i++){ for (j = 0; j<n; j++)
printf(“%d”,a[i][j]); printf(“\n”);
} }
2. Dữ liệu kiểu chuỗi ký tự
2.1. Khái niệm
Chuỗi ký tự là một dãy gồm các ký tự hoặc một mảng các ký tự được kết thúc bằng ký tự '\0' (còn được gọi là ký tự NULL trong bảng mã Ascii).
Các hằng chuỗi ký tựđược đặt trong cặp dấu nháy kép "".
2.2. Khai báo chuỗi
2.2.1. Khai báo theo mảng
Cú pháp:
char <Biến> [Chiều dài tối đa]
Ví dụ: Trong chương trình, ta có khai báo: char Ten[12];
Trong khai báo này, bộ nhớ sẽ cung cấp 12-1 bytes đểlưu trữ nội dung của chuỗi ký tự Ten; byte cuối cùng lưu trữ ký tự‘\0’ để chấm dứt chuỗi.
Ghi chú:
Chiều dài tối đa không nên khai báo thừa để tránh lãng phí bộ nhớ, nhưng cũng
không nên khai báo thiếu.
2.2.2. Khai báo theo con trỏ
Cú pháp:
char *<Biến>
Ví dụ: Trong chương trình, ta có khai báo: char *Ten;
Trong khai báo này, bộ nhớ sẽdành 2 byte đểlưu trữđịa chỉ của biến con trỏ Ten
đang chỉđến, chưa cung cấp nơi đểlưu trữ dữ liệu. Muốn có chỗđểlưu trữ dữ liệu, ta phải gọi đến hàm malloc() hoặc calloc() có trong “alloc.h”, sau đó mới gán dữ liệu cho biến.
2.2.3. Vừa khai báo vừa gán giá trị
Ví dụ:
#include<stdio.h> #include<conio.h> int main(){
char Chuoi[]="Mau nang hay la mau mat em" ; printf("Vua khai bao vua gan trị : %s",Chuoi) ; getch();
return 0; }
Ghi chú: Chuỗi được khai báo là một mảng các ký tự nên các thao tác trên mảng có thể
2.3. Các thao tác trên chuỗi
Không thể gán giá trị hay sử dụng phép toán + (ghép chuỗi) và các phép toán so
sánh như: > (lớn hơn), < (nhỏhơn),… mà phải gọi các hàm thư viện trong <string.h>;
2.3.1. Cộng chuỗi - Hàm strcat()
Cú pháp:
char *strcat(char *des, const char *source)
Hàm này có tác dụng ghép chuỗi nguồn vào chuỗi đích.
Ví dụ: Nhập vào họ lót và tên của một người, sau đó in cả họ và tên của họ lên màn