Topic về con trỏ hàm và mã nguồn tùy biến

Cùng thảo luận nhé

##MỘT SỐ KIẾN THỨC NÂNG CAO TRONG C/C++

###I.TEMPLATE FUNCTION(KHUÔN MẪU HÀM)

Đặt vấn đề rằng ta có đoạn hàm để CT sau:

#include <iostream>
#include <conio.h>
using namespace std;
void HoanVi(int &x, int &y)
{
    int temp = x;
    x = y;
    y = temp;
}

void main()
{
    int x = 1, y = 2;
    HoanVi(x, y);
    cout<<"x = "<<x<<", y = "<<y;
    getch();
}

Đây là 1 đoạn CT hoàn toàn đúng. Nhưng giả sử chúng ta muốn bảo trì CT rằng chúng ta muốn hoán vị 2 số thực kiễu float. Với hàm xây dựng sẵn như trên việc chúng ta truyền 2 biến kiểu float vào cho hàm sẽ bị lỗi biên dịch. Cách khác là chúng ta tạo ra riêng 1 hàm để hoán vị 2 kiểu khác chúng ta muốn nhưng cách này ko khả quan.
Nếu 1 chương trình muốn hoán vị tới 10 kiểu khác nhau không lẽ ta phải tạo 10 hàm HoanVi. => Code dài nhìn thiếu chuyên nghiệp, khó khăn trong việc bảo trì, nâng cấp.

Rất maymắn C++ cho ra đời template function(khuôn mẫu hàm) để ta có thể tham số hóa kiễu dữ liệu được truyền vào 1 hàm.

Một hàm được tham số hóa là coi như là 1 hàm tổng quát xài chung cho mọi kiểu dữ liệu từ cơ sơ cho đến kiễu dữ liệu định nghĩa.Chúng ta chỉ cần cài đặt hàm 1 lần nhưng sử dụng được với nhiều kiểu dữ liệu .

CÁCH THỰC HIỆN.

  • Đối với những hàm chúng ta xác định rằng hàm đó sẽ có thể dùng chung đối với mọi kiễu dữ liệu thì chúng ta sẽ đặt dòng template<class T> hoặc template<typename T> , chúng giống nhau hoàn trong hai trường hợp trên tuy nhiên có một số chỗ chúng ta buộc phải dùng class hoặc buộc phải dùng typename. Ở đây ta sẽ chỉ dùng class T (theo tôi thấy được sử dụng rộng rãi hơn
    ^^)

  • Sau khi đã khai báo template cho hàm thì ta sẽ thay đổi kiễu dữ liệu trong hàm mà ta cho rằng chúng sẽ là 1 kiễu dữ liệu không biết trước bởi vì đối với hàm có xây dựng template thì trình compile sẽ thực sự những biến đó thuộc kiễu dữ liệu nào chỉ khi ta truyền biến vào hàm.

VD ta sửa lại đoạn HoanVi trên cho nó có tính tham số hóa.

template<class T>  // T là tên kiễu dữ liệu coi như là chưa biết trước. Các bạn có thể đặt tên khác tùy ý.
HoanVi(T &x, T &y)  // Tôi thay kiểu int thành 1 kiểu T
{
    T temp = x;         // Thay int temp = T temp để có thể đồng nhất với kiểu của x,y
    x = y;
    y = temp;
}

Như vậy ở bất kỳ 1 hàm nào nếu chúng ta truyền 2 số float thì khi gọi hàm trình compile sẽ “hiểu” rằng
T là float, nếu ta truyền vào 2 chuỗi string compile sẽ biến T thành string, thậm chí cả 2 HocSinh, NhanVien,…
Very good. :slight_smile:

