Đề cương môn Lập trình Cơ bản (Phần 2)
Đề cương môn: Lập trình Cơ bản
Chương 5. Hàm
1. Khái niệm
Hàm là một đoạn chương trình độc lập thực hiện trọn vẹn một công việc
nhất định sau đó trả về giá trị cho chương trình gọi nó, hay nói cách khác hàm là
sự chia nhỏ của chương trình.
2. Khai bá o hàm
2.1. Cú phá p khai báo nguyên mẫu hàm
<Kiểu_hàm> Tên_hàm
([Danh_sá ch_tham_số]);
Trong đó:
Tên_hàm: là một tên hợp lệ theo quy tắc về tên của ngôn ngữ C/C++. Mỗi
hàm có tên duy nhất và không được trùng với các từ khóa. Tên hàm sẽ được
dùng để gọi hàm.
Kiểu_hàm: Hàm có thể trả về một giá trị cho nơi gọi, giá trị đó thuộc một
kiểu dữ liệu nào đó, kiểu đó được gọi là kiểu hàm. Kiểu hàm có thể là kiểu chuẩn
cũng có thể là kiểu do người dùng định nghĩa. Nếu hàm không trả về giá trị thì
kiểu hàm là void.
Danh_sách_tham_số: Hàm có thể nhận dữ liệu vào thông qua các tham
số của nó (tham số hình thức), các tham số này cũng thuộc kiểu dữ liệu xác định.
Có thể có nhiều tham số, các tham số cách nhau bởi dấu phẩy (,). Trong nguyên
mẫu không bắt buộc phải có tên tham số nhưng kiểu của nó thì bắt buộc. Nếu
hàm khô ng có tham số chúng ta có thể để trống phần này hoặc có thể khai báo là
void.
Ví dụ:
int max(int a, int b); // khai báo nguyên mẫu hàm max, có hai tham số kiểu
int, kết quả trả về kiểu int.
float f(float, int); // nguyên mẫu hàm f, có hai tham số, tham số thứ nhất
kiểu float, tham số thứ 2 kiểu int, kết quả trả về kiểu float.
void nhapmang(int a[], int ); // hàm nhapmang, kiểu void (không có giá trị
trả về), tham số thứ nhất là một mảng nguyên, tham số thứ 2 là một số nguyên.
void g(); // hàm g không đối, không kiểu.
2.2. Định nghĩa hàm
Cú phá p:
<kiểu_hàm> <tên_hàm>([khai_báo_tham_số])
42
Đề cương môn: Lập trình Cơ bản
{
}
< thâ n hàm>
Dòng thứ nhất là tiêu đề hàm (dòng tiêu đề) chứa các thông tin về hàm:
tên hàm, kiểu của hàm (hai thành phần này giống như trong nguyên mẫu hàm)
và khai báo các tham số (tên và kiểu) của hàm, nếu có nhiều hơn một thì các
tham số cách nhau bởi dấu phẩy(,).
Thân hàm là các lệnh nằm trong cặp { }, đây là các lệnh thực hiện chức
năng của hàm.
Trong hàm có thể có các định nghĩa biến, hằng hoặc kiểu dữ liệu; các
thành phần này trở thành các thành phần cục bộ của hàm.
Ví dụ:
unsigned long giaithua (int n)
{
unsigned long ketqua =1;
int i;
for (i =2; i<=n; i++)
ketqua = ketqua*i;
return ketqua;
}
3. Kết quả trả về của hàm – Lệnh return
Nếu hàm có giá trị trả về (kiểu hàm khác void) thì trong thân hàm trước khi
kết thúc phải có câu lệnh trả về giá trị:
return <giá trị>;
<giá_trị> sau lệnh return chính là giá trị trả về của hàm, nó phải có kiểu
phù hợp với kiểu của hàm được khai báo trong dòng tiêu đề. Trường hợp hàm
void chúng ta có thể dùng câu lệnh return (không có giá trị) để kết thúc hàm hoặc
khi thực hiện xong lệnh cuối cùng (gặp } cuối cùng) hàm cũng kết thúc.
Ví dụ: Hàm nhập một mảng có n phần tử nguyên:
Tên hàm: nhapmang.
Giá trị trả về: không trả về.
Tham số: có hai tham số là mảng cần nhập A và số phần tử cần nhập N
Nguyên mẫu hàm là: void nhapmang (int [], int);
định nghĩa hàm như sau:
43
Đề cương môn: Lập trình Cơ bản
void nhapmang (int A[], int N)
{
int i;
cout<<"\nNhap so phan tu cua mang ";
for(i=0;i<N; i++)
{
cout<<"a[“<<i<< ”]= ";
cin>>a[i];
}
return ;
}
4. Lời gọi hàm và Cách truyền tham số cho hàm
Một hàm có thể gọi thực hiện thông qua tên hàm, với những hàm có tham
số thì trong lời gọi phải truyền cho hàm các tham số thực sự (đối số) tương ứng
với các tham số hình thức.
Khi hàm được gọi và truyền tham số phù hợp thì các lệnh trong thân hàm
được thực hiên bắt đầu từ lệnh đầu tiên sau dấu mở móc { và kết thúc khi gặp
lệnh return, exit hay gặp dấu đóng móc } kêt thúc hàm.
Cú phá p:
<tên_hàm> ([danh sách các tham số thực sự]);
Các tham số thực sự phải phù hợp với các tham số hình thức:
Số tham số thực sự phải bằng số tham số hình thức.
Tham số thực sự được truyền cho các tham số hình thức tuần tự từ trái
sang phải, tham số thực sự thứ nhất truyền cho tham số hình thức thứ nhất,
tham số thực sự thứ 2 truyền cho tham số hình thức thứ 2,.. kiểu của các tham
số hình thức phải phù hợp với kiểu của các tham số hình thức. Sự phù hợp ở
đây được hiểu là kiểu trùng nhau hoặc kiểu của tham số thực sự có thể ép về
kiểu của tham số hình thức.
Với C/C++ việc truyền tham số cho hàm được thực hiện qua cơ chế truyền
tham trị. Tức là trong hàm chúng ta sử dụng tham số hình thức như là một bản
sao dữ liệu của tham số được truyền cho hàm, do vậy chúng không làm thay đổi
giá trị của tham số truyền vào. Hay nói khác đi, các tham số hình thức là các biến
cục bộ trong hàm, sự thay đổi của nó trong hàm không ảnh hưởng tới các biến
bên ngoài.
44
Đề cương môn: Lập trình Cơ bản
5. Đệ qui
5.1. Khái niệm
Một hàm được gọi có tính đệ qui nếu trong thân của hàm đó có lệnh gọi lại
chí nh nó một cách tường minh hay tiềm ẩn.
5.2. Ví dụ
Ví dụ 1: Tính tổng S = 1 + 2 + 3 + … + n (n là số nguyên dương, được
nhập từ bàn phím).
Thuật toán giải bằng phương pháp đệ quy như sau:
long TongS (int n)
{
if(n==0)
return 0;
return (TongS(n-1) + n);
}
Ví dụ 2: Tính giá trị của n! (với n được nhập từ bàn phím)
Thuật toán giải bằng phương pháp đệ quy như sau:
long GiaiThua (int n)
{
if(n==0)
return 1;
return (GiaiThua(n-1) * n);
}
Bài tập luyện:
Bài 1: Viết chương trình tính diện tích và chu vi của hình chữ nhật với
chiều dài và chiều rộng được nhập từ bàn phím.
Bài 2: Viết chương trình tính diện tích và chu vi hình tròn với bán kính
được nhập từ bàn phím.
Bài 3: Nhập số nguyên dương n (n>0). Liệt kê tất cả các số nguyên tố nhỏ
hơn n.
Bài 4: Viết chương trình tính tiền lương ngày cho công nhân, cho biết
trước giờ vào ca, giờ ra ca của mỗi người.
Giả sử rằng:
- Tiền trả cho mỗi giờ trước 12 giờ là 6000đ và sau 12 giờ là 7500đ.
45
Đề cương môn: Lập trình Cơ bản
- Giờ vào ca sớm nhất là 6 giờ sáng và giờ ra ca trễ nhất là 18 giờ (Giả sử
giờ nhập vào nguyên).
Bài 5: Nhập vào 3 số thực a, b, c và kiểm tra xem chúng có thành lập
thành 3 cạnh của một tam giác hay không? Nếu có hãy tính diện tích, chiều dài
mỗi đường cao của tam giác và in kết quả ra màn hình.
Biết rằng:
- Công thức tính diện tích s = sqrt(p*(p-a)*(p-b)*(p-c) )
- Công thức tính các đường cao: ha = 2s/a, hb=2s/b, hc=2s/c.
(Với p là nữa chu vi của tam giác).
Bài 6: Nhập vào 6 số thực a, b, c, d, e, f . Giải hệ phương trình sau :
ax + by = c
dx + ey = f
Bài 7: Viết chương trình nhập 2 số nguyên dương a, b. Tìm USCLN và
BSCNN của hai số nguyên đó.
Bài 8: Viết chương trình tính tổng nghịch đảo của n giai thừa.
Bài 9: Viết chương trình nhập số nguyên dương n gồm k chữ số (0<k<=5),
tính tổng các ước số dương của n.
Ví dụ: Nhập n=6
Tổng các ước số từ 1 đến n: 1+2+3+6=12.
46
Đề cương môn: Lập trình Cơ bản
Chương 6. Mảng
1. Khái niệm
Mảng thực chất là một biến được cấp phát bộ nhớ liên tục và bao gồm
nhiều biến thành phần.
Các thành phần của mảng là tập hợp các biến có cùng kiểu dữ liệu và
cùng tên. Do đó để truy xuất các biến thành phần, ta dùng cơ chế chỉ mục (chỉ
số).
2. Khai báo mảng
2.1. Khai báo mảng
Cú phá p:
< Kiểu_mảng> < Tên_mảng > [ < Số_phần_tử_tối_đa> ] ;
Trong đó:
Kiểu_mảng: đây là kiểu của mảng, là tên một kiểu dữ liệu đã tồn tại, có
thể là kiểu chuẩn hoặc kiểu dữ liệu do người lập trình định nghĩa .
Tên_mảng: là tên của mảng, do người lập trình đặt, theo quy tắc về tên
của C/C++.
Số_phần_tử: là hằng (hoặc biểu thức hằng) nguyên, dương là số phần tử
của mảng.
Ví dụ:
int a[10]; // Khai báo mảng a, có số phần tử tối đa là 10, kiểu dữ liệu của
mảng a là int.
float MT[20]; //Khai báo mảng MT, có số phần tử tối đa là 20, kiểu dữ liệu
của mảng MT là float.
2.2. Truy xuất đến các phần tử của mảng
Cú phá p :
tên_mảng [chỉ_số]
ví dụ a[1], MT[3];
chỉ_số là số thứ tự của phần tử trong mảng, các phần tử của mảng được
đánh chỉ số bắt đầu từ 0. Với mảng có n phần tử thì các phần tử của nó có chỉ số
là 0, 1,..,n-1.
47
Đề cương môn: Lập trình Cơ bản
3. Khởi tạo mảng
Các phần tử của mảng cũng như các biến đơn, chúng ta có thể khởi tạo
giá trị ban đầu cho chúng trên dòng định nghĩa mảng (gọi là khởi đầu) với cú
phá p sau:
Kiểu_mảng tên_mảng [ số_phần_tử ] = {gt_0, gt_1,..,gt_k};
hoặc
Kiểu_mảng tên_mảng [ ] = {gt_0, gt_1,..,gt_k};
Trong đó các thành phần Kiểu_mảng , tên_mảng, số_phần_tử như trong
phần khai báo mảng. gt_0, gt_1,.., gt_k là các giá trị khởi đầu (gọi là bộ khởi đầu)
cho các phần tử tương ứng của mảng, tức là gán tuần tự các giá trị trong bộ khởi
đầu cho các phần tử của mảng từ trái qua phải.
Trong dạng thứ nhất, số giá trị trong bôn khởi đầu chỉ có thể <= số phần tử
của mảng (k ≤ số_phần_tử). Khi đó những phần tử mảng thừa ra (không có giá
trị khởi đầu) sẽ tự động được gán bằng 0 (trong trường hợp mảng số, nếu là con
trỏ sẽ là NULL (rỗng) ).
Ví dụ:
int a[3] ={ 1, 3, 4}; thì giá trị của a[0] là 1, a[1] là 3, a[2] là 4.
int b[5] ={1,2}; thì giá trị của b[0] là 1, b[1] là 2, b[3]=b[4] là 0.
với mảng các ký tự hoặc xâu ký tự thì có hai cách khởi đầu như sau:
char c[4] ={‘a’,’b’,’c’ }; // c[0] là ‘a’, c[1] là ‘b’, c[2] là ‘c’, c[3] là ‘\0’
char s[10] =”ABC”; // tương đương với char s[10] ={‘A’,’B’,’C’,’\0’}
(nếu số giá trị trong bộ khởi đầu > số phần tử mảng chương trình dịch sẽ
báo lỗi)
Trong dạng thứ hai, chúng ta không xác định số phần tử của mảng, trong
trường hợp này chương trình biên dịch sẽ tự động xác định kích thước (số phần
tử) của mảng theo số giá trị trong bộ khởi đầu.
Ví dụ:
int a[] ={1,3,4};
thì a là mảng có 3 phần tử, giá trị của a[0] là 1, a[1] là 3, a[2] là 4.
Ví dụ 1: Viết chương trình nhập vào một mảng A có n phần tử kiểu
nguyên, n<=20, n được nhập từ bàn phím. In các phần tử của mảng theo trật tự
xuôi, ngược (theo trật tự chỉ số).
#include<iostream.h>
main()
{
48
Đề cương môn: Lập trình Cơ bản
const max = 20;
int n, i, a[max];
do {
cout<<"Nhap vao so phan tu cua mang: "; cin>>n;
}
while (n < 1 || n > max);
cout<<"Nhap vao mang a: "<<endl;
for (i = 0; i<n; i++)
cin>>a[i];
cout<<"Cac phan tu mang theo TT xuoi la: ";
for (i = 0; i<n; i++)
cout<<a[i]<<"\t";
cout<<endl;
cout<<"Cac phan tu mang theo TT nguoc la: ";
for (i = n-1;i>=0; i--)
cout<<a[i]<<"\t";
return 0;
}
Ví dụ 2: Nhập vào 2 mảng A và B, có số phần tử tối đa là 10. Tính tổng
của A và B.
//Tinh tong cua C = A + B
#include<iostream.h>
main()
{
const max = 10;
int n, i, a[max], b[max], c[max];
do {
cout<<"Nhap vao so phan tu cua mang: "; cin>>n;
}
while (n < 1 || n > max);
cout<<"Nhap vao so PT mang a: "<<endl;
for (i = 0; i<n; i++)
cin>>a[i];
cout<<"Nhap vao so PT mang b: "<<endl;
for (i = 0; i<n; i++)
49
Đề cương môn: Lập trình Cơ bản
cin>>b[i];
cout<<"Tong C = A + B = ";
for (i = 0; i< n; i++)
c[i] = a[i] + b[i];
for (i = 0; i<n; i++)
cout<<c[i]<<"\t";
return 0;
}
4. Dùng mảng làm tham số
Vào một lúc nào đó có thể chúng ta cần phải truyền một mảng tới một hàm
như là một tham số. Trong C++, việc truyền theo tham số giá trị một khối nhớ là
không hợp lệ, ngay cả khi nó được tổ chức thành một mảng. Tuy nhiên chúng ta
lại được phép truyền địa chỉ của nó, việc này cũng tạo ra kết quả thực tế giống
thao tác ở trên nhưng lại nhanh hơn nhiều và hiệu quả hơn.
Để có thể nhận mảng là tham số thì điều duy nhất chúng ta phải làm khi
khai báo hàm là chỉ định trong phần tham số kiểu dữ liệu cơ bản của mảng, tên
mảng và cặp ngoặc vuông trống. Ví dụ, hàm sau:
void procedure (int arg[])
nhận vào một tham số có kiểu "mảng của char" và có tên arg. Để truyền
tham số cho hàm này một mảng được khai báo:
int myarray [40];
chỉ cần gọi hàm như sau:
procedure (myarray);
Ví dụ 1:
#include <iostream.h>
void printarray (int arg[], int length) {
for (int n=0; n<length; n++)
cout << arg[n] << " ";
cout << "\n";
}
int main ()
{
int firstarray[] = {5, 10, 15};
int secondarray[] = {2, 4, 6, 8, 10};
50
Đề cương môn: Lập trình Cơ bản
printarray (firstarray,3);
printarray (secondarray,5);
return 0;
}
Ví dụ 2: Tính tổng C = A + B
#include <iostream.h>
void nhapmang(int a[], int n);
void inmang(int a[], int n);
void tong(int A[],int B[],int C[],int n);
void main(){
const int max = 20;
int A[max], B[max],C[max];
int n;
do{
cout<<"\nNhap so phan tu mang = ";
cin>>n;
} while(n<1 || n>max);
cout<<"\nNhap A \n";
nhapmang(A,n);
cout<<"\nNhap B \n";
nhapmang(B,n);
tong(A,B,C,n);
cout<<"\nmang A: ";
inmang(A,n);
cout<<"\nmang B: ";
inmang(B,n);
cout<<"\nmang C: ";
inmang(C,n);
}
void nhapmang(int a[], int n){
int i;
cout<<"\nNhap mang co so phan tu: "<<n;
for(i=0; i<n; i++)
{
cout<<"\nPhan tu thu"<<"["<<i<<"]";
51
Đề cương môn: Lập trình Cơ bản
cin>>a[i];
}
}
void inmang(int a[], int n){
int i;
for(i=0; i<n; i++)
cout<<a[i];
}
void tong(int A[],int B[],int C[],int n){
int i;
for (i = 0; i<n; i++)
C[i]=A[i]+B[i];
}
5. Với mảng hai chiều
5.1. Định nghĩa
Mảng hai chiều có thể hiểu như bảng gồm các dòng các cột, các phần tử
thuộc cùng một kiểu dữ liệu nào đó. Mảng hai chiều được định nghĩa như sau.
Cú phá p:
Kiểu_mảng tên_mảng [sd][sc];
Trong đó:
- Kiểu_mảng: đây là kiểu của mảng, là tên một kiểu dữ liệu đã tồn tại, có
thể là kiểu chuẩn hoặc kiểu dữ liệu do người lập trình định nghĩa.
- tên_mảng: là tên của mảng, do người lập trình đặt, theo quy tắc về tên
của C/C++.
- sd, sc: là hằng (hoặc biểu thức hằng) nguyên, dương tương ứng là số
dò ng và số cột mảng, số phần tử của mảng sẽ là sd*sc.
Ví dụ:
int a[2][5]; // a là mảng số nguyên có 2 dòng, 5 cột (có 10 phần tử).
float D[3][10]; // D là mảng số thực có 3 dòng, 10 cột (có 30 phần tử).
char DS[5][30]; // DS là mảng kí tự có 5 dòng, 30 cột.
5.2. Truy xuất các phần tử mảng hai chiều
Một phần tử của mảng 2 chiều được xác định qua tên (tên của mảng) và
chỉ số dòng, chỉ số cột của nó trong mảng theo cú pháp sau:
52
Đề cương môn: Lập trình Cơ bản
tên_mảng [csd][csc]
Với csd là số nguyên xác định chỉ số dòng và csc là số hiệu cột cũng như
trong mảng 1 chiều các chỉ số được tính từ 0. Tức là 0 ≤csd ≤sd-1 và 0≤csc≤sc-1.
5.3. Khởi tạo giá trị mảng hai chiều
Các phần tử mảng hai chiều cũng có thể được khởi đầu giá trị theo cú
pháp (4 dạng sau):
+ Kiểu_mảng tên_mảng [sd][sc]={{kđ_dòng_1},{ kđ_dòng_2},..,{ kđ_dòng_k}};
+ Kiểu_mảng tên_mảng [ ][sc] = {{kđ_dòng_1},{ kđ_dòng_2},..,{ kđ_dòng_k}};
+ Kiểu_mảng tên_mảng [sd][sc] = { gt_1, gt_2,...,gt_n };
+ Kiểu_mảng tên_mảng [ ][sc] = { gt_1, gt_2,...,gt_n };
Cú pháp trên có thể giải thích như sau:
Dạng 1: có k bộ giá trị sẽ được gán cho k dòng đầu tiên của mảng (k ≤ sd
), với mỗi dòng (được coi như mảng một chiều) được khởi tạo giá trị như mảng
một chiều:
dòng thứ nhất được khởi đầu bởi {kđ_dòng_1}, dòng thứ hai được khởi
đầu bởi {kđ_dòng_1},.., dòng thứ k được khởi đầu bởi {kđ_dòng_k}. Yêu cầu k ≤
sd, ngược lại chương trình sẽ báo lỗi.
Cá c dò ng cuối của mảng nếu không có bộ khởi đầu tương ứng thì sẽ được
tự động gán giá trị 0 (hoặc NULL nếu là con trỏ).
Dạng 2: (không xác định số dòng) chương trình dịch sẽ tự động ấn định số
dòng của mảng bằng số bộ khởi đầu ( = k), sau đó thực hiện khởi đầu như dạng
1.
Dạng 3: n giá trị trong bộ khởi đầu được gán cho các phần tử mảng theo
cá ch: sc giá trị đầu tiên trong các giá trị khởi đầu (gt_1,..,gt_sc) được gán tuần tự
cho các phần tử của dòng thứ nhất trong mảng, sc phần tử kế tiếp sẽ gán cho
các phần tử ở dòng thứ 2,... nếu phần tử nào của mảng không có giá trị khởi đầu
sẽ được gán 0 (con trỏ là NULL) - với điều kiện n ≤ sd*sc, ngược lại là lỗi.
Dạng 4: số dòng của mảng sẽ được chương trình tự tính theo số giá trị
trong bộ khởi đầu theo công thức sd = (n/sc) +1, và khởi đầu như dạng 3.
Ví dụ 1: int a[3][2] = {{1,2},{3},{4,5}}; thì các phần tử của a như sau:
a[0][0]=1, a[0][1]=2, a[1][0]=3, a[1][1]= 0, a[2][0]=4,a[2][1]=5;
Ví dụ 2: int b[ ][2] = {{1,2},{3},{4,5}};
thì là mảng 3 dòng, 2 cột các phần tử của b như sau:
b[0][0]=1, b[0][1]=2, b[1][0]=3,b[1][1]= 0, b[2][0]=4,b[2][1]=5;
Ví dụ 3: int c[ ][2] = {1,2,3,4,5};
53
Đề cương môn: Lập trình Cơ bản
thì số dòng của c là mảng 5/2 +1 =3 dòng, các phần tử của a như sau:
c[0][0]=1, c[0][1]=2, c[1][0]=3,c[1][1]= 4, b[2][0]=5,b[2][1]=0;
5.4. Ví dụ
Viết chương trình nhập vào mảng A(n, m) với 1<=n,m<=5 các số nguyên.
Yêu cầu:
+ In mảng A ra màn hình.
+ In ra màn hình các phần tử chẵn của mảng.
+ In ra màn hình các phần tử lẻ của mảng.
#include<iostream.h>
main()
{
const max = 5;//Kich thuoc toi da
int a[max][max];
int n,m,i,j;
do {
cout<<"Nhap so dong cua mang = ";cin>>n;
cout<<"Nhap so cot cua mang = "; cin>>m;
}
while (n<1 || n>max || m<1 || m>max);
cout<<"Nhap cac gia tri cua mang: "<<endl;
for (i = 0; i<n; i++)
for (j = 0; j<m; j++)
{
cin>>a[i][j];
}
cout<<"Cac PT cua mang la: "<<" ";
for (i = 0; i<n; i++)
{
cout<<endl;
for (j = 0; j<m; j++)
cout<<a[i][j]<<" ";
}
cout<<"\n Cac PT chan cua mang la: ";
for (i = 0; i<n; i++)
54
Đề cương môn: Lập trình Cơ bản
for (j = 0; j<m; j++)
{if (a[i][j] % 2 == 0)
cout<<a[i][j]<<"\t";}
cout<<"\n Cac PT le cua mang la: ";
for (i = 0; i<n; i++)
for (j = 0; j<m; j++)
{if (a[i][j] % 2 != 0)
cout<<a[i][j]<<"\t";}
}
Bài tập luyện
Bài 1: Viết chương trình nhập vào mảng N số nguyên (N<100)
+ In các phần tử trong mảng
+ Đếm và in số phần tử chẵn trong mảng
+ Đếm và in số phần tử lẻ trong mảng
Bài 2: Viết chương trình nhập vào mảng N số nguyên (N<100)
+ Tính tổng và in tất cả các phần tử chẵn trong mảng
+ Tính tổng và in tất cả các phần tử lẻ trong mảng
+ Tính tổng và in tất cả các phần tử âm trong mảng
+ Tính tổng và in tất cả các phần tử lẻ không âm trong mảng
Bài 3:Viết chương trình nhập vào mảng N số nguyên (N<100)
+ Kiểm tra mảng có sắp thứ tự tăng không ?
+ Kiểm tra mảng có sắp thứ tự giảm không ?
+ Sắp xếp mảng theo thứ tự tăng (nếu mảng chưa được sắp tăng)
+ Sắp xếp mảng theo thứ tự giảm (nếu mảng chưa được sắp thứ tự giảm)
55
Đề cương môn: Lập trình Cơ bản
Chương 7. Con trỏ
1. Khái niệm
Con trỏ là một biến dùng để chứa địa chỉ. Vì có nhiều loại địa chỉ nên cũng
có nhiều kiểu con trỏ tương ứng. Kiểu con trỏ int dùng để chứa địa chỉ biến kiểu
int. Tương tự ta có con trỏ kiểu float, kiểu double,…
Cũng như với 1 biến bất kỳ nào khác, con trỏ cần được khai báo trước khi
sử dụng.
2. Toán tử lấy địa chỉ (&)
Địa chỉ: Khi khai báo biến, máy sẽ cấp phát cho biến một khoảng nhớ. Địa
chỉ của biến là số thứ tự của byte đầu tiên trong một dãy các byte liên tiếp mà
máy dành cho biến (các byte được đánh số từ 0).
Máy phân biệt các loại địa chỉ như các biến. Ví dụ như: địa chỉ kiểu int,
kiểu float,…
Vào thời điểm mà chúng ta khai báo một biến thì nó phải được lưu trữ
trong một vị trí cụ thể trong bộ nhớ. Chúng ta không quyết định nơi nào biến đó
được đặt, điều đó làm tự động bởi trình biên dịch và hệ điều hành. Nhưng khi hệ
điều hành đã gán một địa chỉ cho biến thì chúng ta cần biết biến đó được lưu trữ
ở đâu. Điều này được thực hiện bằng cách đặt trước tên biến một dấu và (&).
Ví dụ: x = &y;
Giải thích: Gán cho biến x địa chỉ của biến y vì khi đặt trước tên biến y dấu
& ta không nói đến nội dung của biến nữa mà chỉ nói đến địa chỉ của nó trong bộ
nhớ.
Giả sử biến y được đặt trọng ô nhớ có địa chỉ là 1202 và có các câu lệnh
như sau:
y = 30;
z = y;
x = &y;
Kết quả: z = 30; x = 1202;
Chú ý: Có thể định nghĩa con trỏ như sau: Những biến lưu trữ địa chỉ của
biến khác được gọi là con trỏ. Ở ví dụ trên, biến x là con trỏ.
56
Đề cương môn: Lập trình Cơ bản
3. Toán tử tham chiếu (*)
Bằng cách sử dụng con trỏ chúng ta có thể truy xuất trực tiếp đến giá trị
được lưu trữ trong biến được trỏ bởi nó bằng cách đặt trước tên biến con trỏ một
dấu (*).
Ví dụ: Giả sử biến y được đặt trong ô nhớ có địa chỉ là 1202 và có các câu
lệnh như sau:
y = 30;
z = y;
x = &y;
t = *y;
Kết quả: biến t sẽ mang giá trị là 30.
4. Khai báo biến kiểu con trỏ.
Vì con trỏ có khả năng tham chiếu trực tiếp đến giá trị mà chúng trỏ tới nên
cần thiết phải chỉ rõ kiểu dữ liệu nào mà một biến con trỏ trỏ tới khi khai báo.
Cấu trúc khai báo:
Kiểu_dữ_liệu
Chú ý: kiểu dữ liệu ở đây là kiểu dữ liệu được trở tới, không phải là kiểu
của bản thân con trỏ.
*Tên_con_trỏ;
Ví dụ:
int *x; //Khai báo con trỏ x kiểu int
float *t; //Khai báo con trỏ t kiểu float
Chú ý: dấu (*) khi khai báo biến kiểu con trỏ chỉ có nghĩa rằng đó là một
con trỏ, không liên quan đến toán tử tham chiếu.
Ví dụ đơn giản về sử dụng con trỏ: khai báo, sử dụng toán tử tham chiếu,
toán tử lấy địa chỉ:
#include<iostream.h>
main()
{
int x1 = 5, x2 = 15;
int *y;
y = &x1;
*y = 10;
y = &x2;
*y = 20;
57
Đề cương môn: Lập trình Cơ bản
cout<<“Gia tri 1 = “<<x1<<“\n Gia tri 2 = “<<x2;
return 0;
}
Kết quả: x1 = 10; x2 = 20;
5. Cá c phé p toá n
Có bốn nhóm phép toán liên quan đến con trỏ và địa chỉ là: phép gán,
phép tăng giảm địa chỉ, phép truy nhập bộ nhớ và phép so sánh. Kiểu con trỏ và
kiểu địa chỉ có vai trò quan trọng trong các phép toán nói trên.
5.1. Phé p gá n
phé p gá n chỉ nên thực hiện phép gán cho con trỏ cùng kiểu. Muốn gán các
con trỏ khác kiểu phải dùng phép ép kiểu như ví dụ sau:
int
x;
char *pc;
pc = (char*)(&x); //ép kiểu
5.2. Phép tăng giảm địa chỉ
//Giải thích phép tăng giảm địa chỉ qua các ví dụ.
Ví dụ 1: Các câu lệnh: float x[30], *px;
px = &x[10];
Cho biết px là con trỏ float trỏ tới phần tử x[10]. Kiểu địa chỉ float là kiểu địa
chỉ 4 byte (mỗi giá trị float chứa trong 4 byte), nên các phép tăng giảm địa chỉ
được thực hiện trên 4 byte. Nói cách khác:
px + i trỏ tới phần tử x[10+i];
px – i trỏ tới phần tử x[10-i];
Ví dụ 2: float b[40][50];
b trỏ tới đầu dòng thứ nhất (phần tử b[0][0];
b + 1 trỏ tới đầu dòng thứ hai (phần tử b[1][0];
….
5.3. Phép truy nhập bộ nhớ
Có nguyên tắc là con trỏ float truy nhập tới 4 byte, con trỏ int truy nhập 2
byte, chon trỏ char truy nhập 1 byte.
Ví dụ:
float *pf;
int *pi;
58
Đề cương môn: Lập trình Cơ bản
char *pc;
Khi đó: Nếu pf trỏ đến byte thứ 10001, thì *pf biểu thị vùng nhớ 4 byte liên
tiếp từ byte 10001 đến 10004.
Nếu pi trỏ đến byte thứ 10001, thì *pi biểu thị vùng nhớ 2 byte là 10001 và
10002.
Nếu pc trỏ đến byte thứ 10001, thì *pc biểu thị vùng nhớ 1 byte là byte
10001.
5.4. Phé p so sá nh
Cho phép so sánh các con trỏ cùng kiểu, ví dụ nếu p1 và p2 là 2 con trỏ
float thì :
p1<p2 nếu địa chỉ p1 trỏ tới thấp hơn địa chỉ p2 trỏ tới.
p1=p2 nếu địa chỉ p1 trỏ tới bằng địa chỉ p2 trỏ tới.
p1<p2 nếu địa chỉ p1 trỏ tới cao hơn địa chỉ p2 trỏ tới.
6. Con trỏ hằng
Ví dụ có khai báo sau:
int a[20];
int *p;
Lệnh sau sẽ hợp lệ: p = a;
ở đây p và a là tương đương và chúng có cùng thuộc tính, sự khác biệt
duy nhất là chúng ta có thể gán một giá trị khác cho con trỏ p trong khi a luôn trỏ
đến phần tử đầu tiên trong số 20 phần tử kiểu int mà nó được định nghĩa. Vì vậy
không giống như p – đó là một biến con trỏ bình thường, a, là một con trỏ hằng.
Lệnh gán sau đây là không đúng: a = p; bởi vì a là một mảng (con trỏ hằng) và
không có giá trị nào có thể được gán cho các hằng.
7. Con trỏ mảng
Trong thực tế, tên của một mảng tương đương với địa chỉ phần tử đầu tiên
của nó, giống như một con trỏ tương đương với địa chỉ của phần tử đầu tiên mà
nó trỏ tới, vì vậy thực tế chúng hoàn toàn như nhau. Ví dụ, cho hai khai báo sau:
int numbers[20];
int * p;
Lệnh sau sẽ hợp lệ:
p = numbers;
59
Đề cương môn: Lập trình Cơ bản
Ở đây p và numbers là tương đương và chúng có cũng thuộc tính, sự khác
biệt duy nhất là chúng ta có thể gán một giá trị khác cho con trỏ p trong khi
numbers luôn trỏ đến phần tử đầu tiên trong số 20 phần tử kiểu int mà nó được
định nghĩa với. Vì vậy, không giống như p - đó là một biến con trỏ bình thường,
numbers là một con trỏ hằng. Lệnh gán sau đây là không hợp lệ:
numbers = p;
bởi vì numbers là một mảng (con trỏ hằng) và không có giá trị nào có thể
được gán cho các hằng.
Vì con trỏ cũng có mọi tính chất của một biến nên tất cả các biểu thức có
con trỏ trong ví dụ dưới đây là hoàn toàn hợp lệ:
#include <iostream.h>
int main ()
{
int a[5];
int * p;
p = a; *p = 10;
p++; *p = 20;
p = &a[2]; *p = 30;
p = a + 3; *p = 40;
p = a; *(p+4) = 50;
for (int n=0; n<5; n++)
cout << a[n] << ", ";
return 0;
}
Bài tập luyện tập:
Bài 1: Tính tổng các phần tử của mảng 1 chiều. Các phần tử được nhập từ
bàn phí m.
Bài 2:
8. Khởi tạo con trỏ
Khi khai báo con trỏ có thể chúng ta sẽ muốn chỉ định rõ ràng chúng sẽ trỏ
tới biến nào:
int number;
int *tommy = &number;
là tương đương với:
60
Đề cương môn: Lập trình Cơ bản
int number;
int *tommy;
tommy = &number;
Trong một phép gán con trỏ chúng ta phải luôn luôn gán địa chỉ mà nó trỏ
tới chứ không phải là giá trị mà nó trỏ tới. Cần phải nhớ rằng khi khai báo một
biến con trỏ, dấu sao (*) được dùng để chỉ ra nó là một con trỏ, và hoàn toàn
khác với toán tử tham chiếu. Đó là hai toán tử khác nhau mặc dù chúng được
viết với cùng một dấu. Vì vậy, các câu lệnh sau là không hợp lệ:
int number;
int *tommy;
*tommy = &number;
Như đối với mảng, trình biên dịch cho phép chúng ta khởi tạo giá trị mà
con trỏ trỏ tới bằng giá trị hằng vào thời điểm khai báo biến con trỏ:
char * terry = "hello";
trong trường hợp này một khối nhớ tĩnh được dành để chứa "hello" và một
con trỏ trỏ tới kí tự đầu tiên của khối nhớ này (đó là kí tự h') được gán cho terry.
Nếu "hello" được lưu tại địa chỉ 1702, lệnh khai báo trên có thể được hình dung
như thế này:
Cần phải nhắc lại rằng terry mang giá trị 1702 chứ không phải là 'h' hay
"hello".
Biến con trỏ terry trỏ tới một xâu kí tự và nó có thể được sử dụng như là
đối với một mảng (hãy nhớ rằng một mảng chỉ đơn thuần là một con trỏ hằng). Ví
dụ, nếu chúng ta muốn thay kí tự 'o' bằng một dấu chấm than, chúng ta có thể
thực hiện việc đó bằng hai cách:
terry[4] = ‘!’;
*(terry+4) = '!';
Hã y nhớ rằng viết terry[4] là hoàn toàn giống với viết *(terry+4) mặc dù
biểu thức thông dụng nhất là cái đầu tiên. Với một trong hai lệnh trên xâu do terry
trỏ đến sẽ có giá trị như sau:
61
Tải về để xem bản đầy đủ
Bạn đang xem 20 trang mẫu của tài liệu "Đề cương môn Lập trình Cơ bản (Phần 2)", để tải tài liệu gốc về máy hãy click vào nút Download ở trên
File đính kèm:
de_cuong_mon_lap_trinh_co_ban_phan_2.pdf

