Professional Documents
Culture Documents
Hàm
1
Review :
• Giai phong xong, van truy cap duoc ?
• Tai cap phat bo nho ( C++)
float *m; int n; // nhap n
m= new float [n];
float *p;
// nhap lai kt moi k
p=new float [k];
for(int i=0; i<k;i++) p[i] =(i<n)?m[i]:0;
delete []m;
m=p;
n=k;
2
3,4 D ?
int alloc3D(float *** &Cub, int &m, int &r, int &c)
{
int m2,r2,c2;
float ***Cub2;
// nhap m2,r2,c2
Cub2= new float ** [m2];
if( cub2==NULL) return 0;
for (int i=0;i<m2;i++) {
Cub2[i]=new float *[r2];
if (Cub2[i]=NULL) return 0;
for(int j=0; j<n;j++) {
Cub2[i][j] = new float [c2]; if (Cub2[i][j]=NULL) return 0;
for(int k=0;k<c2;k++) Cub2[i,][j][k] =(i<m) && (j<r) && (k<c)?Cub[i][j][k]:0;
}
}
// giai phong Cub
Cub=cub2;
m=m2; r=r2; c=c2;
return 1;
} 3
Stack:
Vùng nhớ thấp
Stack pointer (SP)
( top of stack)
Local array “buffer2”
Overwrite
Frame pointer (FP) –
Saved (old) frame pointer dùng để truy cập
local variables &
Return address in main() parameters
1
2
Vùng nhớ cao Stack có thể mở rộng,
3
tùy hàm được gọi
4
Kq ???
void main() { f(“123”,”456”); }
void f(char a[8], char b[16]) {
char buf1[5];
char buf2[10];
strcpy(buf1, "This is a very long string!!!!!!!");
strcpy(a,buf2);
puts(b);
}
5
int tong (int a, int b) {
return a+b;
}
int y ,x=10;
y=tong(x,x++);
y=?
6
Nội dung
1. Truyền tham trị, tham biến và tham số ngầm định
2. Đa năng hóa hàm (function overload)
3. Con trỏ hàm và tham số hóa hàm
4. Khái quát hóa hàm (function templates)
5. Biểu thức lamda và hàm nặc danh
7
MODULATION
• Function abstraction
• Ctcon
• 2 loai : procedure va function
• Cau truc ct :
• Phan cap
• Dong dang
• Cach truyen tham so
• Truyen bien, tham chieu ( c++, pascal, vb,…)
• Truyen tham tri, hay ban sao gia tri (C )
• Bien va pham vi
• Tong the
• Cuc bo
8
Khái niệm về hàm
• Là một nhóm các khai báo và các câu lệnh được gán
một tên gọi
• Đây là khối lệnh được đặt tên nên sử dụng thuận tiện,
hiệu quả
• Hàm thường trả về một giá trị
• Là một chương trình con
• Khi viết chương trình C/C++ ta luôn định nghĩa một hàm
có tên là main
• Phía trong hàm main ta có thể gọi các hàm khác
• Bản thân các hàm này lại có thể gọi các hàm khác ở
trong nó và cứ tiếp tục như vậy…
9
Cú pháp
return-type name(argument-list){
local-declarations
statements
return return-value;
}
10
Ví dụ: Square
double square(double a)
{ Đây là định nghĩa hàm ngoài
return a * a; hàm main
}
int main(void)
{
double num = 0.0, sqr = 0.0;
printf("enter a number\n");
scanf("%lf",&num);
return 0;
}
11
Tại sao cần sử dụng hàm?
• Chia vấn đề thành nhiều tác vụ con
• Dễ dàng hơn khi giải quyết các vấn đề phức tạp
• Tổng quát hóa được tập các câu lệnh hay lặp
lại
• Ta không phải viết cùng một thứ lặp đi lặp lại
nhiều lần
• printf và scanf là ví dụ điển hình…
• Hàm giúp chương trình dễ đọc và bảo trì hơn
nhiều
12
Hàm và truyền tham số
• Trong C: tên hàm phải là duy nhất, lời gọi hàm phải có các
đối số đúng bằng và hợp tương ứng về kiểu với tham số trong
đn hàm. C chỉ có duy nhất 1 cách truyền tham số: tham trị (kể
cả dùng địa chỉ cũng là truyền tham trị !!!).
• Trong C++: ngoài truyền tham trị, C++ còn cho phép truyền
tham chiếu. Tham số trong C++ còn có kiểu tham số ngầm
định (default parameter), vì vậy số đối số trong lời gọi hàm có
thể ít hơn tham số định nghĩa. Đồng thời C++ còn có cơ chế
đa năng hóa hàm, vì vậy tên hàm không phải duy nhất.
13
Truyền tham chiếu
14
Truyền tham chiếu
15
???
void vidu( int x,int &y, int *z)
{ x+=10;
y+=15;
*z +=2;
}
void main() {
int x=2,y=3,z=4;
vidu(y,z,&x);
printf(“\n x= %d y= %d z=%d”, x,y,z);
}
16
Hàm trả về 1 tham chiếu
#include <iostream>
Khi một hàm using namespace std;
int X = 4;
trả về một int & MyFunc(){
tham chiếu, return X;
}
chúng ta có thể int main(){
cout << "X=" << X << endl;
gọi hàm ở phía
cout << "X=" << MyFunc() << endl;
bên trái của MyFunc() = 20; // ~X=20
cout << "X=" << X << endl;
một phép gán. return 0;
}
17
Tham số ngầm định
• Định nghĩa các giá trị tham số mặc định cho các hàm
• Ví dụ
void MyDelay(long Loops = 1000){
18
Tham số ngầm định
• Nếu có prototype, các tham số có giá trị mặc định chỉ được cho
trong prototype của hàm và không được lặp lại trong định nghĩa
hàm.
• Một hàm có thể có nhiều tham số có giá trị mặc định. Các tham số
có giá trị mặc định cần phải được nhóm lại vào các tham số cuối
cùng (hoặc duy nhất) của một hàm. Khi gọi hàm có nhiều tham số
có giá trị mặc định, chúng ta chỉ có thể bỏ bớt các tham số theo
thứ tự từ phải sang trái và phải bỏ liên tiếp nhau
• Ví dụ:
int MyFunc(int a = 1, int b, int c = 3, int d = 4); // ✖
19
Đa năng hóa hàm (Overloading)
• Cung cấp nhiều hơn một định nghĩa cho tên hàm đã cho
trong cùng một phạm vi.
• Trình biên dịch sẽ lựa chọn phiên bản thích hợp của hàm hay
toán tử dựa trên các tham số mà nó được gọi.
20
#include <iostream>
#include <math.h>
Using namespace std;
long abs(long X){
return labs(X);
}
double abs(double X){
return fabs(X);
}
int main(){
int X = -7;
long Y = 200000l;
double Z = -35.678;
cout << "Tri tuyet doi cua so nguyen " << X <<" la " << abs(X) << endl;
cout << "Tri tuyet doi cua so nguyen " << Y <<" la " << abs(Y) << endl;
cout << "Tri tuyet doi cua so thuc " << Z <<" la " << abs(Z) << endl;
return 0;
}
21
Đa năng hoá toán tử
• Định nghĩa lại chức năng của các toán tử đã
có sẵn
Thể hiện các phép toán một cách tự nhiên hơn
• Ví dụ: thực hiện các phép cộng, trừ số phức
Trong C: Cần phải xây dựng các hàm
AddSP(), TruSP()
Không thể hiện được phép cộng và trừ cho các
biểu thức như: a=b+c-d+e+f-h-k
a=s(s(a(a(s(a(b,c) ,c),e),f
),h),k);
22
#include <stdio.h>
struct SP {
double real;
double img;
};
SP SetSP(double real, double img);
SP AddSP(SP C1,SP C2);
SP SubSP(SP C1,SP C2);
void DisplaySP(SP C);
int main(void) {
SP C1,C2,C3,C4;
C1 = SetSP(1.0,2.0);
C2 = SetSP(-3.0,4.0);
cout << "\nSo phuc thu nhat:"; DisplaySP(C1);
cout << "\nSo phuc thu hai:"; DisplaySP(C2);
C3 = AddSP(C1,C2);
C4 = SubSP(C1,C2);
cout << "\nTong hai so phuc nay:"; DisplaySP(C3);
cout << "\nHieu hai so phuc nay:"; DisplaySP(C4);
return 0;
}
23
SP SetSP(double real, double img) {
SP tmp;
tmp.real = real; tmp.img = img;
return tmp;
}
SP AddSP(SP C1,SP C2) {
SP tmp;
tmp.real = C1.real + C2.real;
tmp.img = C1.img + C2.img;
return tmp;
}
SP SubSP(SP C1,SP C2) {
SP tmp;
tmp.real = C1.real - C2.real;
tmp.img = C1.img - C2.img;
return tmp;
}
void DisplaySP(SP C) {
cout << C.real << " i " << C.img;
}
24
C++
• C++ cho phép chúng ta có thể định nghĩa lại chức năng của các
toán tử đã có sẵn một cách tiện lợi và tự nhiên. Điều này gọi là
đa năng hóa toán tử.
• Một hàm định nghĩa một toán tử có cú pháp sau:
data_type operator operator_symbol ( parameters )
{ ………………………………
}
Trong đó:
• data_type: Kiểu trả về.
• operator_symbol: Ký hiệu của toán tử.
• parameters: Các tham số (nếu có).
25
#include <iostream>
using namespace std;
typedef struct { double real; double img;} SP;
SP SetSP(double real, double img);
void DisplaySP(SP C);
SP operator + (SP C1, SP C2);
SP operator - (SP C1, SP C2);
int main() {
SP C1,C2,C3,C4;
C1 = SetSP(1.1,2.0);
C2 = SetSP(-3.0,4.0);
cout << "\nSo phuc thu nhat:"; DisplaySP(C1);
cout << "\nSo phuc thu hai:"; DisplaySP(C2);
C3 = C1 + C2;
C4 = C1 - C2;
cout << "\nTong hai so phuc nay:"; DisplaySP(C3);
cout << "\nHieu hai so phuc nay:"; DisplaySP(C4);
return 0;
26
SP SetSP(double real,double img) {
SP tmp;
tmp.real = real; tmp.img = img;
return tmp;
}
SP operator + (SP C1,SP C2) {
SP tmp;
tmp.real = C1.real + C2.real;
tmp.img = C1.img + C2.img;
return tmp;
}
SP operator - (SP C1,SP C2) {
SP tmp;
tmp.real = C1.real - C2.real;
tmp.img = C1.img - C2.img;
return tmp;
}
void DisplaySP(SP C) {
cout << "(" << C.real << "," << C.img << ")"; 27
Các giới hạn của đa năng hóa toán tử
• Không thể định nghĩa các toán tử mới.
• Hầu hết các toán tử của C++ đều có thể được đa năng hóa. Các toán
tử sau không được đa năng hóa là :
:: Toán tử định phạm vi.
-> Truy cập đến con trỏ là trường của struct hay class.
. Truy cập đến trường của struct hay class.
?: Toán tử điều kiện
sizeof
Các ký hiệu tiền xử lý.
• Không thể thay đổi thứ tự ưu tiên của một toán tử cũng như số các
toán hạng của nó.
• Không thể thay đổi ý nghĩa của các toán tử khi áp dụng cho các
kiểu có sẵn.
• Đa năng hóa các toán tử không thể có các tham số có giá trị mặc 28
• +=
• SP+=SP; ~ SP1=SP1+SP2
• SP+=ST; ~ SP1=SP1+ST
SP & opertor +=(SP &c1,SP c2)
{ c1.real +=c2.real;
c1.img +=c2.img;
return c1; }
• ++,--
• Tien to
SP & operator ++(SP & c)
{
c.real++;
return c; }
Hau to
SP operator ++(SP &c, int)
{
SP tg=c;
c.real++;
return tg; }
29
Đa năng hóa toán tử
• Khi nào thì ta dùng khái niệm “Định nghĩa tóan tử”
• Khi có yêu cầu đa năng hóa tóa tử với một kiểu dữ liệu do
người dùng định nghĩa ( ví dụ với số phức) thì ta phải làm
những gì ?
• Có cách nào để làm cho gọn nhẹ ?
• Khi nào toán tử trả về 1 tham chiếu ?
• Khi nào phải truyền tham chiếu ?
• Bai tap 1 : dn kieu dl phanso, đa năng hóa các toán tử có thể
( tối giản ? )
• BT 2 : xây dựng cấu trúc matrix { int r;int c; float **M}
• Định nghĩa phép gán matrix và cho biết tại sao lại cần thiết
30
Con trỏ hàm
int foo(){
return 0;
}
int main(){
int n = foo();
return 0;
}
Khi trong hàm main chạy đến dòng lệnh gọi hàm foo, hệ điều
hành sẽ tìm đến địa chỉ của hàm foo trên bộ nhớ ảo và chuyển
mã lệnh của hàm foo cho CPU tiếp tục xử lý
31
Con trỏ hàm
int foo(){
return 0;
}
int main(){
printf("%p\n", foo);
return 0;
}
Kết quả:
013D1492
32
Cú pháp khai báo con trỏ hàm
<return_type> (*<name_of_ptr>)(<data_type_of_parameters>);
Ví dụ 1:
int foo(){
return 0;
}
int (*pFoo) ();
Ví dụ 2:
void swapValue(int &value1, int &value2) {
int temp = value1;
value1 = value2;
value2 = temp;
}
void(*pSwap) (int &, int &);
33
Ví dụ sử dụng con trỏ hàm
void swapValue(int &value1, int &value2){
int temp = value1;
value1 = value2;
value2 = temp;
}
int main(){
void(*pSwap) (int &, int &) = swapValue;
int a = 1, b = 5;
cout << "Before: " << a << " " << b << endl;
(*pSwap)(a, b);
cout << "After: " << a << " " << b << endl;
return 0;
}
34
Sắp xếp dãy số
bool ascending(int left, int right){
return left > right;
}
bool descending(int left, int right){
return left < right;
}
void selectionSort(int *arr, int length, bool
(*comparisonFunc)(int, int)){
for (int i_start = 0; i_start < length; i_start++) {
int minIndex = i_start;
for (int i_current = i_start + 1; i_current <
length; i_current++){
if (comparisonFunc(arr[minIndex], arr[i_current]))
{
minIndex = i_current;
}
}
swap(arr[i_start], arr[minIndex]); // std::swap
}
} 35
Sắp xếp dãy số
int main() {
int arr[] = {1, 4, 2, 3, 6, 5, 8, 9, 7};
int length = sizeof(arr) / sizeof(int);
cout << "Before sorted: ";
printArray(arr, length);
selectionSort(arr, length, descending);
cout << "After sorted: ";
printArray(arr, length);
return 0;
}
36
Sắp xếp dãy số
int main() {
int arr[] = {1, 4, 2, 3, 6, 5, 8, 9, 7};
int length = sizeof(arr) / sizeof(int);
cout << "Before sorted: ";
printArray(arr, length);
selectionSort(arr, length, ascending);
cout << "After sorted: ";
printArray(arr, length);
return 0;
}
37
Khái quát hóa hàm (Function templates)
38
Khái quát hóa hàm (Function templates)
Ví dụ:
template <typename T>
T maxval(T x, T y){
return (x > y) ? x : y;
}
39
Khái quát hóa hàm (Function templates)
#include <iostream>
using namespace std;
template <typename T>
T maxval(T x, T y){
return (x > y) ? x : y;
}
int main() {
int i = maxval(3, 7); // returns 7
cout << i << endl;
double d = maxval(6.34, 18.523); // returns 18.523
cout << d << endl;
char ch = maxval('a', '6'); // returns 'a'
cout << ch << endl;
return 0;
}
40
Da nang hoa phep + voi so phuc
template <typename T>
sp operator +( sp a, T b) {
sp tg;
tg.real =a.real+b; sp operator +( sp a, sp b) {
tg.img =a.img;
sp tg;
return tg;
}
tg.real =a.real+b.real;
template <typename T> tg.img =a.img + b.img;
sp operator +( T b, sp a) { return tg;
sp tg; }
tg.real =a.real+b;
tg.img =a.img;
return tg;
}
41
Từ khóa auto
• Đối với biến (từ C++11): auto xác định kiểu của biến được
khởi tạo một cách tự động từ giá trị khởi tạo của biến.
auto d { 5.0 }; // d will be type double
auto i { 1 + 2 }; // i will be type int
• Đối với hàm (từ C++14): auto tự động xác định kiểu trả về
của hàm dựa vào câu lệnh return.
auto add(int x, int y) -> int;
auto divide(double x, double y) -> double;
auto printSomething() -> void;
auto generateSubstring(const std::string
&s, int start, int len) -> std::string;
42
Từ khóa auto
• Đối với kiểu tham số (từ C++14): auto tự động xác định kiểu của
tham số dựa vào giá trị được truyền.
• Ví dụ:
auto maxval(auto x, auto y){
return (x > y) ? x : y;
}
int main() {
int i = maxval(3, 7); // returns 7
cout << i << endl;
double d = maxval(6.34, 18.523); // returns 18.523
cout << d << endl;
char ch = maxval('a', '6'); // returns 'a'
cout << ch << endl;
return 0;
}
43
Hàm nặc danh - cú pháp lambda
• Lambda hay còn gọi là hàm nặc danh, nó có thể dùng để
truyền vào một hàm khác và sử dụng một lần. Khác với các
cách dùng hàm thông thường buộc phải định nghĩa hàm sau
đó dùng tên hàm truyền vào một hàm khác.
• Lợi ích của lambda là không nhất thiết phải khai báo tên
hàm ở một nơi khác, mà có thể tạo ngay một hàm (dùng một
lần hay hiểu chính xác hơn là chỉ có một chỗ gọi một số tác
vụ nhỏ). Như vậy, ta sẽ giảm được thời gian khai báo một
hàm. Để làm rõ hơn về khái niệm này, ta sẽ xét 2 ví dụ sau.
44
Hàm nặc danh - cú pháp lambda
#include <iostream>
using namespace std;
void stdio_doing(int n) {
n = n + 10;
cout << n << " ";
}
int main(){
int arr[] ={1, 2, 3, 4, 5} , n = 5;
for_each(arr, n, stdio_doing);
return 0; 45
Hàm nặc danh - cú pháp lambda
#include <iostream>
using namespace std;
void for_each (int *arr, int n, void (*func)(int
a)){
for (int i = 0; i < n; i++){
func(*(arr + i));
}
}
int main(){
int arr[] ={1, 2, 3, 4, 5} , n = 5;
for_each(arr, n, [] (int a){
a = a + 10;
cout << a << " ";
});
return 0;
}
46
Hàm nặc danh - cú pháp lambda
48
Mệnh đề bắt giữ (capture clause)
• [] () {} không có ảnh chụp
• [=] () {} chụp mọi thứ bằng bản sao (tránh dùng)
• [&] () {} ghi lại mọi thứ bằng cách tham khảo (tránh)
• [x] () {} chụp x bằng bản sao
• [& x] () {} chụp x bằng cách tham chiếu
• [&, x] () {} chụp x bằng bản sao, mọi thứ khác bằng cách
tham chiếu
• [=, & x] () {} chụp x theo tham chiếu, mọi thứ khác bằng
bản sao
49
Hàm nặc danh - cú pháp lambda
Danh sách tham số
Ngoài khả năng bắt giữ các biến bên ngoài, lambda còn có thể
nhận đối số bằng cách khai báo danh sách tham số.
50
Hàm nặc danh - cú pháp lambda
Kiểu trả về của một lambda
Chúng ta có thể trả về bất kỳ kiểu dữ liệu nào giống như hàm thông thường. Ví dụ:
Tuy nhiên, để chương trình được rõ ràng hơn, chúng ta nên viết lambda có kiểu
trả về như sau:
Thêm khai báo -> int giúp việc đọc hiểu lambda dễ dàng hơn.
51
Hàm nặc danh - cú pháp lambda
Phần thân của một lambda
Phần thân của một lambda có thể:
• sử dụng những biến được bắt giữ trong mệnh đề bắt giữ.
• sử dụng các tham số.
• sử dụng các biến được khai báo bên trong struct/class chứa
nó thông qua con trỏ this (OOP).
• sử dụng các biến toàn cục, biến static.
52
Ví dụ
#include <iostream>
using namespace std;
int main(){
int m = 0;
int n = 0;
auto func = [&, n] (int a) mutable {
m = ++n + a;
};
func(4);
cout << m << endl << n << endl;
}
Kết quả:
5
0
53
một lambda duy nhất sẽ được tạo cho mỗi loại khác nhau, vd : một
lambda chung biến thành hai lambda riêng biệt:
#include <algorithm>
#include <array>
#include <iostream>
#include <string_view>
int main()
{ // in ra 1 gia trị và số lần @print được gọi.
auto print{
[](auto value) { static int callCount{ 0 };
std::cout << callCount++ << ": " << value << '\n';
}
Output
} 0: hello
print("hello"); // 0: hello 1: world
print("world"); // 1: world 0: 1
print(1); // 0: 1 1: 2
print(2); // 1: 2 2: ding dong
print("ding dong"); // 2: ding dong
return 0;
} 54
Lưu ý
• Lambdas có vẻ quá phức tạp khi so với giải pháp sử dụng
vòng lặp. Tuy nhiên, nó có thể cho phép một số hoạt động
rất mạnh mẽ chỉ trong một vài dòng mã và có thể dễ đọc hơn
là viết các vòng lặp
• Lambdas rất tuyệt, nhưng chúng không thay thế các chức
năng thông thường cho mọi trường hợp. Ưu tiên các chức
năng thông thường cho các trường hợp không đặc biệt và có
thể tái sử dụng.
• KISS
55
Tài liệu đọc thêm
1. Function templates:
https://docs.microsoft.com/en-us/cpp/cpp/function-templat
es?view=vs-2019
2. Auto:
https://docs.microsoft.com/en-us/cpp/cpp/auto-cpp?view=
vs-2019
3. Lambda expression:
https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressi
ons-in-cpp?view=vs-2019
56
For va For-each loops
for(bt1;bt2;bt3) loops
for(bt1,bt2;bt3,bt4;bt5,bt6) loops ?
for-each syntax :
Lặp với từng phần tử của mảng được gán cho element
Tốt nhất kiểu của element trùng với kiểu của mảng, nếu khong sẽ tự động chuyển kiểu
#include <iostream>
int main()
{
int fibonacci[]{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 };
for (int number : fibonacci)
std:cout << number << ' ';
return 0;
}
output :
0 1 1 2 3 5 8 13 21 34 55 89
57
For-each loops and references
• Trong vd sau, element được khai bao theo giá trị :
std::string Str[] { “xuan", “ha", “thu", “dong" };
for (auto element : Str) // element sẽ sao chep từng phần tử mảng
{
std::cout << element << ' ';
}
• Việc sao chép các phần tử mảng dẫn đến tổn hao tài nguyên, thay vì như vậy
chúng ta có thể truyền tham chiếu như sau :
std::string Str[] {“xuan", “ha", “thu", “dong"};
for (auto& element: Str) // element sẽ tham chiếu tới từng phtử mảng
{
std::cout << element << ' ';
}
58
Xin cảm
ơn!
59