Có 2 cách gọi hàm tham số hóa
C1: Gọi tường minh TenHam<kiểu dữ liệu truyền vào>(//Tham số nếu có)
C2: Gọi không tường mình thì như chúng ta gọi bình thường ^^ là như thế này TenHam(//Tham số nếu có)
Theo kinh nghiệm của tôi thì trong 1 số trường hợp trình compile yêu cầu bạn phải gọi tường minh đó ^^.

CHÚ Ý 1: Chỉ khi ta xài các phương thức nhập xuất(cout và cin) của C++ thì mới nên dùng template function
vì C++ việc nhập xuất không cần quan tâm đến đặc tả % của dữ liệu.
VD đoạn dùng template function theo nhập xuất của thư viện <stdio.h> (standard input output của C )
như sau vẫn bị lỗi vì “vướng” phải đặc tả %.

#include <stdio.h>
#include <conio.h>
template<class T>
void Sum(T x, T y)
{
    printf("x + y = %d", x + y);  // ? tại sao lại là %d khi kiểu dữ liệu T là kiểu chưa             //biết trước! }
    void main()
    {
        double x = 1, y = 2;
        Sum(x,y);  // x  + y = 0 . Một kết quả ko như mong muốn
        getch();
    }
}

VD đoạn dùng template function theo thư viện (input output stream của C++)

#include <iostream>
#include <conio.h>
using namespace std;
template<class T>
void Sum(T x, T y)
{
    cout<<"x + y = "<<x+y; // OK. Hoàn hảo
}
void main()
{
    double x = 1, y = 2;
    Sum(x,y);  // x  + y = 3
    getch();
}
  • Chúng ta cũng có thể khai báo template với 2 kiểu dữ liệu không biết trước trình compile sẽ xác định kiểu
    dữ liệu theo thứ tự biến truyền vào hàm

VD:

#include <iostream>
#include <conio.h>
using namespace std;
template<class T, class Y>   // 2 kiểu T và Y
void Nhap2Bien(T &x, Y &y)
{
    cout<<"Nhap x:";
    cin>>x;
    cout<<"Nhap y:";
    cin>>y;
}
void main()
{
    int x;
    char c;
    Nhap2Bien(x, c);  // T sẽ có kiểu int, Y có kiểu char
    cout<<"x = "<<x<<endl;
    cout<<"c = "<<c<<endl;
    long a;
    double b;
    Nhap2Bien(a, b); // T sẽ có kiểu long, Y có kiểu double
    getch();
}

CHÚ Ý 2: Chúng ta đã biết rằng hàm có cài đặt template function thí mọi kiễu dữ liệu truyền vào sẽ được tham số hóa nhưng ta cũng nên quản lý việc truyền kiểu dữ liệu 1 cách cẩn thận.

Ví dụ về việc sử dụng tham số hóa không hợp lý.

#include <iostream>
#include <conio.h>
#include <string>      // Thư viện string của C++
using namespace std;
template<class T, class Y>   // 2 kiểu T và Y
void Nhap2Bien(T &x, Y &y)
{
    cout<<"Nhap x:";
    cin>>x;
    cout<<"Nhap y:";
    cin>>y;
}
void main()
{
    int x;
    string str;
    Nhap2Bien(x, str);  // Y có kiểu string. Nhưng nhìn đoạn hàm trên.Chuỗi mà //chúng ta lại dùng cin???
    // Đáng lẽ phải fflush(stdin); rồi getline(cin, y); chứ :)
    cout<<"x = "<<x<<endl;
    cout<<"str = "<<str<<endl;
    getch();
}

###II. CLASS TEMPLATE( KHUÔN MẪU LỚP)
1.Tham số hóa cho lớp

  • Tương tự như khuôn mẫu hàm, khuôn mẫu lớp có cú pháp sử dụng như sau.
    VD1:
template<class T>     // T hay tên gì khác tùy bạn đặt
class TenLop
{
    // Nội dung của lớp
};

Khi định nghĩa các phương thức của lớp được tham số hóa kiễu dữ liệu như trên ta sử dụng
cú pháp như sau:

template<class T>
KieuTraVe TenLop<T>::TenHam(//các tham số nếu có)
{
    // Nội dung của phương thức
}

Khác với việc sử dụng khuôn mẫu hàm đối với khuôn mẫu lớp chúng ta phải nếu tường minh kiễu dữ liệu cần sử dụng. Ví dụ trong trường hợp muốn sử dụng lớp Mang1Chieu có kiểu int ta phải viết như sau:

Mang1Chieu<int> tendoituong;

2. Các tham số không kiểu
Chúng ta cũng có thể khai bao kiểu dữ liệu mặc định trong template
VD 1:

template<class T, int size)
class MyArray
{
    private:
    T a[size];
public:
... // Các phương thức
};
void main()
{
    MyArray<int,50> a; // Mảng a có kiểu dữ liệu int với tối đa 50 phần tử
    MyArray<HOCSINH, 100> h; // Mảng h có kiểu dữ liệu HOCSINH ( Gia sử ta  //đã xây dựng class HOCSINH)// với tối đa 100 em
}

Đặc biệt hơn ta có thể khai báo vừa kiễu dữ liệu biết trước và cả giá trị mặc định trong template như VD2 sau:
VD 2:

template<class T = int, int size = 50>
class MyArray
{
private:
    T arr[size];
public:
    ...// Các phương thức
};

Ở hàm main ta chỉ việc khai báo MyArray<> tendoituong; thì trình compile cũng tự xác định rằng Mảng tendoituong có kiểu dữ liệu int với tối đa 50 phần tử.

3.Kế thừa các lớp tham số hóa
Khi 1 class dẫn xuất kế thừa 1 class cơ sở có sử dụng tham số hóa thì cú pháp như sau:

template<class T>
class CoSo
{
    //Nội dung của lớp cơ sở
public :
    void NhapCoSo()
    {
        ... // Nội dung phương thức Nhap
    }
};
template<class T>
class DanXuat:public CoSo<T>
{
    // Nội dung của lớp dẫn xuất
    void NhapDanXuat()
    {
        CoSo<T>::NhapCoSo(); // Gọi phương thức NhapCoSo của class CoSo
    }
};

VD:

#include <iostream>
#include <conio.h>
#include <string>      // Thư viện string của C++
using namespace std;
template<class T>
class CoSo
{
    //Nội dung của lớp cơ sở
public :
    void NhapCoSo()
    {
        cout<<"OK";
    }
};
template<class T>
class DanXuat:public CoSo<T>
{
    // Nội dung của lớp dẫn xuất
public:
    void NhapDanXuat()
    {
        CoSo<T>::NhapCoSo(); // Gọi phương thức NhapCoSo của class CoSo
        a = 69;
        cout<<endl<<a;
    }
    T a;
};

void main()
{
    DanXuat<int> x;
    x.NhapDanXuat();
    getch();
}

CHÚ Ý: Cẩn thận trong trường hợp hàm NhapCoSo là hàm ảo và có sử dụng tính chất đa xạ.
Khi đó cách thức hoạt động của hàm có thể khác với mong muốn!

III.Con trỏ hàm
Biến đều có địa chỉ trong bộ nhớ.
Vậy hàm tồn tại tức nó sẽ có địa chỉ trong bộ nhớ.
=> Sẽ có 1 loại con trỏ trỏ đến các hàm. Ta gọi là con trỏ hàm
Cú pháp:

<Kiểu trả về của hàm> (*<Tên con trỏ hàm>)(<Danh sách tham số nếu có>)

CHÚ Ý: Phần danh sách tham số ta chỉ cần quan tâm đến kiễu dữ liệu của tham số, ko cần thiết khai tên tham số(giống khai báo hàm).

TRỎ ĐẾN HÀM: Chỉ có thể trỏ đến các hàm nếu con trỏ hàm phù hợp kiểu trả về, các ds tham số.

Cách 1:

<Tên con trỏ> = <Tên hàm>;

Cách 2:

<Tên con trỏ> = &<Tên hàm>;

Sau khi trỏ thay vì gọi hàm bằng cách thông thường ta có thể thay tên hàm bằng tên con trỏ .
VD1:

void Nhap2SoNguyen(int &x, int &y)
{
    printf("Nhap x,y:\n");
    scanf("%d%d", &x, &y);
}
int TinhTong(int x, int y)
{
    return x + y;
}
int TinhHieu(int x, int y)
{
    return x - y;
}
void main()
{
    int x, y;
    void (*q)(int &, int &); //Khai báo con trỏ hàm trỏ đến các hàm không có kiểu trả //về là với 2
    tham số kiểu int truyền theo kiểu tham chiếu ( tham biến).int (*p)(int , int ); // Khai //báo con trỏ hàm trỏ đến các hàm có kiểu trả là int với 2 tham số kiểu
// int truyền theo kiểu tham trị.
    q = TinhTong; // ERROR. Ko phù hợp để trỏ
    q = Nhap2SoNguyen; // OK con trỏ hàm q đang trỏ đến hàm Nhap2SoNguyen
    q = &Nhap2SoNguyen; // OK con trỏ hàm q đang trỏ đến hàm Nhap2SoNguyen
    p = TinhTong; // OK Con trỏ p trỏ đến hàm TinhTong
    q(x, y);
    <=>Nhap2SoNguyen(x,y);
    printf("x + y = %d", p(x,y));
}

VD2 là sự kết hợp khuôn mẫu hàm và con trỏ hàm để tạo nên tình tùy biến mã nguồn cao nhất.

template<class T>
void HoanVi(T* x, T* y)
{
    T temp = *x;
    *x = *y;
    *y = temp;
}

void main()
{
    // Khai báo con trỏ hàm để có thể trỏ tới hàm HoanVi
    float x,y;
    x = 1;
    y = 2;
    void (*Pointer)(float*, float*);
    // Có 2 cách cho con trỏ trỏ tới hàm
    Pointer = HoanVi;  // C1
    Pointer = &HoanVi;  // C2
    //HoanVi(&x, &y);
    Pointer(&x, &y);
}

IV. MẢNG CON TRỎ HÀM

  • Là mảng lưu giữ các phần tử là biến con trỏ hàm.
    CÚ PHÁP:
KieuTraVe(*TenConTroHam[SoLuongphantu])(//Ds tham số nếu có)

Chú ý rằng các biến con trỏ của mảng phải cùng kiểu tra ve, DS tham số.
VD:

int (*array[2])(int , int);
array[0] = TinhTong; // Con trỏ tại vị trí 0 trong mảng trỏ đến hàm TinhTong
array[1] = TinhHieu; // Con trỏ tại vị trí 1 trong mảng trỏ đến hàm TinhHieu

VD: Thực hiện nhập xuất mảng có kiễu dữ liệu tùy chọn

#include <iostream>
using namespace std;
template <class T>
void NhapMang(T *a, int n)
{
    for(int i = 0; i < n; i++)
    {
        cout<<"Nhap a["<<i<<"]:\n";
        cin>>*(a+i);
    }
}
template <class T>
void XuatMang(T *a, int n)
{
    for(int i = 0; i < n; i++)
    {
        cout<<*(a+i)<<"  ";
    }
}

int main()
{
    int n;
    do
    {
        cout<<"Nhap so phan tu:";
        cin>>n;
        if(n < 0)
            cout<<"So luong khong hop le. Nhap lai!";
    }
    while(n < 0);
    char *char_array = new char[n];
    void (*Pointer[2])(char , int);
    Pointer[0] = &NhapMang;
    Pointer[1] = &XuatMang;
    for(int i = 0; i < 2; i++)
    {
        Pointer[i]<char>(char_array, n); // Gọi hàm theo cách tường minh
        // Pointer[i](char_array, n); // Gọi hàm cách không tường minh
    }
    delete []char_array;
    cin.get();
}

V. KHUÔN MẪU HÀM, CON TRỎ HÀM TRONG VIỆC XÂY DỰNG CHƯƠNG TRÌNH CÓ TÍNH CHẤT TÙY BIẾN MÃ NGUỒN.
-Ta đã tìm hiểu về khuôn mẫu hàm, con trỏ hàm và các công dụng của chúng. Và công dụng lớn nhất
của chúng theo đánh giá của tôi là sự kết hợp để tạo nên mã nguồn có tính tùy biến, ngắn gọn, dễ bảo trì.

Ví dụ rằng ta có 2 hàm sắp xếp tăng dần và giảm dần mảng 1 chiều như sau:

void SapTang(int a[], int n)
{
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i + 1; j < n; j++)
        {
            if(a[j] < a[i])
            {
                int temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}
void SapGiam(int a[], int n)
{
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i + 1; j < n; j++)
        {
            if(a[j] > a[i])
            {
                int temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}

Ta thấy rằng 2 hàm này nó gần tương đương nhau khác biết chỉ trong 1 câu lệnh if. Vậy có cách nào đểxây dưng nên chỉ 1 hàm SapTuyChon và có thể tùy cách ta gọi hàm mà nó có thể vừa làm công việc sắp tăng, vừa làm công việc sắp giảm được ko?
Câu trả lời rằng hãy ứng dụng con trỏ hàm như các ví dụ sau:
VD1:
//Ta tạo ra 2 hàm sosanh
//Nhắc lại về kiễu dữ liệu bool của C++:bool là kiểu dữ liệu do C++ xây dựng nên thuận tiện trong việc kiểm tra 1 điều kiện gì đó. Nó trả ra 2 giá trị true hoặc false

bool  SoSanhNho(int x, int y)
{
    if(x < y)
        return true;
    else
        return false;
}
bool SoSanhLon(int x, int y)
{
    if(x > y)
        return true;
    else
        return false;
}

void SapTuyChon(int a[], int n, bool (*p)(int, int)) // Chúy y tham số thứ 3 là 1 con //trỏ hàm phù hợp để trỏ đến
// 2 hàm SoSanhNho va SoSanhLon
{
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i + 1; j < n; j++)
        {
            if(p(a[j], a[i]) == true)
            {
                int temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}
void main()
{
    int a[5] = {1, 5, 3, 2, 0}; // Khởi tạo mảng a gồm 5 phần tử
    SapTuyChon(a, 5, NhoHon); // Ta truyền tên hàm NhoHon vào tham số thứ 3. //Lúc này con trỏhàm  p trong hàm SapTuyChon sẽ trỏ vào hàm NhoHon. Như vậy câu if trong hàm tương đuong
    // if(SoSanhNho(a[j], a[i]) == true) => Hàm đang thực hiện sắp xếp tăng

    SapTuyChon(a, 5, LonHon);   // Hàm đang thực hiện sắp giảm
}
  • Qua ví dụ trên ta đã thấy việc ứng dụng con trỏ hàm để tùy biến mã nguồn. Bây giờ ta tăng thêm tính tùy biến khi bổ sung kết hợp cả khuôn mẫu hàm như sau để có thể tùy sửa là mảng kiểu int hay kiểu khác thật nhanh gọn ở hàm main.
template<class T>
bool  SoSanhNho(T x, T y)
{
    if(x > y)
        return true;
    else
        return false;
}
template<class T>
bool SoSanhLon(T x, T y)
{
    if(x < y)
        return true;
    else
        return false;
}
template<class T>
void SapTuyChon(T a[], int n, bool (*p)(T, T)) // Chúy y tham số thứ 3 là 1 con trỏ    //hàm phù hợp để trỏ đến  2 hàm SoSanhNho va SoSanhLon
{
    for(int i = 0; i < n - 1; i++)
    {
        for(int j = i + 1; j < n; j++)
        {
            if(p(a[j], a[i]) == true)
            {
                int temp = a[j];
                a[j] = a[i];
                a[i] = temp;
            }
        }
    }
}

void main()
{
    int a[5] = {1, 5, 3, 2, 0}; // Khởi tạo mảng a gồm 5 phần tử
    SapTuyChon(a, 5, NhoHon);
// Ta truyền tên hàm NhoHon vào tham số thứ 3. //Lúc này con trỏ hàm p trong hàm SapTuyChon sẽ trỏ vào hàm NhoHon. Như vậy //câu if trong hàm tương đuong
    // if(SoSanhNho(a[j], a[i]) == true) => Hàm đang thực hiện sắp xếp tăng
    SapTuyChon(a, 5, LonHon);   // Hàm đang thực hiện sắp giảm
}

=>Bây giờ hàm đã thực sự có tính tùy biến rất cao. Điều quan trọng là biết cách nhận ra điểm giống nhau của các hàm để thêm 1 tham số là con trỏ hàm và tùy biến con trỏ hàm sẽ trỏ vào hàm nào tùy cách ta gọi hàm.

VD2: Thực hiện tìm max, min của mảng có kiểu dữ liệu bất kỳ

template<class T>
bool SoSanhNho(T x, T y)
{
    if(x < y)
        return true;
    else
        return false;
}
template<class T>
bool SoSanhLon(T x, T y)
{
    if(x > y)
        return true;
    ele
    return false;
}
template<class T>
T MaxorMin(T a[], int n, bool (*p)(T, T))
{
    T x = a[0];
    for(int i = 1; i < n; i++)
    {
        if(p(a[i], x) == true)
        {
            x = a[i];
        }
    }
    return x;
}

void main()
{
    float a[10] = {1, 5, 7, 69, -13.2 , 0 ,6, 8.4 , 11.5 , 1};
    printf("Max = %f", MaxorMin(a,10, LonHon)); // Tìm max và trả về là 69
    printf("Max = %f", MaxorMin(a, 10, NhoHon)); //Tìm min và trả về là -13.2
    getch();
}

VD3: Thực hiện tính tổng các số nguyên tố, số chính phương, số hoàn thiện.

Thử tưởng tượng rằng nếu ta chưa biết đến các tùy biến mã nguồn thì mã nguồn chúng ta sẽ như sau(
Ở đây tôi không cần dùng template function được lý do là vì số nguyên tố , số hoàn hảo ngoài truyển kiểu int ra thì truyền kiểu khác sao được ^_< .Điều hiển nhiên nhé ^^)

bool KiemTraNguyenTo(int x)  // Kiểm tra 1 số x có phải là số nguyên tố không. Có nhiều thuật toán để làm bài này
{
    if(x < 2)
        return false;
    else if(x > 2)
    {
        if(x % 2 == 0)
            return false;
        else
        {
            for(int i = 3; i <= (int)sqrt(float(x)); i += 2)
            {
                if(x % i == 0)
                    return false;
            }
        }
    }
    return true;
}
bool KiemTraChinhPhuong(int x) // Hàm kiểm tra 1 số có phải là số chính phương không
{
    if(sqrt(float(x)) * sqrt(float(x)) == x)
        return true;
    else
        return false;
}
bool KiemTraHoanThien(int x) // Hàm kiểm tra 1 số có phải là số hoàn thiện không
{
    int tonguocso = 0;
    for(int i = x - 1; i >= 1; i--)
    {
        if(x % i == 0)
            tonguocso += i;
        if(tonguocso > x)
            return false;
    }
    if(tonguocso == x)
        return true;
}
// Gio ta phải tạo ra 3 hàm tính tổng
int TongNguyenTo(int a[],int n)
{
    int tong = 0;
    for(int i = 0; i < n; i++)
    {
        if(KiemTraNguyenTo(a[i]) == true)
            tong += a[i];
    }
    return tong;
}

int TongChinhPhuong(int a[],int n)
{
    int tong = 0;
    for(int i = 0; i < n; i++)
    {
        if(KiemTraChinhPhuong(a[i]) == true)
            tong += a[i];
    }
    return tong;
}
int TongHoanThien(int a[],int n)
{
    int tong = 0;
    for(int i = 0; i < n; i++)
    {
        if(KiemTraHoanThien(a[i]) == true)
            tong += a[i];
    }
    return tong;
}

=> Đến đây bạn đã nhận ra rằng tại sao ta lại phải tốn công tạo ra 3 hàm như vậy khi công dụng của chúng đều là tính tổng và chỉ khác biệt đoạn code if.

Với ví dụ trên tôi sẽ sử dụng con trỏ hàm để tăng tính tùy biến mã nguồn.
Tôi thay 3 hàm tính tổng với chỉ 1 hàm duy nhất là TongTuyChon

int TongTuyChon(int a[], int n, bool (*p)(int))
{
    int tong = 0;
    for(int i = 0; i < n; i++)
    {
        if(p(a[i]) == true)
            tong += a[i];
    }
    return tong;
}
// Ở hàm main tôi sẽ tùy trường hợp mà truyền vào tham số thứ 3 tên hàm kiểm tra
//để con trỏ hàm p trỏ đến nằm cùng địa chỉ với hàm đó.
void main()
{
    int a[5] = {1, 3, 5, 12, 6};
    printf("Tong nguyen to = %d", TongTuyChon(a, n, KiemTraNguyenTo)); //8
    printf("Tong hoan thien = %d", TongTuyChon(a, n, KiemTraHoanThien));  // 6
    printf("Tong Chinh phuong = %d", TongTuyChon(a, n, KiemTraChinhPhuong));  /// 5
    getch();
}

Xin nhắc lại lần nữa để sử dụng tốt kĩ năng tùy biến mã nguồn ta phải vững con trỏ hàm và cả khuôn mẫu
hàm. Hãy xác định các hàm có mỗi liên quan, điểm tương đồng để có thể gom chúng lại 1 hàm TuyChon nào đó.

BÀI TẬP RÈN LUYỆN .
Hãy thử làm bài tập sau bằng việc ứng dụng con trỏ hàm và khuôn mẫu hàm để tạo nên mã nguồn có tính tùy biến.

1.Xây dựng mảng 1 chiều với kiểu dữ liệu “bất kỳ” thực hiện các yêu cầu sau:
Chú ý hàm nào trả về giá trị được thì phải tạo ra hàm trả về giá trị. Ko được tạo hàm không kiểu(hàm void).

  • Đếm số dương
  • Đếm số âm
  • Tính tổng số dương
  • Tính tổng số âm
  • Tính tổng các số cực tiểu. Số cực tiểu là số lớn nhỏ 2 số xung quanh nó (a[i] <a[i-1] && a[i] < a[i+1])
  • Tính tổng các số cực đại. Số cực đại là số lớn hơn 2 số xung quanh nó (a[i] >a[i-1] && a[i] > a[i+1])
  • Xóa các số lớn hơn 100
  • Xóa các số nhỏ hơn 100

Tuấn Nguyễn 15 - 02 - 2015

7 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?