Ép Kiểu Trong C++

Ép kiểu trong C++ là việc gán giá trị của một biến có kiểu dữ liệu này tới biến khác có kiểu dữ liệu khác. Trong bài viết ngày hôm nay, chúng ta sẽ tìm hiểu kĩ hơn về ép kiểu trong C++ qua bai viết dưới đây nhé.

I. Ép Kiểu Trong C++ Là Gì

Toán tử ép kiểu (một cast) trong C++ là 1 toán tử đặc biệt mà làm một kiểu dữ liệu này biến đổi thành kiểu dữ liệu khác. Toán tử ép kiểu là một toán tử 1 ngôi và có cùng độ ưu tiên như bất kỳ toán tử một ngôi nào khác trong C++.

Cú pháp được dùng thường xuyên của toán tử ép kiểu trong C++ là:

(kieu_du_lieu) bieu_thuc

Ở đây, kieu_du_lieu là kiểu dữ liệu bạn muốn. Dưới đây là một số toán tử ép kiểu được hỗ trợ bởi C++:

const_cast<kieu_du_lieu> (bieu_thuc): Toán tử const_cast được sử dụng để ghi đè const và/hoặc volatile. Kiểu dữ liệu bạn muốn buộc phải giống như kiểu dữ liệu nguồn ngoại trừ sự sửa đổi của những thuộc tính const hoặc volatile trong một cast. Dạng ép kiểu này thao tác thuộc tính const của đối tượng đã truyền: hoặc được thiết lập hoặc gỡ bỏ.

dynamic_cast<kieu_du_lieu> (bieu_thuc): Toán tử dynamic_cast trong C++ thực hiện một ép kiểu tại runtime mà thẩm tra tính hợp lệ của cast. Nếu cast không thể được tạo ra, cast này thất bại và biểu thức ước lượng là null. Một toán tử dynamic_cast thực hiện các cast trên các kiểu đa hình và có thể ép một con trỏ A* thành một con trỏ B* chỉ khi đối tượng đang được trỏ tới thực sự là một đối tượng B.

reinterpret_cast<kieu_du_lieu> (bieu_thuc): Toán tử reinterpret_cast trong C++ thay đổi 1 con trỏ tới bất kỳ kiểu con trỏ khác. Nó cũng cho phép ép kiểu từ con trỏ tới một kiểu integer và ngược lại.

static_cast<kieu_du_lieu> (bieu_thuc): Toán tử static_cast trong C++ thực hiện một cast không có tính đa hình. Ví dụ, nó có thể được dùng để ép kiểu một con trỏ lớp cơ sở thành một con trỏ lớp kế thừa.

Tất cả toán tử ép kiểu trên sẽ được sử dụng trong lúc làm việc với lớp và đối tượng. Bây giờ, bạn thử ví dụ sau để hiểu toán tử ép kiểu đơn giản trong C++. Copy và paste chương trình C++ sau trong tệp test.cpp, sau đó biên dịch và chạy chương trình:

#include <iostream>
using namespace std;
 
main()
{
   double a = 15.65653;
   float b = 9.02;
   int c ;
 
   c = (int) a;
   cout << "Dong 1: Gia tri cua (int)a la: " << c << endl ;
   
   c = (int) b;
   cout << "Dong 1: Gia tri cua (int)b la: " << c << endl ;
   
   return 0;
}

Chạy chương trình C++ trên sẽ cho kết quả như hình sau:

Ép Kiểu Trong C++ Là Gì
Ép Kiểu Trong C++ Là Gì

II. Cách Ép Kiểu Dữ Liệu Trong C++


III. Ép Kiểu Class Trong C++

Ép kiểu (Casting) là quá trình convert từ kiểu dữ liệu A sang kiểu dữ liệu B. C++ có 2 kiểu casting.

Implicit conversion: việc ép kiểu được thực hiện tự động bởi compiler.

int iVar = 10;
float fVar = iVar; //fVar = 10.00

Explicit conversion: ép kiểu được thực hiện bởi lập trình viên.

int iVar = 20;
float fVar = (float)iVar / 10;

Ngôn ngữ C++ support 5 toán tử ép kiểu.
– static_cast
– const_cast
– reinterpret_cast
– dynamic_cast

static_cast

Ví dụ 1:

// static_cast_Operator.cpp  
// compile with: /LD  
class B {};  
   
class D : public B {};  
   
void f(B* pb, D* pd) 
{  
   D* pd2 = static_cast<D*>(pb);   // Not safe, D can have fields  
                                   // and methods that are not in B.  
   
   B* pb2 = static_cast<B*>(pd);   // Safe conversion, D always  
                                   // contains all of B.  
}

Trong ví dụ 1

– Line B* pb2 = static_cast<B*>(pd); an toàn vì các thuộc tính và method của class B đều thuộc class D.
– Line D* pd2 = static_cast<D*>(pb); không an toàn vì class D có các thuộc tính và method mà class B không có. Tuy nhiên, câu lệnh này không báo lỗi khi chạy runtime vì toán tử static_cast không có check runtime (khác với toán tử dynamic_cast). Điều thực sự nguy hiểm khi sử dụng *pd2 truy cập tới thuộc tinh và method chỉ thuộc lớp D và không thuộc lớp B –> Chương trình crash do ACCESS VIOLATION.

Ví dụ 2:

// static_cast_Operator_2.cpp  
// compile with: /LD /GR  
class B 
{  
public:  
   virtual void Test(){}  
};  
class D : public B {};  
   
void f(B* pb) 
{  
   D* pd1 = dynamic_cast<D*>(pb);  
   D* pd2 = static_cast<D*>(pb);  
}

Trong ví dụ 2

– Nếu pb thực sự trỏ tới đối tượng class D, thì pd1 và pd2 sẽ có cùng giá trị, pd1 và pd2 sẽ có cùng giá trị nếu pd == 0 (NULL).
– Nếu pb trỏ tới đối tượng class B, thì dynamic_static sẽ trả về 0, nhưng static_cast không thể detect được vấn đề này. Do vậy, trong trường hợp sử dụng static_cast, lập trình viên phải kiểm tra nếu pb trỏ tới đối tượng class D thì mới trả về con trỏ trỏ tới class D.

Ví dụ 3:

// static_cast_Operator_3.cpp  
// compile with: /LD /GR  
typedef unsigned char BYTE;  
   
void f() 
{  
   char ch;  
   int i = 300;  
   float f = 2.5;  
   double dbl;  
   
   ch = static_cast<char>(i);   // int to char  
   dbl = static_cast<double>(f);   // float to double  
   i = static_cast<BYTE>(ch);  
}

Trong ví dụ 3,

– Toán tửstatic_cast được sử dụng để ép kiểu cho các kiểu dữ basic. Từ int –> char, float –> double, char –> int.
– Câu lệnh “ch = static_cast(i); // int to char” gây ra mất dữ liệu vì kiểu char (1 byte) không đủ chứa kiểu int (4 bytes). Do vậy, chỉ ép kiểu từ kiểu dữ liệu NHỎ –> kiểu dữ liệu TO để tránh convert sai.

const_cast

const_cast < type-id > ( expression )

// expre_const_cast_Operator.cpp  
// compile with: /EHsc  
#include <iostream>  
   
using namespace std;  
class CCTest 
{  
public:  
   void setNumber( int );  
   void printNumber() const;  
private:  
   int number;  
};  
   
void CCTest::setNumber( int num ) { number = num; }  
   
void CCTest::printNumber() const
{  
   cout << "\nBefore: " << number;  
   const_cast< CCTest * >( this )->number--;  
   cout << "\nAfter: " << number;  
}  
   
int main() 
{  
   CCTest X;  
   X.setNumber( 8 );  
   X.printNumber(); 
   system("pause");
}
Ép Kiểu Class Trong C++
Ép Kiểu Class Trong C++

IV. Ép Kiểu Tường Minh Trong C++

Ép kiểu tường minh (Explicit type conversion) là quá trình chuyển đổi kiểu dữ liệu một cách tường minh (rõ ràng) bởi lập trình viên, sử dụng toán tử ép kiểu (casting operator) để thực hiện việc chuyển đổi.

Trong C++, có 5 cách ép kiểu tường minh:

  • C-style casts
  • Static casts
  • Const casts
  • Dynamic casts
  • Reinterpret casts

Phạm vi bài học này sẽ nói về C-style casts và Static casts, 2 cách phổ biến nhất trong C++.

Dynamic casts, Const casts và Reinterpret casts cần những kiến thức chuyên sâu hơn, nên sẽ được bỏ qua trong bài học này.

C-style casts

Trong ngôn ngữ C chuẩn, ép kiểu được thực hiện thông qua toán tử (), và tên kiểu dữ liệu cần chuyển được đặt bên trong.

Ví dụ:

#include <iostream>
using namespace std;

int main()
{
   int n1 = 3;
   int n2 = 2;
   double d = (double)n1 / n2;

   cout << d << endl;

   return 0;
}

Trong chương trình trên, trình biên dịch (compiler) chuyển đổi biến n1 từ kiểu int sang kiểu double thông qua ép kiểu tường minh C-style. Sau đó biểu thức có 2 toán hạng có kiểu double và kiểu int, nên toán hạng kiểu int sẽ được chuyển đổi ngầm định sang kiểu double. Vì vậy, kết quả của biểu thức là phép chia giữa 2 số chấm động kiểu double.

Ngôn ngữ C++ cho phép thực hiện ép kiểu tường minh C-style với cú pháp như một lời gọi hàm:

int n1 = 3;
int n2 = 2;
double d = double(n1) / n2;

Ép kiểu tường minh C-style không được trình biên dịch (compiler) kiểm tra tại thời điểm biên dịch (compile time), nên trình biên dịch sẽ không đưa ra những cảnh báo trong những trường hợp chuyển đổi không đúng

Chú ý: Tránh sử dụng ép kiểu tường minh C-style.

static_cast

Ngôn ngữ C++ có 1 toán tử ép kiểu gọi là static_cast. Bạn đã biết đến nó trong bài KIỂU KÝ TỰ TRONG C++ (Character):

#include <iostream>
#include <iomanip>	// for std::setprecision()
using namespace std;

int main()
{
   int n{ 75 };
   cout << static_cast<char>(n) << endl; // in ký tự với mã ASCII 75

   char ch{ 'K' };
   cout << static_cast<int>(ch) << endl; // in mã ASCII của ký tự 'K'
   return 0;
}

Chương trình trên sử dụng toán tử ép kiểu static cast trong C++ để in một ký tự từ một số nguyên và ngược lại.

Sử dụng static cast trong biểu thức:

Ví dụ:

#include <iostream>
using namespace std;

int main()
{
   int n1 = 3;
   int n2 = 2;
   double d = static_cast<double>(n1) / n2;

   cout << d << endl;

   return 0;
}

Ưu điểm của toán tử static_cast là nó yêu cầu compiler kiểm tra kiểu dữ liệu tại thời điểm biên dịch chương trình, hạn chế được những lỗi ngoài ý muốn.

Chú ý: Nên sử dụng toán tử static_cast thay vì ép kiểu C-style.

 Ép Kiểu Tường Minh Trong C++
Ép Kiểu Tường Minh Trong C++

V. Ép Kiểu Con Trỏ Trong C++

 

VI. Ép Kiểu Float Trong C

Chắc hẳn khi mới bắt đầu tập tành với C/C++ ai cũng gặp trường hợp phải ép kiểu int sang float và ngược lại. Vậy bài viết này mình sẽ hướng dẫn các bạn cách ép kiểu nguyên sang kiểu thực trong C/C++.

Ép từ kiểu nguyên sang kiểu thực

Ép kiểu int sang float

Có nhiều trường hợp chúng ta phải ép kiểu từ int sang float như ví dụ dưới đây

Bài toán: Nhập vào hai số nguyên dương a và b. Hãy xuất ra kết quả a/b.

#include<iostream>
 
using namespace std;
 
int main()
{
    int a, b;
    float kq;
    cout << "Nhap a: "; cin >> a;
    cout << "Nhap b: "; cin >> b;
    if (!b) {// Hoặc if(b==0)
        cout << "Mau phai khac 0 !";
        return 0;
    }
    kq = a / b;
    cout << "Ket qua la: " << kq << endl;
    return 0;
}
Nhap a: 2
Nhap b: 3
Ket qua la: 0

Trong trường hợp trên ta bắt buộc phải ép kiểu int sang float. Cú pháp ép kiểu như sau:

 
kq = (float)a / (float)b;
kq = float(a) / float(b);

Ở trên có hai cách ép kiểu nhưng mình khuyên các bạn nên dùng cách dưới, nhìn dễ hiểu hơn.

Cùng xem lại đoạn code ở trên nhé

 
#include<iostream>
 
using namespace std;
 
int main()
{
    int a, b;
    float kq;
    cout << "Nhap a: "; cin >> a;
    cout << "Nhap b: "; cin >> b;
    if (!b) {// Hoặc if(b==0)
        cout << "Mau phai khac 0 !";
        return 0;
    }
    //kq = (float)a / (float)b;
    kq = float(a) / float(b);
    cout << "Ket qua la: " << kq << endl;
    return 0;
}
Nhap a: 2
Nhap b: 3
Ket qua la: 0.666667

Ép kiểu int sang double

Cách làm cũng tương tự như trên mà thôi.

Ép từ kiểu thực sang kiểu nguyên

Ép kiểu float sang int

Ta có thể ép kiểu dữ liệu như sau:

 
#include<iostream>
 
using namespace std;
 
int main()
{
    float a = 3.5f;
    int b = int(a);
    cout << "Sau khi ep kieu: " << b << endl;
    return 0;
}
Sau khi ep kieu: 3

Lưu ý: Khi ép kiểu từ int sang float thì chỉ nhận được các chữ số trước dấu phẩy mà thôi. Chứ không làm tròn lên nhé.

Ví dụ: int a = float(8.9) thì a sẽ nhận giá trị 8 mà thôi.

Ép kiểu double sang int

Tương tự như ép kểu float sang int.

Ép Kiểu Float Trong C
Ép Kiểu Float Trong C

VII. Ép Kiểu Int Sang String Trong C++

Hướng dẫn cách chuyển int sang string trong C++. Bạn sẽ học được cách sử dụng hàm to_string() trong C++ sau bài học này.

Chúng ta có 2 phương pháp để chuyển int sang string trong C++ như sau:

  • Sử dụng hàm template (trước C++11)
  • Sử dụng hàm to_string (từ C++11)

Chuyển float và int sang string trong C++ bằng hàm template

Phương pháp đầu tiên cũng để chuyển int sang string trong C++ đó chính là sử dụng hàm template sau đây:

#include <sstream>
template <typename T> std::string tostr(const T& t)
{
    std::ostringstream os; os<<t; return os.str();
}

Hàm template trên có tác dụng chuyển đổi kiểu số bao gồm cả int lẫn float sang kiểu string. Sau khi đã khai báo template này, chúng ta có thể sử dụng nó nhiều lần trong chương trình.

Ví dụ cụ thể, chúng ta có thể chuyển float và int sang string như chương trình sau. Lưu ý chúng ta sử dụng tới hàm typeid.name() để kiểm tra kiểu của dữ liệu trước và sau lúc đổi thay bằng hàm template.

#include <iostream>
#include <sstream>  
#include <typeinfo>

using namespace std;
 
template <typename T> string tostr(const T& t)
{
    ostringstream os; os<<t; return os.str();
}

 
int main() {
    int a = 123;
    float b = 3.45;

    cout << "Kieu cua a: "<< typeid(a).name() <<endl;
    cout << "Kieu cua b: "<<typeid(b).name() <<endl;
    
    //Kiểu sau khi thay đổi
    cout << "Kieu thay doi cua a: "<< typeid(tostr(123)).name() <<endl;
    cout << "Kieu thay doi cua b: "<<typeid(tostr(3.1415)).name() <<endl;
    return 0;
}

Kết quả, hai biến a và b với kiểu int và float đã được chuyển sang kiểu string bằng hàm tostr như sau:

Kieu cua a: i
Kieu cua b: f
Kieu thay doi cua a: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Kieu thay doi cua b: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE

Chuyển int sang string trong C++ bằng hàm to_string

Để dùng hàm template ở trên, chúng ta đều cần khai báo template ở đầu mỗi chương trình, và chuyện này thật là rườm rà phải không nào?

Đó là lý do mà từ phiên bản C++11 trở đi, song hành cùng với hàm template ở trên thì các nhà phát triển C++ đã thêm vào 1 hàm mới có chức năng tương tự nhưng lại có thể sử dụng dễ dàng hơn, đó chính là hàm to_string.

Hàm to_string là 1 hàm được thêm vào từ phiên bản C++11, có tác dụng chuyển số sang string trong C++. Hàm to_string có thể chuyển tất cả các định dạng kiểu số như int, float, double sang string trong C++ với cú pháp đơn giản và ngắn gọn.

Chúng ta sử dụng hàm to_string() trong C++ với cú pháp sau đây:

to_string(num);

Trong đó num là số cần chuyển sang kiểu chuỗi string, và num có thể ở bất kiểu số nào, ví dụ như int, float, double hay short chẳng hạn.

Ví dụ cụ thể, chúng ta có thể chuyển int sang string trong C++ bằng hàm to_string và kiểm tra kiểu trước và sau khi chuyển đổi bằng hàm typeid.name() như sau:

#include <iostream>
#include <typeinfo>

using namespace std;

int main() {
    int a = 123;
    float b = 3.45;

    cout << "Kieu cua a: "<< typeid(a).name() <<endl;
    cout << "Kieu cua b: "<<typeid(b).name() <<endl;
    
    //Kiểu sau khi thay đổi
    cout << "Kieu thay doi cua a: "<< typeid(to_string(123)).name() <<endl;
    cout << "Kieu thay doi cua b: "<<typeid(to_string(3.1415)).name() <<endl;
    return 0;
}

Kết quả, hai biến a và b với kiểu int và float đã được chuyển sang kiểu string bằng hàm to_string như sau:

Kieu cua a: i
Kieu cua b: f
Kieu thay doi cua a: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Kieu thay doi cua b: NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
Ép Kiểu Int Sang String Trong C++
Ép Kiểu Int Sang String Trong C++

VIII. Ép Kiểu String Sang Char Trong C+

 

IX. Ép Kiểu Int Sang Char Trong C++

X. Phân Loại Ép Kiểu Trong C++

Phân loại ép kiểu trong C++

Trong C++, có hai loại ép kiểu dữ liệu:

  1. Nới rộng (widening): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu có kích thước lớn hơn. Kiểu biến đổi này không làm mất thông tin.
  2. Thu hẹp (narrowwing): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước lớn hơn sang kiểu có kích thước nhỏ hơn. Kiểu biến đổi này có thể làm mất thông tin

1. Nới rộng (widening)

Phân Loại Ép Kiểu Trong C++
Phân Loại Ép Kiểu Trong C++

Nới rộng (widening): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu có kích thước lớn hơn. Kiểu biến đổi này không làm mất thông tin. Ví dụ chuyển từ int sang float. Chuyển kiểu loại này có thế được thực hiện ngầm định bởi trình biên dịch.

Ví dụ:

#include <iostream>
 
using namespace std;
  
int main() {
    int i = 100;
    long l = i;    // khong yeu cau chi dinh ep kieu
    float f = l;   // khong yeu cau chi dinh ep kieu
    cout << "Gia tri Int: " << i << endl;
    cout << "Gia tri Long: " << l << endl;
    cout << "Gia tri Float: " << f << endl;
    return 0;
}

Kết quả:

Giá trị Int: 100
Giá trị Long: 100
Giá trị Float:  100

2.Thu hẹp (narrowwing)

Phân Loại Ép Kiểu Trong C++
Phân Loại Ép Kiểu Trong C++

Thu hẹp (narrowwing): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước lớn hơn sang kiểu có kích thước nhỏ hơn. Kiểu biến đổi này có thể làm mất thông tin như ví dụ ở trên. Chuyển kiểu loại này không thể thực hiện ngầm định bởi trình biên dịch, người dùng phải thực hiện chuyển kiểu tường minh.

Ví dụ:

#include <iostream>
 
using namespace std;
  
int main() {
    double d = 100.04;
    long l = (long) d; // yeu cau chi dinh kieu du lieu (long)
    int i = (int) l; // yeu cau chi dinh kieu du lieu (int)
    cout << "Gia tri Double: " << d << endl;
    cout << "Gia tri Long: " << l << endl;
    cout << "Gia tri Int: " << i << endl;
    return 0;
}

Kết quả:

Giá trị Double: 100.04
Giá trị Long: 100
Giá trị Int:  100

The post Ép Kiểu Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/ep-kieu-trong-c/

Căn Bậc 2 Trong C++

Trong bài viết này chúng ta sẽ tìm hiểu về hàm sqrt() trong C++. Đây là hàm được sử dụng rất nhiều trong các ngôn ngữ lập trình khi giải các bài toán.

I. Căn Bậc 2 Trong C++ Là Gì ?

Hàm sqrt() là hàm có sẵn trong thư viện math, vì vậy trước khi sử dụng nó hãy khai báo thư viện đã nhé: #include<math.h>

Hàm sqrt() là hàm được sử dụng để tính căn bậc hai của một số. Tham số truyền vào nó là một số bất kỳ và giá trị trả về là căn bậc hai của tham số đó.

Cú pháp:

sqrt(x)

Trong đó x là một số truyền vào bất kỳ, có thể là số nguyên hoặc số thực.

Căn Bậc 2 Trong C++ Là Gì ?
Căn Bậc 2 Trong C++ Là Gì ?

II. Hàm Tính Căn Bậc 2 Trong C++

Hôm nay Techacademy quay lại cùng với một bài toán đơn giản mà phức tạp. Chắc hẳn khi học lập trình bạn nào cũng đã từng tính căn bậc hai của một số. Chắc không ai quên được tên hàm nó chính là hàm sqrt(). Nhưng các bạn đã bao giờ tự hỏi làm sao để có thể tự viết hàm sqrt() này chưa? Hãy cũng mình đi tìm giải pháp trong bài viết này nhé.

Tính căn bậc hai sử dụng hàm trong C/C++

 
// Code from https://techacademy.edu.vn
#include <stdio.h>
#include <math.h>
 
int main()
{
    int x;
    printf("Input x: ");
    scanf("%d", &x);
    printf("Sqrt of %d = %f\n", x, sqrt(x));
}

Chạy demo:

 
Input x: 5
Sqrt of 5 = 2.236068

Tìm căn bậc hai của một số không dùng hàm thì sao?

Ý tưởng tìm căn bậc hai:

  • Khai báo 1 epsilon đặt sai số chấp nhận, vì căn bậc hai của một số có thể là số thập phân vô hạn
  • Khởi tạo kết quả bằng 1.0

Nếu kết quả có sai số cao hơn epsilon, cập nhật lại kết quả theo công thức

 
result = (number/result - result) / 2 + result;
  • Mình sẽ thu hẹp dần giới hạn trên và giới hạn dưới của kết quả, lấy trung bình hiệu khoảng cách giới hạn đó để cập nhật kết quả. Điều này luôn đảm bảo rằng giới hạn trên dưới sẽ bị thu hẹp nhưng sẽ luôn bao bọc đáp án.
  • Nếu kết quả có sai số nhỏ hơn EPSILON thì dừng lại và lấy kết quả đó làm đáp án.

Ví dụ: Bạn cần tính căn bậc 2 của 5.

  • Ta khởi tạo kết quả là 1.0. Kết quả này dĩ nhiên không đúng rồi, nên đáp số sẽ nằm trong khoảng 1.0 và 5/1.0 = 5.0.
  • Lấy một nửa hiệu khoảng 1.0 đến 5.0 là 1.0 + (5.0 – 1.0)/2 được 3.0. Nhưng 3.0 lớn hơn kết quả thực(bình phương là biết, trong code thì sai số để check),
  • Lại lấy nửa hiệu khoảng từ 5/3.0 đến 3.0 cộng vào kết quả hiện tại(3.0) = 3.0 + (5/3.0 – 3.0) = 2.33…
  • Cứ làm tiếp tục như vậy cho tới khi sai số nhỏ hơn EPSILON

Code đầy đủ cho ý tưởng này là

 
// Code from https://techacademy.edu.vn
 
#include <stdio.h>
#include <math.h>
 
#define EPSILON 0.0001f
 
double mySqrt(int number)
{
    double result = 1.0f;
    while (fabs(result * result - number) / number >= EPSILON)
        result = (number / result  - result) / 2 + result;
    return result;
}
 
int main()
{
    int x;
    printf("Input x: ");
    scanf("%d", &x);
    printf("Sqrt of %d = %1.9f\n", x, mySqrt(x));
    return 0;
}

Và chạy thử xem sao

 
Input x: 5
Sqrt of 5 = 2.236069
Hàm Tính Căn Bậc 2 Trong C++
Hàm Tính Căn Bậc 2 Trong C++

III. Ví Dụ Về Căn Bậc 2 Trong C++

Trong phần này mình sẽ thực hiện hai ví dụ sử dụng hàm sqrt() để các bạn có thể so sánh kết quả nhé.

Ví dụ 1: Ở ví dụ này mình sẽ khai báo và khởi tạo giá trị x = 9, sau đó gọi hàm sqrt() để tính căn bậc hai của x.

#include <iostream>
#include <math.h>
using namespace std;
 
int main()
{
  //khai báo và khởi tạo giá trị x = 9
  double x = 9, result;
  //gọi hàm sqrt() để tính căn bậc hai của x
    result = sqrt(x);
  //hiển thị kết quả ra màn hình
    cout << "Căn bậc hai của " << x << " là " << result << endl;
   
  cout<<"\n--------------------------------------------\n";
  cout<<"Chương trình này được đăng tại techacademy.edu.vn";
}

Kết quả:

Ví Dụ Về Căn Bậc 2 Trong C++
Ví Dụ Về Căn Bậc 2 Trong C++

Ví dụ 2: Ở ví dụ này mình sẽ thay đổi giá trị của x thành 125, cùng xem kết quả nhé.

#include <iostream>
#include <math.h>
using namespace std;
 
int main()
{
  //khai báo và khởi tạo giá trị x = 125
  double x = 125, result;
  //gọi hàm sqrt() để tính căn bậc hai của x
    result = sqrt(x);
  //hiển thị kết quả ra màn hình
    cout << "Căn bậc hai của " << x << " là " << result << endl;
   
  cout<<"\n--------------------------------------------\n";
  cout<<"Chương trình này được đăng tại Freetuts.net";
}

Kết quả:

Ví Dụ Về Căn Bậc 2 Trong C++
Ví Dụ Về Căn Bậc 2 Trong C++

IV. Căn Bậc N Trong C

Bài này mình sẽ hướng dẫn các bạn mới học lập trình cách để tính căn bậc n với n là một số bất kỳ nhé, nhưng n >= 2 nha. Chỉ là chỉ lại giúp các bạn nhớ lại toán học thôi ^^.

Một số thắc mắc của các bạn sinh viên:

  • Tình hình là em đang bí trong cách tính căn bậc n của 1 số. Thực tình em mới học môn này được 2 tuần nên thực tình ko biết giải quyết thế nào, ai biết phép toán của nó ra sao thì giúp em với?
  • Trong C chỉ có hàm sqrt để tính căn bậc 2, vậy nếu em muốn tính căn bậc 3 hay căn bậc n của một số thì làm thế nào ạ?

Tính căn bậc 3 trong C/C++

Để tính căn bậc 3 trong C/C++, bạn sử dụng công thức toán học sau. Công thức này đúng với căn bậc n luôn nhé. Bạn có thể áp dụng để tính căn bậc n của số bất kỳ:

Như vậy, với một số x = 8 chẳng hạn, bạn muốn tính căn bậc 3 của 8. Khi đó, trong công thức trên x = 8, n = 3, m = 1. Vậy ta sẽ phải tính x1/3

Để tính ax trong C/C++, chúng ta có thể sử dụng hàm pow() có cú pháp như sau:

 
double pow (double base, double exponent);

Ví dụ dưới đây sử dụng C/C++ để tính căn bậc 3 của 8 theo cách trên. Các bạn lưu ý em kiểu cho số mũ nhé. Vì 2 số nguyên chia cho nhau sẽ chỉ ra giá trị nguyên(1/3 = 0).

 
#include <stdio.h>
#include <math.h>
 
int main ()
{
    int a = 8;
    printf("%f", pow(a, 1.0/3));
}

Kết quả: 2.000000

Như vậy, để tính căn bậc n của số a, bạn sẽ dùng hàm pow để tính kết quả của phép tính a1/n

Tính căn bậc 3 không dùng hàm pow

Để tính căn bậc 3 của 1 số dương a ( tổnq quát căn bậc n ), ta phải xây dựng một chuỗi ( toán học ) mà chuỗi này hội tụ về căn bậc 3 của a. Sau đó lặp hữu hạn 1 số lần để được kết quả với độ chính xác nào đó chấp nhận được. Sau day la chuong trinh trong C de thuật tính căn bậc 3 cua 2 voi độ chính xác 0.00000001 chỉ bằng các phép toán +, – , *, / mà thôi.

Dưới đây là lời giải tính căn bậc 3 của 2 của một thành viên trong Cộng đồng C Việt:

 
////////////////////////////////////////////////////////////////////////
// Cube root of 2 by Nguyen Van Noi - DHTG
// Email : nvnoi76@yahoo.com
///////////////////////////////////////////////////////////////////////
 
#include <stdio.h>
double myabs(double x)
{
    return ((x>=0)?x:(-x));
}
void main()
{
    double a=2.0, xo, xn=1, e=1e-8;
    do
    {
       xo=xn;
       xn=(a/xo/xo+2.0*xo)/3.0;
    }
    while (myabs(xn-xo)>e);
    printf("Can bac 3 cua 2 = %1.8f\n",xn);
}

Một lời giải khác do mình tổng hợp:

Ý tưởng: Tìm phần nguyên trước, sau đó tìm phần thập phân.

Với cách làm này, bạn chỉ thu được kết quả xấp xỉ với đáp án chính xác. Độ chính xác phụ thuộc vào giá trị precision mà bạn mong muốn.

 
#include <stdio.h>
 
double cubeRoot(double n) {
   double i, precision = 0.000001;
   
   for(i = 1; (i*i*i) <= n; ++i);   //tim phan nguyen
 
   for(--i; (i*i*i) < n; i += precision);  //tim phan thap phan
   
   return i;
}
 
int main() {
   int n = 125;
 
   printf("Can bac ba cua %d = %lf", n, cubeRoot(n));
 
   return 0;
}

Chạy thử:

 
Can bac 3 cua 125 = 5.000000
Căn Bậc N Trong C
Căn Bậc N Trong C

V. Dấu Căn Bậc 2 Trong C++

Hàm sqrt() trong C

Hàm double sqrt(double x) trong Thư viện C trả về căn bậc hai của x.

Khai báo hàm sqrt() trong C

Dưới đây là phần khai báo cho hàm sqrt() trong C:

double sqrt(double x)

Tham số

x − Đây là giá trị số thực dấu chấm động.

Trả về giá trị

Hàm này trả về căn bậc hai của x.

Ví dụ

Chương trình C sau minh họa cách sử dụng của hàm sqrt() trong C:

#include <stdio.h>
#include <math.h>

int main ()
{

   printf("Can bac hai cua %lf bang %lf\n", 4.0, sqrt(4.0) );
   printf("Can bac hai cua %lf bang %lf\n", 5.0, sqrt(5.0) );
   
   return(0);
}

Biên dịch và chạy chương trình C trên sẽ cho kết quả:

Dấu Căn Bậc 2 Trong C++
Dấu Căn Bậc 2 Trong C++

VI. Hàm Căn Bậc 3 Trong C++

Bài tập C: Tìm căn bậc ba của một số

Tương tự như cách tìm căn bậc hai (không sử dụng hàm sqrt()), bài tập C này có thể được giải theo hai bước:

Tìm phân nguyên

Tìm phần thập phân

Đây chỉ là cách giải cho kết quả xấp xỉ, và độ chính xác phụ thuộc vào số các số sau dấu thập phân.

Bạn theo dõi phần code dưới đây để hiểu cách tìm căn bậc ba của một số bất kỳ trong C.

Chương trình C

Dưới đây là chương trình C để giải bài tập tìm căn bậc ba của một số trong C:

#include <stdio.h>

double cubeRoot(double n) {
   double i, precision = 0.000001;
   
   for(i = 1; (i*i*i) <= n; ++i);   //tim phan nguyen

   for(--i; (i*i*i) < n; i += precision);  //tim phan thap phan
   
   return i;
}

int main() {
   int n = 125;

   printf("Can bac ba cua %d = %lf", n, cubeRoot(n));

   return 0;
}

Biên dịch chương trình C trên sẽ cho kết quả:

Hàm Căn Bậc 3 Trong C++
Hàm Căn Bậc 3 Trong C++

VII. Bài Tập Về Căn Bậc 2 Trong C++

Đề bài

Viết chương trình giải phương trình bậc 2 trong C++. Phương trình bậc 2 có dạng:

 Bài Tập Về Căn Bậc 2 Trong C++
Bài Tập Về Căn Bậc 2 Trong C++

Lời giải

Lời giải của chúng tôi sử dụng phương thức sqrt() để tính căn bậc 2 của một số trong C++.

#include <iostream>
#include <cmath>
  
using namespace std;
  
/**
 * giai phuong trinh bac 2: ax2 + bx + c = 0
 * 
 * @param a: he so bac 2
 * @param b: he so bac 1
 * @param c: so hang tu do
 */
void giaiPTBac2(float a, float b, float c) {
    // kiem tra cac he so
    if (a == 0) {
        if (b == 0) {
            printf("Phuong trinh vo nghiem!");
        } else {
            printf("Phuong trinh co mot nghiem: x = %f", (-c / b));
        }
        return;
    }
    // tinh delta
    float delta = b*b - 4*a*c;
    float x1;
    float x2;
    // tinh nghiem
    if (delta > 0) {
        x1 = (float) ((-b + sqrt(delta)) / (2*a));
        x2 = (float) ((-b - sqrt(delta)) / (2*a));
        printf("Phuong trinh co 2 nghiem la: x1 = %f va x2 = %f", x1, x2);
    } else if (delta == 0) {
        x1 = (-b / (2 * a));
        printf("Phong trinh co nghiem kep: x1 = x2 = %f", x1);
    } else {
        printf("Phuong trinh vo nghiem!");
    }
}
 
/**
 * ham main
 */
int main() {
 float a, b, c;
    cout << "Nhap he so bac 2, a = ";
    cin >> a;
    cout << "Nhap he so bac 1, b = ";
    cin >> b;
    cout << "Nhap so hang tu do, c = ";
    cin >> c;
    giaiPTBac2(a, b, c);
    return 1;
}

Kết quả:

Nhap he so bac 2, a = 2
Nhap he so bac 1, b = 3
Nhap so hang tu do, c = 1
Phuong trinh co 2 nghiem la: x1 = -0.500000 va x2 = -1.000000

VIII. Tìm Căn Bậc Hai Không Dùng Sqrt

Tìm căn bậc hai không sử dụng hàm sqrt? Bạn nghĩ sao? Hôm nay Techacademy quay lại cùng với một bài toán đơn giản mà phức tạp. Chắc hẳn khi học lập trình bạn nào cũng đã từng tính căn bậc hai của một số. Chắc không ai quên được tên hàm nó chính là hàm sqrt(). Nhưng các bạn đã bao giờ tự hỏi làm sao để có thể tự viết hàm sqrt() này chưa? Hãy cũng mình đi tìm giải pháp trong bài viết này nhé.

Tìm Căn Bậc Hai Không Dùng Sqrt
Tìm Căn Bậc Hai Không Dùng Sqrt

Tìm căn bậc hai của một số không dùng hàm thì sao?

Ý tưởng tìm căn bậc hai:

  • Khai báo 1 epsilon đặt sai số chấp nhận, vì căn bậc hai của một số có thể là số thập phân vô hạn.
  • Khởi tạo kết quả bằng 1.0

Nếu kết quả có sai số cao hơn epsilon, cập nhật lại kết quả theo công thức

 
result = (number/result – result) / 2 + result;
  • Mình sẽ thu hẹp dần giới hạn trên và giới hạn dưới của kết quả, lấy trung bình hiệu khoảng cách giới hạn đó để cập nhật kết quả. Điều này luôn đảm bảo rằng giới hạn trên dưới sẽ bị thu hẹp nhưng sẽ luôn bao bọc đáp án.
  • Nếu kết quả có sai số nhỏ hơn EPSILON thì dừng lại và lấy kết quả đó làm đáp án.

Ví dụ: Bạn cần tính căn bậc 2 của 5.

  • Ta khởi tạo kết quả là 1.0. Kết quả này dĩ nhiên không đúng rồi, nên đáp số sẽ nằm trong khoảng 1.0 và 5/1.0 = 5.0.
  • Lấy một nửa hiệu khoảng 1.0 đến 5.0 là 1.0 + (5.0 – 1.0)/2 được 3.0. Nhưng 3.0 lớn hơn kết quả thực(bình phương là biết, trong code thì sai số để check),
  • Lại lấy nửa hiệu khoảng từ 5/3.0 đến 3.0 cộng vào kết quả hiện tại(3.0) = 3.0 + (5/3.0 – 3.0) = 2.33…
  • Cứ làm tiếp tục như vậy cho tới khi sai số nhỏ hơn EPSILON

Code đầy đủ cho ý tưởng này là

 
// Code from https://nguyenvanhieu.vn
 
#include <stdio.h>
#include <math.h>
 
#define EPSILON 0.0001f
 
double mySqrt(int number)
{
    double result = 1.0f;
    while (fabs(result * result – number) / number >= EPSILON)
        result = (number / result  – result) / 2 + result;
    return result;
}
 
int main()
{
    int x;
    printf(“Input x: “);
    scanf(“%d”, &x);
    printf(“Sqrt of %d = %1.9fn”, x, mySqrt(x));
    return 0;
}

Và chạy thử xem sao

 
Input x: 5
Sqrt of 5 = 2.236069
 

The post Căn Bậc 2 Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/can-bac-2-trong-c/

Con Trỏ Trong C++

Con trỏ trong lập trình là 1 định nghĩa hơi khó nhằn đối với các bạn mới học về C++. Không ngoa khi nói rằng C++ khó vì có con trỏ. Tuy nhiên ví như làm chủ được con trỏ, bạn có thể hiểu và thao tác với dữ liệu trong bộ nhớ máy tính, và những kiến ​​thức liên quan mà bạn học được thông qua con trỏ cũng rất hữu ích cho việc học các ngôn ngữ hướng đối tượng sau này như Java chẳng hạn.

Hãy cùng tìm hiểu con trỏ trong C++ là gì, quan hệ giữa con trỏ và địa chỉ trong C++, cấu trúc, vai trò và cách sử dụng con trỏ sau bài học này nhé.

I. Khai Báo Con Trỏ Trong C++

Để khai báo con trỏ trong C++, chung ta sử dụng với cấu trúc ngữ pháp sau đây:

type *p;

Trong đó type là kiểu dữ liệu của con trỏ, và p là tên con trỏ. Lưu ý là kiểu dữ liệu của con trỏ phải giống với kiểu dữ liệu của dữ liệu cần lưu địa chỉ trong con trỏ.

Ví dụ, chúng ta khai báo con trỏ tên p với kiểu int như sau:

int *p;

Lưu ý là các cách viết sau đây cũng được chấp nhận khi khai báo con trỏ trong C++:

int* p;
int * p;
Khai BáoKhai Báo Con Trỏ Trong C++Con Trỏ Trong C++
Khai Báo Con Trỏ Trong C++

II. Cấp Phát Bộ Nhớ Cho Con Trỏ Trong C++

Ngôn ngữ C++ hỗ trợ ba loại cấp phát bộ nhớ cơ bản, hai loại trong số đó bạn đã được học ở các bài học trước:

1. Cấp phát bộ nhớ tĩnh (Static memory allocation):

  • Xảy ra trên các biến tĩnh và biến toàn cục.
  • Vùng nhớ của các loại biến này được cấp phát một lần khi chương trình bắt đầu chạy và vẫn tồn tại trong suốt thời gian tồn tại của chương trình.
  • Kích thước của biến/mảng nên được biết tại thời điểm biên dịch chương trình.

2. Cấp phát bộ nhớ tự động (Automatic memory allocation):

  • Xảy ra trên các tham số hàm và biến cục bộ.
  • Vùng nhớ của các loại biến này được cấp phát khi chương trình đi vào khối lệnh và được giải phóng khi khối lệnh bị thoát.
  • Kích thước của biến/mảng phải được biết tại thời điểm biên dịch chương trình.

3. Cấp phát bộ nhớ động (Dynamic memory allocation) sẽ được kể tới trong bài học này.

Trong tất cả những trường hợp, cấp phát bộ nhớ tĩnh và tự động có thể đáp ứng tốt các đề nghị của chương trình. Tuy nhiên, ta cùng xem ví dụ bên dưới:

Ví dụ: Chúng ta cần sử dụng một chuỗi để lưu tên của người dùng, nhưng chúng ta không biết tên của họ dài bao nhiêu cho đến khi họ nhập tên. Hoặc chúng ta cần lưu trữ danh sách nhân viên trong một công ty, nhưng chúng ta không biết trước được công ty đó sẽ có bao nhiêu nhân viên.

Đối với cấp phát bộ nhớ tĩnh và tự động, kích thước của biến/mảng phải được biết tại thời điểm biên dịch chương trình. Vì vậy, điều tốt nhất chúng ta có thể làm là cố gắng đoán một kích thước tối đa của các biến đó:

char name[30]; // tên không quá 30 ký tự
Staff staff[500]; // công ty không quá 500 nhân viên

Khuyết điểm của cách khai báo trên:

+ Gây lãng phí bộ nhớ nếu các biến không thực sự sử dụng hết kích thước khi khai báo. Ví dụ: nếu công ty chỉ có 100 nhân viên, chúng ta có 400 vùng nhớ nhân viên không được sử dụng tới.

+ Thứ hai, hầu hết các biến thông thường (bao gồm mảng tĩnh) được cấp phát trong một phần bộ nhớ gọi là ngăn xếp (stack). Kích thước bộ nhớ stack cho một chương trình khá nhỏ (khoảng 1Mb với Visual Studio), nếu yêu cầu cấp phát vùng nhớ vượt quá con số này, chương trình sẽ bị đóng bởi hệ điều hành với lỗi stack overflow.

char byte[1000000 * 2]; // khoảng 2Mb bộ nhớ => lỗi stack overflow 

+ Thứ ba, điều gì xảy ra nếu công ty có 600 nhân viên, trong khi mảng staff chỉ có 500 phần tử. Lúc này, chương trình sẽ bị giới hạn bởi kích thước được khai báo ban đầu.

Để giải quyết những hạn chế trên, cấp phát bộ nhớ động được ra đời.

Cấp Phát Bộ Nhớ Cho Con Trỏ Trong C++
Cấp Phát Bộ Nhớ Cho Con Trỏ Trong C++

III. Con Trỏ Trong C++ Dùng Để Làm Gì

Tác dụng của con trỏ trong c chính là để lưu giữ địa chỉ của dữ liệu trong bộ nhớ máy tính, và bằng cách truy cập vào địa chỉ này, chúng ta có thể lấy được giá trị của dữ liệu tại đó.

Ngoài ra thì giá trị của con trỏ cũng là một số, nên chúng ta cũng có thể thực hiện các phép tính toán với con trỏ, ví dụ như cộng thêm hoặc hoặc trừ đi một số lượng đơn vị.

Do đó, con trỏ trong C sẽ được dùng để làm 1 trong 2 công việc sau đây trong chương trình:

  1. Thao tác với địa chỉ bằng các phép tính toán với số được lưu trong nó
  2. Thao tác với giá trị tại địa chỉ mà nó lưu mà thôi.
Con Trỏ Trong C++ Dùng Để Làm Gì
Con Trỏ Trong C++ Dùng Để Làm Gì

IV. Mảng Con Trỏ Trong C++

Trước khi chúng ta hiểu về khái niệm mảng các con trỏ, chúng ta xem xét ví dụ sau, mà sử dụng một mảng gồm 3 số integer:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
 
   for (int i = 0; i < MAX; i++)
   {
      cout << "Gia tri cua var[" << i << "] = ";
      cout << var[i] << endl;
   }
   return 0;
}

Khi code trên được biên dịch và thực thi, nó cho kết quả sau:

Gia tri cua var[0] = 10
Gia tri cua var[1] = 100
Gia tri cua var[2] = 200

Có một tình huống khi chúng ta muốn duy trì một mảng, mà có thể lưu giữ các con trỏ tới một kiểu dữ liệu int hoặc char hoặc bất kỳ kiểu nào khác. Sau đây là khai báo một mảng của các con trỏ tới một integer:

int *contro[MAX];

Nó khai báo contro như là một mảng các con trỏ MAX kiểu integer. Vì thế, mỗi phần tử trong contro, bây giờ giữ một con trỏ tới một giá trị int. Ví dụ sau sử dụng 3 số integer, mà sẽ được lưu giữ trong một mảng các con trỏ như sau:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int *contro[MAX];
 
   for (int i = 0; i < MAX; i++)
   {
      contro[i] = &var[i]; // gan dia chi cua so nguyen.
   }
   for (int i = 0; i < MAX; i++)
   {
      cout << "Gia tri cua var[" << i << "] = ";
      cout << *contro[i] << endl;
   }
   return 0;
}

Khi code trên được biên dịch và thực thi, nó cho kết quả sau:

Gia tri cua var[0] = 10
Gia tri cua var[1] = 100
Gia tri cua var[2] = 200

Bạn có thể sử dụng một mảng các con trỏ tới ký tự để lưu giữ một danh sách các chuỗi như sau:

#include <iostream>
 
using namespace std;
const int MAX = 4;
 
int main ()
{
   char *tensv[MAX] = {
                   "Nguyen Thanh Tung",
                   "Tran Minh Chinh",
                   "Ho Ngoc Ha",
                   "Hoang Minh Hang",
   };

   for (int i = 0; i < MAX; i++)
   {
      cout << "Gia tri cua tensv[" << i << "] = ";
      cout << tensv[i] << endl;
   }
   return 0;
}

Chạy chương trình C++ trên sẽ cho kết quả như hình sau:

Mảng Con Trỏ Trong C++
Mảng Con Trỏ Trong C++

V. Con Trỏ Hàm Trong C++

Con trỏ hàm là một biến lưu trữ địa chỉ của một hàm, thông qua biến đó, ta có thể gọi hàm mà nó trỏ tới.

Cú pháp khai báo con trỏ hàm:

<kiểu trả về> (*<tên con trỏ>)(<danh sách tham số>);

int(*fcnPtr)(int); // con trỏ hàm nhận vào 1 biến kiểu int và trả về kiểu int
void(*fcnPtr)(int, int); // con trỏ hàm nhận vào 2 biến kiểu int và trả về kiểu void

Chú ý: Dấu ngoặc () quanh *fcnPtr là bắt buộc.

VI. Con Trỏ Trong Class C++

Một con trỏ tới một lớp trong C++ được thực hiện theo cách giống hệt như một con trỏ tới một cấu trúc; và để truy cập các thành viên của một con trỏ tới một lớp bạn sử dụng toán tử truy cập thành viên trong C++ là toán tử ->, như khi bạn thực hiện với các con trỏ tới cấu trúc. Cũng như với tất cả con trỏ, bạn phải khai báo con trỏ trước khi sử dụng nó.

Bạn thử ví dụ sau để hiểu khái niệm con trỏ tới một lớp trong C++:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      // phan dinh nghia Constructor
      Box(double dai=1.0, double rong=1.0, double cao=1.0)
      {
         cout <<"Constructor duoc goi." << endl;
         chieudai = dai;
         chieurong = rong;
         chieucao = cao;
      }
      double theTich()
      {
         return chieudai * chieurong * chieucao;
      }
   private:
      double chieudai;     // chieu dai cua mot box
      double chieurong;    // chieu rong cua mot box
      double chieucao;     // chieu cao cua mot box
};

int main(void)
{
   Box Box1(2.4, 4.2, 2.2);    // khai bao box1
   Box Box2(4.5, 2.0, 3.2);    // khai bao box2
   Box *controBox;                // khai bao con tro toi mot class.

   // luu giu dia chi cua doi tuong dau tien
   controBox = &Box1;

   // bay gio thu truy cap mot thanh vien boi su dung toan tu truy cap thanh vien
   cout << "The tich cua Box1 la: " << controBox->theTich() << endl;

   // luu giu dia chi cua doi tuong thu hai
   controBox = &Box2;

   // bay gio thu truy cap mot thanh vien boi su dung toan tu truy cap thanh vien
   cout << "The tich cua Box2 la: " << controBox->theTich() << endl;
  
   return 0;
}

Biên dịch và chạy chương trình C++ trên sẽ cho kết quả sau:

Con Trỏ Trong Class C++
Con Trỏ Trong Class C++

VII. Con Trỏ Trong Struct C++

Chào các bạn đang theo dõi khóa học lập trình trực tuyến ngôn ngữ C++.
Chúng ta cùng tiếp tục tìm hiểu về kiểu dữ liệu tự định nghĩa thông qua từ khóa struct mà ngôn ngữ C++ hỗ trợ. Trong bài học này, mình sẽ trình bày về kiểu struct khi sử dụng kết hợp với con trỏ.

Như các bạn đã học trong bài trước, sau khi chúng ta tự định nghĩa một struct, compiler sẽ coi tên gọi của struct đó như là một kiểu dữ liệu. Điều này có nghĩa khi chúng ta sử dụng các kiểu dữ liệu built-in để tạo ra các biến, tham chiếu hoặc con trỏ thì chúng ta cũng có thể sử dụng kiểu struct để tạo ra biến struct, tham chiếu struct và con trỏ kiểu struct (Pointer to struct).

Pointer to struct

Đầu tiên, chúng ta cùng định nghĩa một kiểu dữ liệu theo ý muốn. Dưới đây, mình định nghĩa một kiểu dữ liệu có tên là Letter:

struct Letter
{
   
};

Trong struct Letter mình chưa định nghĩa các trường dữ liệu, lúc này Letter là một kiểu dữ liệu rỗng. Nhưng ngôn ngữ C++ vẫn đặt kích thước của kiểu Letter này là 1 bytes.

Mục đích là để đảm bảo địa chỉ của 2 biến được tạo ra sẽ có địa chỉ khác nhau. Tuy nhiên, định nghĩa ra một struct rỗng không có tác dụng gì trong chương trình, chúng ta cùng thêm vào một số trường dữ liệu cho Letter:

struct Letter
{
   char from[50];
   char to[50];
};

Một lá thư sẽ có thông tin về người gửi và người nhận, nên mình thêm vào 2 trường dữ liệu kiểu C-style string dùng để lưu thông tin mà người dùng điền vào một lá thư.

Mình vừa định nghĩa xong một kiểu dữ liệu mới để phục vụ cho chương trình của mình. Bây giờ chúng ta cùng tạo ra một đơn vị từ kiểu dữ liệu trên (mình thao tác luôn trong hàm main):

int main()
{
   Letter myLetter;
   
   return 0;
}

Với mỗi biến kiểu Letter được tạo ra, chương trình sẽ yêu cầu cấp phát 100 bytes (50 bytes cho trường dữ liệu from và 50 bytes cho trường dữ liệu to), và chắc chắn rồi, biến đó sẽ có một địa chỉ xác định được thông qua toán tử address-of.

int main()
{
   Letter myLetter;
   std::cout << "Address of myLetter: " << &myLetter << std::endl;
   std::cout << "Address of from field: " << &myLetter.from << std::endl;
   
   return 0;
}

Ở đoạn chương trình trên, mình in ra địa chỉ của biến myLetter, đồng thời in ra luôn địa chỉ của trường dữ liệu from của biến myLetter. Kết quả cho thấy 2 địa chỉ được in ra có giá trị hoàn toàn giống nhau. Điều này có nghĩa địa chỉ của trường dữ liệu đầu tiên trong một biến struct cũng là địa chỉ của biến struct đó.

Các bạn có thể liên hệ struct với mảng một chiều trong C/C++, khi mảng một chiều mà tập hợp các phần tử có cùng kiểu dữ liệu được bao bọc bởi tên mảng một chiều và địa chỉ của mảng một chiều cũng là địa chỉ của phần tử đầu tiên trong mảng, một biến struct sẽ bao gồm tập hợp các trường dữ liệu mà địa chỉ của biến struct sẽ là địa chỉ của trường dữ liệu được khai báo đầu tiên trong struct.

Và như các bạn cũng đã học về con trỏ (Pointer), kiểu dữ liệu của con trỏ dùng để xác định kiểu dữ liệu của vùng nhớ mà con trỏ có thể trỏ đến. Vậy thì để cho con trỏ trỏ đến một địa chỉ của biến kiểu struct, chúng ta cần có một con trỏ cùng kiểu struct với biến được trỏ đến.

Letter myLetter;
Letter *pLetter = &myLetter;

Dù kích thước của kiểu dữ liệu struct có lớn bao nhiêu, biến con trỏ cũng chỉ có kích thước 4 bytes trên hệ điều hành 32 bits và kích thước 8 bytes trên hệ điều hành 64 bits (đủ để trỏ đến toàn bộ địa chỉ trên bộ nhớ ảo).

Access struct members

Trong bài học trước, các bạn đã biết cách truy cập đến các trường dữ liệu của các biến struct thông qua member selection operator (dấu chấm). Nhưng khi sử dụng Pointer to struct, member selection operator được sử dụng dưới cách viết khác. Để phân biệt sự khác nhau khi sử dụng member selection operator cho biến struct thông thường và một Pointer to struct, các bạn cùng xem ví dụ bên dưới:

struct BankAccount
{
   __int64 accountNumber;
   __int64 balance;
};

int main()
{
   BankAccount myAccount = { 123456789, 50 }; // $50
   BankAccount *pAccount = &myAccount; 

   std::cout << "My bank account number: " << myAccount.accountNumber << std::endl;
   std::cout << "My bank account number: " << pAccount->accountNumber << std::endl;

   std::cout << "My balance: " << myAccount.balance << std::endl;
   std::cout << "My balance: " << pAccount->balance << std::endl;

   return 0;
}

Như các bạn thấy, kết quả của việc truy xuất giá trị thông qua tên biến struct và con trỏ kiểu struct là hoàn toàn giống nhau, và chúng đều dùng toán tử member selection. Tuy nhiên, để phân biệt biến con trỏ và biến thông thường, biến con trỏ kiểu struct sẽ truy cập đến các trường dữ liệu trong vùng nhớ bằng toán tử (->). Hai toán tử này cùng tên, chỉ khác nhau về cách biểu diễn.

Một số nhầm lần khi sử dụng struct và Pointer to struct

Khi mới tìm hiểu về Pointer to struct, các bạn có thể bị nhầm lẫn giữa cách khởi tạo hoặc gán giá trị cho biến struct thông thường và biến con trỏ struct.

struct BankAccount
{
   __int64 accountNumber;
   __int64 balance;
};

int main()
{
   BankAccount myAccount = { 12345, 50 };

   BankAccount *pAccount = { 12345, 50 }; //error

   return 0;
}

Đoạn chương trình trên báo lỗi vì biến con trỏ chỉ nhận giá trị là địa chỉ. Tuy nhiên, lỗi này có thể thấy dễ dàng vì Visual studio đưa ra thông báo lỗi ngay. Dưới đây là cách gán giá trị đúng khi mình sử dụng toán tử dereference cho biến con trỏ struct để thay đổi giá trị bên trong vùng nhớ:

struct BankAccount
{
   __int64 accountNumber;
   __int64 balance;
};

int main()
{
   BankAccount myAccount = { 0, 0 };

   BankAccount *pAccount = &myAccount;

   *pAccount = { 12345, 50 };

   std::cout << pAccount->accountNumber << " " << pAccount->balance << std::endl;

   return 0;
}

Hoặc một cách khác là chúng ta cấp phát vùng nhớ cho biến con trỏ struct, và dereference đến đó để gán giá trị cho nó:

BankAccount *pAccount = new BankAccount;
*pAccount = { 12345, 50 };

Và các bạn lưu ý khi sử dụng biến kiểu con trỏ struct thì chúng ta sử dụng toán tử member selection này (->). Có một số bạn nhầm lẫn giữa biến con trỏ struct và trường dữ liệu kiểu con trỏ. Ví dụ:

struct BankAccount
{
   char *name;
   __int64 accountNumber;
   __int64 balance;
};

Mình thêm vào struct một trường dữ liệu kiểu con trỏ char nhưng việc truy xuất đến trường dữ liệu này không có gì thay đổi khi mình sử dụng biến struct thông thường.

BankAccount myAccount = { "Le Tran Dat", 12345, 50 };

std::cout << myAccount.name << std::endl;
std::cout << myAccount.accountNumber << std::endl;
std::cout << myAccount.balance << std::endl;

Sẽ phức tạp hơn một chút khi các bạn sử dụng các nested struct. Ví dụ:

struct BankAccount
{
   Date registrationDate;
   __int64 accountNumber;
   __int64 balance;
};

int main()
{
   BankAccount *pAccount = new BankAccount;
   *pAccount = { {2, 5, 2016}, 12345, 50 };

   std::cout << pAccount->registrationDate.year << std::endl;

   return 0;
}

Như các bạn thấy, từ biến con trỏ pAccount truy xuất vào các trường dữ liệu bên trong thì mình dùng toán tử (->), nhưng trường dữ liệu Date trong struct BankAccount là biến thông thường, nên mình dùng dấu chấm để truy xuất dữ liệu ngày đăng kí.

Con Trỏ Trong Struct C++
Con Trỏ Trong Struct C++

VIII. Con Trỏ Null Trong C++

Con trỏ NULL trong C++ là một hằng với một giá trị là 0 được định nghĩa trong một vài thư viện chuẩn, gồm iostream.

#include <iostream>
 
using namespace std;
 
int main () {
    int  *ptr = NULL;
    cout << "Gia tri cua contro la " << ptr ;
    return 0;
}

Kết quả:

Gia tri cua contro la 0

Trên hầu hết các hệ điều hành, các chương trình không được phép truy cập bộ nhớ tại địa chỉ 0, vì bộ nhớ đó được dự trữ bởi hệ điều hành. Tuy nhiên, địa chỉ bộ nhớ 0 có ý nghĩa đặc biệt, nó chỉ ra rằng con trỏ không được trỏ tới một vị trí ô nhớ có thể truy cập. Nhưng theo qui ước, nếu một con trỏ chứa giá trị 0, nó được xem như là không trỏ tới bất cứ thứ gì.

Để kiểm tra một con trỏ null trong C++, bạn có thể sử dụng lệnh if như sau:

if(contro)     // true neu contro khong la NULL
if(!contro)    // true neu contro la NULL
Con Trỏ Null Trong C++
Con Trỏ Null Trong C++

IX. Nhập Xuất Mảng Bằng Con Trỏ Trong C++

Bằng cách sử dụng con trỏ mảng, chúng ta có thể chỉ định vị trí các phần tử trong mảng, cũng như là truy cập và lấy giá trị của các phần tử đó.

Ứng dụng điều này, chúng ta cũng có thể nhập xuất mảng bằng con trỏ trong C++ như sau.

Nhập mảng bằng con trỏ trong C++

Trong bài Nhập xuất mảng trong C++ chúng ta đã biết cách tạo hàm nhập trực tiếp các giá trị từ bàn phím vào mảng như sau:

/*Tạo hàm nhập mảng 1 chiều trong C++*/
void input_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for (short i = 0; i < length; i++) cin >> array[i];
}

Trong đó array và length lần lượt là tên và độ dài (số phần tử) của mảng cần nhập.

Để nhập mảng bằng con trỏ trong C++ chúng ta chỉ cần sử dụng giá trị con trỏ thay cho mảng, và thay vì dùng index để chỉ định vị trí nhập dữ liệu, thì chúng ta sẽ sử dụng trực tiếp giá trị con trỏ để chỉ định vị trí cần nhập. Chúng ta viết hàm nhập mảng bằng con trỏ trong C++ như sau:

/*Tạo hàm nhập mảng bằng con trỏ trong C++*/
void input_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for (short i = 0; i < length; i++) cin >> *(array + i);
}

Lưu ý tại đây chúng ta sử dụng lệnh cin để nhập dữ liệu vào mảng, nên cần chỉ định bản thân dữ liệu mà con trỏ chỉ đến bằng cách thêm dấu hoa thị * trước con trỏ.

Tuy nhiên nếu sử dụng hàm scanf được kế thừa từ ngôn ngữ C để nhập mảng bằng con trỏ trong C++, thay vì chỉ định dữ liệu thì chúng ta cần chỉ định địa chỉ của dữ liệu đã được lưu vào con trỏ như sau:

/*Tạo hàm nhập mảng bằng con trỏ trong C*/
void input_array(int *array, int length){
    //array: tên mảng
    //length: độ dài mảng
    for (short i = 0; i < length; i++) scanf("%d", (array + i));
}

Xuất mảng bằng con trỏ trong C++

Trong bài xuất xuất mảng trong C++ chúng ta đã biết cách tạo hàm xuất trực tiếp các giá trị của mảng như sau:

/*Tạo hàm xuất mảng 1 chiều trong C++*/
void show_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for(short i = 0; i < length; i++)  cout << array[i] <<' ';   
    cout << endl;
}

Trong đó array và length lần lượt là tên và độ dài (số phần tử) của mảng cần xuất.

Để xuất mảng bằng con trỏ trong C++ chúng ta chỉ cần sử dụng con trỏ thay cho mảng, và thay vì truy xuất giá trị các phần tử của mảng bằng index thì chúng ta sẽ dùng tên con trỏ và dấu hoa thị để xuất giá trị đó. Chúng ta viết hàm xuất mảng bằng con trỏ trong C++ như sau:

/*Tạo hàm xuất mảng bằng con trỏ trong C++*/
void show_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for(short i = 0; i < length; i++)  cout << *(array + i) <<' ';   
    cout << endl;
}

Chương trình mẫu nhập xuất mảng bằng con trỏ trong C++

Dưới đây là chương trình mẫu sử dụng các hàm trên để nhập xuất mảng bằng con trỏ trong C++:

#include <iostream>
using namespace std;

/*Tạo hàm nhập mảng bằng con trỏ trong C++*/
void input_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for (short i = 0; i < length; i++) cin >> *(array + i);
}

/*Tạo hàm xuất mảng bằng con trỏ trong C++*/
void show_array(int array[], int length){
    //array: tên mảng
    //length: độ dài mảng
    for(short i = 0; i < length; i++)  cout << *(array + i) <<' ';   
    cout << endl;
}

int main(){
    /*Nhập mảng bằng con trỏ trong C++*/
    int n;
    cout << ">>Nhap so phan tu: ";
    cin >>  n;
 
    int array[n], *p;
    cout << (">>Nhap phan tu:\n");
    input_array(array, n);
    
    /*Xuất mảng bằng con trỏ trong C++*/ 
    cout << (">>Mang vua nhap:\n"); 
    show_array(array, n);
}

Kết quả:

>>Nhap so phan tu: 5
>>Nhap phan tu:
1 2 3 4 5
>>Mang vua nhap:
1 2 3 4 5
Nhập Xuất Mảng Bằng Con Trỏ Trong C++
Nhập Xuất Mảng Bằng Con Trỏ Trong C++

X. Con Trỏ Và Tham Chiếu Trong C++

C và C++ hỗ trợ con trỏ cái mà khác với hầu hết các ngôn ngữ lập trình khác. Các ngôn ngữ khác bao gồm C ++, Java, Python, Ruby, Perl và PHP đều hỗ trợ tham chiếu.

Nhìn bề ngoài, cả hai tham chiếu và con trỏ đều rất giống nhau, cả hai đều được sử dụng để có một biến cung cấp quyền truy cập cho một biến khác. Với việc cả hai đều cung cấp nhiều khả năng giống nhau, người ta thường không rõ điều gì khác biệt giữa các cơ chế khác nhau này. Trong bài viết này, tôi sẽ cố gắng minh họa sự khác biệt giữa con trỏ và tham chiếu.

Con trỏ: Con trỏ là một biến chứa địa chỉ bộ nhớ của một biến khác. Một con trỏ cần được deference với toán tử * để truy cập vào vị trí bộ nhớ mà nó trỏ tới.

Tham chiếu: Một biến tham chiếu là một bí danh, tức là một tên khác của một biến đã tồn tại. Một tham chiếu, giống như một con trỏ, cũng được hiện thựcbằng cách lưu trữ địa chỉ của một đối tượng.
Một tham chiếu có thể được coi là một con trỏ hằng (đừng nhầm với một con trỏ đến một giá trị không đổi!) với tính năng tự động chuyển hướng, tức là trình biên dịch sẽ áp dụng toán tử * cho bạn.

int i = 3; 
 
// Một con trỏ đến biến i
// (lưu trữ địa chỉ của i)
int *ptr = &i; 
 
// Một tham chiếu (bí danh) của i.
int &ref = i;

Sự khác nhau giữa Con trỏ và Tham chiếu

Khởi tạo

Một con trỏ có thế được khai báo và khởi tạo đồng thời hoặc trên nhiều dòng.

C++
int a = 10;        
int *p = &a;    
hay
int *p;
p = &a;

Trong khi tham chiếu phải được khởi tạo cùng lúc với khai báo.

int a=10;
int &p=a;  //OK
nhưng
int &p;
p=a;      //NOK

Chú ý: Sự khác biệt này có thể thay đổi từ trình biên dịch này sang trình biên dịch khác. Sự khác biệt trên là đối với Turbo IDE.

Gán lại giá trị

Một con trỏ có thể được gán lại. Thuộc tính này hữu ích cho việc triển khai các cấu trúc dữ liệu như danh sách liên kết, cây, v.v. Xem ví dụ sau:

int a = 5;
int b = 6;
int *p;
p =  &a;
p = &b;

Trong khi đó, tham chiếu không gán lại mà phải được gán ngay lúc khởi tạo.

int a = 5;
int b = 6;
int &p = a;
int &p = b;   //Tại dòng này sẽ hiện lỗi là "không được khai báo nhiều lần"
 
//Tuy nhiên, lệnh như dưới đây thì ok.
int &q=p;

Địa chỉ bộ nhớ

Một con trỏ có địa chỉ bộ nhớ và kích thước riêng của nó trên ngăn xếp trong khi một tham chiếu chia sẻ cùng địa chỉ bộ nhớ (với biến gốc) nhưng cũng chiếm một số không gian trên ngăn xếp.

int &p = a;
cout << &p << endl << &a; // Địa chỉ của p và a là như nhau

Giá trị NULL

Con trỏ có thể được gán NULL trực tiếp, trong khi tham chiếu không thể. Các ràng buộc liên quan đến tham chiếu (không NULL, không gán lại) đảm bảo rằng các hoạt động cơ bản không rơi vào trường hợp ngoại lệ.

Con trỏ đến con trỏ

Bạn có thể có các con trỏ đến con trỏ cung cấp nhiều cấp độ chuyển hướng bổ sung. Trong khi đó, các tham chiếu chỉ cung cấp một mức chuyển hướng.

//Con trỏ,
int a = 10;
int *p;
int **q;  //Con trỏ đến con trỏ.
p = &a;
q = &p;
 
//Đối với tham chiếu,
 
int &p = a;
int &&q = p; //Lỗi, không có tham chiếu của tham chiếu.

Các phép tính toán học

Các phép toán số học khác nhau có thể được thực hiện trên con trỏ trong khi không có thứ gọi là Số học tham chiếu. (Nhưng bạn có thể lấy địa chỉ của một đối tượng được trỏ bởi một tham chiếu và thực hiện số học con trỏ trên đó như &obj + 5)

Khi nào sử dụng cái nào

Hiệu suất hoàn toàn giống nhau, vì các tham chiếu được triển khai bên trong dưới dạng con trỏ. Tuy nhiên, bạn vẫn có thể ghi nhớ một số điểm để quyết định khi nào sử dụng cái gì:

Sử dụng tham chiếu:

  • Trong các tham số hàm và kiểu trả về.

Sử dụng con trỏ:

  • Sử dụng con trỏ nếu cấn tính toán con trỏ số học hoặc NULL. Ví dụ đối với mảng (Lưu ý rằng truy cập mảng được thực hiện bằng cách sử dụng số học con trỏ).
  • Để triển khai các cấu trúc dữ liệu như danh sách liên kết, cây, v.v. và các thuật toán của chúng vì để trỏ ô khác nhau, chúng ta phải sử dụng khái niệm con trỏ.
Con Trỏ Và Tham Chiếu Trong C++
Con Trỏ Và Tham Chiếu Trong C++

XI. Ý Nghĩa Con Trỏ This Trong C++

Con trỏ this trong C++

This là một con trỏ đặc biệt dùng để trỏ đến địa chỉ của đối tượng hiện tại. Như vậy để truy cập đến các thuộc tính, phương thức của đối tượng hiện tại thì ta sẽ sử dụng con trỏ this. Hãy xem ví dụ dưới đây.

Ví dụ

#include <iostream>  
using namespace std;  
class NhanVien { 
    int msnv;    
    string ten;
    int tuoi;
    public:  
       void setData(int msnv, string ten, int tuoi) {  
            this->msnv = msnv;    
            this->ten = ten;    
            this->tuoi = tuoi; 
       }    
       void showData() {
            cout << "Ten nhan vien: " << this->ten << endl;
            cout << "Ma so nhan vien: " << this->msnv << endl;
            cout << "Tuoi: " << this->tuoi << endl;
       }    
};

Trong ví dụ này mình đã tạo ra ba thuộc tính để lưu trữ thông tin của một nhân viên đó là: manv, ten, tuoi. Ngoài ra mình có tạo thêm phương thức setData() dùng để gán dữ liệu cho sinh viên, và showData() dùng để hiển thị dữ liệu.

Trong phương thức setData() mình đã sử dụng từ khóa this->ten_thuoc_tinh để thực hiện phép gán dữ liệu cho các thuộc tính, còn ở phương thức showData() mình cũng sử dụng cú pháp tương tự để hiển thị dữ liệu của các thuộc tính. Như vậy công dụng của từ khóa this chính là một con trỏ và trỏ đến địa chỉ của đối tượng hiện tại.

Câu hỏi đặt ra là đối tượng hiện tại tại là gì? Để hiểu rõ hơn thì hãy xem đoạn code sử dụng class trên như sau:

Ví dụ

int main() { 
    // Nhan vien 1
    NhanVien n1 =  NhanVien();
    n1.setData(111231, "Nguyen Van A", 24);
    n1.showData();    
     
    // Nhan vien 2
    NhanVien n2 =  NhanVien();
    n2.setData(111232, "Nguyen Van B", 25);
    n2.showData();
    return 0;  
}

Trong ví dụ này mình đã tạo ra hai đối tượng sinh viên đó là n1 và n2, và con trỏ this của n1 sẽ trỏ đến chính đối tượng n1, con trỏ this của n2 sẽ trỏ đến chính đối tượng n2, đây ta gọi là đối tượng hiện tại.

Lưu ý: Trong các phương thức bình thường (không phải hàm khởi tạo) nếu bạn sử dụng tên của biến thì sẽ có hai trường hợp xảy ra.

  • Nếu biến đó không tôn tại trong phương thức mà nó lại trùng với tên thuộc tính thì mặc nhiên nó sẽ hiểu đó là thuộc tính.
  • Nếu biến đó có khai báo trong phương thức thì ta sẽ hiểu đó là biến bình thường, không phải là thuộc tính.

Một ví dụ khác về con trỏ this

Bạn hãy xem ví dụ dưới đây, đây là một ví dụ mình viết lại ở phần 1 và có một chút thay đổi.

Ví dụ

#include <iostream>  
using namespace std;  
class NhanVien { 
    int msnv;    
    string ten;
    int tuoi;
    public:  
       NhanVien(int msnv, string ten, int tuoi) {  
            cout << "Trong ham xay dung: " << endl;
            cout << "   msnv: " << msnv << endl;
            cout << "   ten: " << ten << endl;
            cout << "   Tuoi: " << tuoi << endl;
            msnv = msnv;    
            ten = ten;    
            tuoi = tuoi; 
       }    
       void HienThi() {
            cout << "Ham in thong tin cua doi tuong nhan vien: " << endl;
            cout << ten << endl;
            cout << "   Ma so nhan vien: " << msnv << endl;
            cout << "   Tuoi: " << tuoi << endl;
       }    
};  
   
int main() {  
    NhanVien n1 =  NhanVien(111231, "Nguyen Van A", 25);    
    n1.HienThi();    
    return 0;  
}
Ý Nghĩa Con Trỏ This Trong C++
Ý Nghĩa Con Trỏ This Trong C++

XII. Con Trỏ Void Trong C++

Để tìm hiều về con trỏ void trong C++, trước hết bạn cần phải nắm vững các kiến thức cơ bản về con trỏ void. Đừng lo lắng vì Techacademy đã chuẩn bị cho bạn trong các bài viết sau đây:

Con trỏ void trong C++ là gì

Trong số các kiểu con trỏ, bạn có thể xác định một con trỏ hơi khác thường được gọi là con trỏ void.

Giống như các loại con trỏ khác trong C++ thì con trỏ void cũng được sử dụng để lưu trữ địa chỉ của một dữ liệu trong bộ nhớ máy tính.

Tuy nhiên điều đặc biệt ở đây là, với kiểu dữ liệu mà con trỏ void lưu giữ địa chỉ thì chương trình có thể truy cập đến địa chỉ của dữ liệu đó, nhưng không xác định được kiểu của nó. Nói cách khác thì con trỏ void được sử dụng để lưu giữ địa chỉ của các kiểu dữ liệu không tồn tại kiểu dữ liệu.

Khai báo con trỏ void trong C++

Cách khai báo con trỏ void trong C++ cũng tương tự như với các loại con trỏ khác, chúng ta viết kiểu void, rồi dấu hoa thị *, và cuối cùng là tên con trỏ void như sau:

void * pdata;

Cách viết này cũng tương tự như với các kiểu con trỏ int hay char chẳng hạn:

char * pCharData;  // Con trỏ kiểu char
int  * pIntData;   // Con trỏ kiểu int

Chúng ta cũng có thể thực hiện các thao tác như gán địa chỉ vào con trỏ, hoặc là in địa chỉ được gán vào con trỏ void tương tự như các loại con trỏ khác trong C++. Ví dụ:

#include <iostream>
using namespace std;

int main()
{
    char data  = 'A'; //Khai báo biến data
    void * pdata = &data; // Khai báo con trỏ void và gán địa chỉ của biến data vào con trỏ

    cout << "pointer: "<< pdata;//In địa chỉ mà con trỏ lưu giữ

    return 0;
}
// pointer: 0x7ffe529fddbf

Tuy nhiên, không giống như với các kiểu con trỏ khác thì chúng ta lại không thể thực hiện các thao tác với giá trị của biến mà con trỏ void trỏ đến, và vì thế cũng không thể biết được kiểu của dữ liệu đó là gì. Ví dụ, chúng ta không thể đọc được giá trị của biến thông qua con trỏ, vì lỗi sau đây sẽ xảy ra:

#include <iostream>
using namespace std;

int main()
{
    char data  = 'A'; //Khai báo biến data
    void * pdata = &data;// Khai báo con trỏ void và gán địa chỉ của biến data vào con trỏ

    cout << "data: "<< *pdata;//In giá trị tại địa chỉ mà con trỏ lưu giữ

    return 0;
}

Vậy chẳng phải con trỏ void trong C++ rất là vô dụng hay sao? Tất nhiên là không phải rồi, vì chúng ta sẽ cần tới con trỏ void trong các trường hợp như dưới đây:

Sử dụng con trỏ void trong C++

Con trỏ void trong C++ sẽ được sử dụng trong các trường hợp đặc biệt sau đây:

Con trỏ vạn năng giúp lưu giữ tất cả các loại giữ liệu trong C++

Một điều dễ hiểu là do con trỏ void không tồn tại kiểu của dữ liệu mà nó đang chỉ đến, nên nó có khả năng chấp nhận và lưu giữ địa chỉ của tất cả các loại giữ liệu khác nhau trong C++. Đây là điều mà các con trỏ khác trong C++ không làm được. Ví dụ như con trỏ kiểu int thì chỉ chấp nhận lưu địa chỉ của dữ liệu kiểu int, còn con trỏ kiểu char thì cũng chỉ có thể chấp nhận lưu giữ địa chỉ của kiểu giữ liệu char. Nhưng với con trỏ void, void chấp hết ^_ .

Ví dụ cụ thể, con trỏ void trong C++ dưới đây có thể lưu giữ địa chỉ của tất cả các loại giữ liệu mà không sợ lỗi xảy ra trong chương trình.

#include <iostream>
using namespace std;

int main(){
    //Khai báo biến
    char   data1;
    short  data2;
    long   data3;
    double data4;

    // Khai báo con trỏ void
    void * pdata;

    //Gán địa chỉ của các loại dữ liệu với nhiều kiểu vào con trỏ void

    pdata = &data1;   // pdata --> data1
    pdata = &data2;   // pdata --> data2
    pdata = &data3;   // pdata --> data3
    pdata = &data4;   // pdata --> data4

    return 0;
}

Con trỏ giúp cố ý ẩn kiểu dữ liệu

Vì con trỏ C++ không cho phép chúng ta đọc kiểu dữ liệu cũng như truy cập vào dữ liệu tại địa chỉ mà nó lưu giữ, nên con trỏ void có vai trò vô cùng quan trong khi chúng ta muốn ẩn kiểu dữ liệu nào đó trong chương trình.

Đây là một thuật toán vô cùng phức tạp đòi hỏi lượng kiến thức khá cao, chỉ dành cho các bạn thực sự pro và muốn tìm hiểu sâu về C++ mà thôi.

Con Trỏ Void Trong C++
Con Trỏ Void Trong C++

XIII. Bài Tập Về Con Trỏ Trong C++

Trong chủ đề này, chúng ta cùng làm một số bài tập về Con trỏ trong C++.

Bài tập 1

Sử dụng con trỏ trong C++, bạn hãy viết một chương trình C++ để nhận dữ liệu từ người dùng và tìm giá trị lớn nhất của một tập dữ liệu nội bộ.

Lời giải

Dưới đây là chương trình C++ để giải bài tập trên. Mình sử dụng một hàm mà nhận mảng các giá trị dữ liệu và kích cỡ của nó. Hàm này trả về con trỏ mà trỏ tới giá trị lớn nhất.

#include<iostream> 
#include<conio.h> 
using namespace std;
int *findMax(int arr[],int n); 
int main(){    
   int n,i,*p;     
  cout<<"Nhap so du lieu: ";     
  cin>>n;     
  int arr[n];         
  for(i=0;i<n;i++)     {       
    cout<<"Nhap gia tri thu "<<i+1<<" la :";       
   cin>>arr[i];             
}           
  p=findMax(arr,n);     
  cout<<"Gia tri lon nhat la: "<<*p;     
  getch();     
  return 0; 
}  
 
int *findMax(int data[],int n){     
  int *max=data;     
  int i;    
 for(i=1;i<n;i++){            
   if(*max<*(max+i)) *max=*(max+i);                              
  }     
  return max; 
}

Chạy chương trình C++ trên sẽ cho kết quả như hình sau:

Bài Tập Về Con Trỏ Trong C++
Bài Tập Về Con Trỏ Trong C++

Bài tập 2

Viết một chương trình C++ để nhận 5 giá trị nguyên từ bàn phím. 5 giá trị này sẽ được lưu trữ trong một mảng bởi sử dụng một con trỏ. Sau đó, in các phần tử của mảng trên màn hình.

Lời giải

Dưới đây là chương trình C++ để giải bài tập trên.

#include<iostream> 
#include<conio.h>
using namespace std;

   int main() 
    { 
     int arr[5],i; 
     int *p=arr; 
     cout<<"Nhap 5 so:"; 
     cin>>*p>>*(p+1)>>*(p+2)>>*(p+3)>>*(p+4); 
     cout<<"Cac so ban vua nhap la:\n"; 
     for(i=0;i<5;i++) 
        cout<<arr[i]<<endl; 
     
     
     return 0; 
  
    }

Chạy chương trình C++ trên sẽ cho kết quả như hình sau:

Bài Tập Về Con Trỏ Trong C++
Bài Tập Về Con Trỏ Trong C++

Sửa đổi lời giải trên để in các phần tử của mảng theo thứ tự đảo ngược bởi sử dụng một con trỏ.

#include<iostream> 
#include<conio.h>
using namespace std;

   int main() 
    { 
     int arr[5],i; 
     int *p=arr; 
     cout<<"Nhap 5 so:"; 
     cin>>*p>>*(p+1)>>*(p+2)>>*(p+3)>>*(p+4); 
     cout<<"Cac so ban vua nhap theo thu tu dao nguoc la:\n"; 
     for(i=4;i>=0;i--) 
        cout<<*(p+i)<<endl; 
     
     
     return 0; 
  
    }

Chạy chương trình C++ trên sẽ cho kết quả như hình sau:

Bài Tập Về Con Trỏ Trong C++
Bài Tập Về Con Trỏ Trong C++

XIV. Ép Kiểu Con Trỏ Trong C++

Ép kiểu trong C++ là việc gán giá trị của một biến có kiểu dữ liệu này tới biến khác có kiểu dữ liệu khác.

Cú pháp:

(type) value;

Ví dụ:

float c = 35.8f;
int b = (int)c + 1;

Trong ví dụ trên, đầu tiên giá trị dấu phảy động c được đổi thành giá trị nguyên 35. Sau đó nó được cộng với 1 và kết quả là giá trị 36 được lưu vào b.

Phân loại ép kiểu trong C++

Trong C++, có hai loại ép kiểu dữ liệu:

  1. Nới rộng (widening): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu có kích thước lớn hơn. Kiểu biến đổi này không làm mất thông tin.
  2. Thu hẹp (narrowwing): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước lớn hơn sang kiểu có kích thước nhỏ hơn. Kiểu biến đổi này có thể làm mất thông tin

1. Nới rộng (widening)

Ép Kiểu Con Trỏ Trong C++
Ép Kiểu Con Trỏ Trong C++

Nới rộng (widening): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước nhỏ hơn sang kiểu có kích thước lớn hơn. Kiểu biến đổi này không làm mất thông tin. Ví dụ chuyển từ int sang float. Chuyển kiểu loại này có thế được thực hiện ngầm định bởi trình biên dịch.

Ví dụ:

#include <iostream>
 
using namespace std;
  
int main() {
    int i = 100;
    long l = i;    // khong yeu cau chi dinh ep kieu
    float f = l;   // khong yeu cau chi dinh ep kieu
    cout << "Gia tri Int: " << i << endl;
    cout << "Gia tri Long: " << l << endl;
    cout << "Gia tri Float: " << f << endl;
    return 0;
}

Kết quả:

Giá trị Int: 100
Giá trị Long: 100
Giá trị Float:  100

2.Thu hẹp (narrowwing)

Ép Kiểu Con Trỏ Trong C++
Ép Kiểu Con Trỏ Trong C++

Thu hẹp (narrowwing): Là quá trình làm tròn số từ kiểu dữ liệu có kích thước lớn hơn sang kiểu có kích thước nhỏ hơn. Kiểu biến đổi này có thể làm mất thông tin như ví dụ ở trên. Chuyển kiểu loại này không thể thực hiện ngầm định bởi trình biên dịch, người dùng phải thực hiện chuyển kiểu tường minh.

Ví dụ:

#include <iostream>
 
using namespace std;
  
int main() {
    double d = 100.04;
    long l = (long) d; // yeu cau chi dinh kieu du lieu (long)
    int i = (int) l; // yeu cau chi dinh kieu du lieu (int)
    cout << "Gia tri Double: " << d << endl;
    cout << "Gia tri Long: " << l << endl;
    cout << "Gia tri Int: " << i << endl;
    return 0;
}

Kết quả:

Giá trị Double: 100.04
Giá trị Long: 100
Giá trị Int:  100

The post Con Trỏ Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/con-tro-trong-c/

Static Trong C++

Chúng ta có thể định nghĩa các thành viên lớp là static bởi dùng từ khóa static trong C++. Khi chúng ta khai báo 1 thành viên của 1 lớp là static, lúc đó dù cho có bao nhiêu đối tượng của lớp được tạo, thì sẽ chỉ mang 1 bản sao của thành viên static.

Một thành viên static chỉ buộc phải khởi tạo 1 lần và được chia sẻ cho tất cả đối tượng của lớp. Tất cả dữ liệu static được khởi tạo về 0 khi đối tượng đầu tiên được tạo.

Chúng ta không thể khởi tạo thành viên static trong định nghĩa lớp, nhưng nó có thể được định nghĩa bên ngoài lớp đó bởi việc khai báo lại biến static như trong ví dụ sau, sử dụng toán tử phân giải phạm vi :: để nhận diện lớp nào sở hữu nó. Hãy cùng Techacademy tìm hiểu chi tiết qua bài viết dưới đây nhé.

I. Định Nghĩa Static C++

Static trong c++ là dữ liệu của lớp không phải là dữ liệu của đối tượng. Static trong c++ tồn tại như 1 biến toàn cục. Hay nói cách khác dữ liệu static xuất hiện trước lúc bạn khởi tạo đối tượng của lớp, và nó chỉ tồn tại duy nhất.

Các thành viên static có thể là public, private hoặc protected.

Static ngoài khai báo biến còn có thể khai báo hàm.

Đối với class, static sử dụng để khai báo thành viên dữ liệu dùng chung cho mọi thể hiện của lớp:

  • Một bản duy nhất tồn tại trong suốt quá trình chạy của chương trình.
  • Dùng chung cho tất cả các thể hiện của lớp.
  • Bất kể lớp đó có bao nhiêu thể hiện.
Định Nghĩa Static C++
Định Nghĩa Static C++

II. Biến Static Trong Hướng Đối Tượng C++

Ví dụ mình sẽ khai báo một biến static, int count, biến này dùng để đếm số lượng các hình chữ nhật đã được tạo ra.

class Rectangle {
  private: int width;
  int length;
  static int count;
  public: void set(int w, int l);
  int area();
  Rectangle() {
    count++;
  }
  Rectangle(int x, int y) {
    count++;
    set(x, y);
  }
}

class Rectangle {
  private:
    int width;
  int length;
  static int count;
  public:
    void set(int w, int l);
  int area();
  Rectangle() {
    count++;
  }
  Rectangle(int x, int y) {
    count++;
    set(x, y);
  }
}

 

Như vậy khi một đối tượng được tạo chúng ta sẽ tăng count lên để thực hiện đúng ý đồ đếm hình chữ nhật.

Tuy nhiên chúng ta không thể gán trước giá trị ban đầu của static trong class mà chúng ta phải gán giá trị cho nó ở ngoài class.

ví dụ

int main() {
    int Rectangle::count = 0; // Sau khi khởi tạo giá trị đầu mình tạo minh họa 3 hình chữ nhật Rectangle r1(2,4); Rectangle r2(1,2); Rectangle r3;}

    int main() {
      int Rectangle::count = 0;
      // Sau khi khởi tạo giá trị đầu mình tạo minh họa 3 hình chữ nhật
      Rectangle r1(2, 4);
      Rectangle r2(1, 2);
      Rectangle r3;
    }

 

Biến Static Trong Hướng Đối Tượng C++
Biến Static Trong Hướng Đối Tượng C++

Ví dụ về static trong c++

Ví dụ bên trên chỉ rõ biến static count không phải là dữ liệu của đối tượng như width và length mà nó là dữ liệu của class và chỉ tồn tại duy nhất.

Để sử dụng, gán hoặc lấy giá trị của static bạn sử dụng phạm vi truy xuất để gọi nó

//Ví dụ khởi tạo ban đầu
int Rectangle::count = 0;
 
//Gọi biến static:
Rectangle::count

Lưu ý thêm ở phần khai báo mình để static count ở private, nên bên ngoài class không thể dùng phạm vi truy xuất đến truy cập (Như cách gọi Rectangle::count ) Nhưng việc khai báo và gán giá trị ban đầu như int Rectangle::count = 0; là được phép.

Để dùng Rectangle::count bạn có thể khai báo biến static này ở public hoặc viết thêm hàm static get giá trị count này.

III. Hàm Static Trong Hướng Đối Tượng C++

Hàm static có vai trò cũng như biến static. Nghĩa là khi bạn đã khai báo class nhưng chưa tạo ra đối tượng như Rectangle r1(2,4) thì chúng ta vẫn truy cập được biến count như ví dụ trên. Vậy ở phần hàm static cũng có vai trò như vậy, hàm này sẽ là tồn tại duy nhất của class.

Mình sẽ khai báo thêm static int getCount() ở thuộc tính public

a. Code cụ thể về static

class Rectangle
{
    private:
        int width;
        int length;
        static int count;
    public:
        void set(int w, int l);
 
        static int getCount()
        {
            return count;
        }
 
        int area();
        Rectangle()
        {
            count++;
        }
        Rectangle(int x, int y) 
        { 
            count++; 
            set(x,y);
        }
}

b. Gọi hàm static

Bạn có thể gọi hàm static bằng cách sau:

  • Gọi Rectangle::getCount() ở ngoài class hoặc bên trong class
  • Gọi getCount() Bên trong class.
Hàm Static Trong Hướng Đối Tượng C++
Hàm Static Trong Hướng Đối Tượng C++

IV. Thành Viên Static Của Class

Hàm thành viên static trong C++

Bằng việc khai báo 1 hàm thành viên là static trong C++, bạn làm nó độc lập với bất kỳ đối tượng cụ thể nào của lớp. Một hàm thành viên static có thể được gọi ngay cả lúc không có đối tượng nào của lớp tồn tại và những hàm static được truy cập chỉ bởi dùng tên lớp và toán tử phân giải phạm vi :: trong C++.

Một hàm thành viên chỉ có thể truy cập thành viên dữ liệu static, những hàm thành viên static khác và bất kỳ hàm khác từ bên ngoài lớp đó.

Các hàm thành viên static có một phạm vi lớp và chúng không có sự truy cập đến con trỏ this của lớp trong C++. Bạn có thể sử dụng 1 hàm thành viên để xác định có hay không một số đối tượng của lớp đã được tạo.

Bạn thử ví dụ sau để hiểu khái niệm của hàm thành viên static trong C++:

#include <iostream>
 
using namespace std;

class Box
{
   public:
      static int biendemDT;
      // phan dinh nghia Constructor
      Box(double dai=1.0, double rong=1.0, double cao=1.0)
      {
         cout <<"Constructor duoc goi." << endl;
         chieudai = dai;
         chieurong = rong;
         chieucao = cao;
         // gia tri cua biendemDT tang len moi khi doi tuong duoc tao
         biendemDT++;
      }
      double theTich()
      {
         return chieudai * chieurong * chieucao;
      }
      static int layBienDem()
      {
         return biendemDT;
      }
   private:
      double chieudai;     // Chieu dai cua mot box
      double chieurong;    // Chieu rong cua mot box
      double chieucao;     // Chieu cao cua mot box
};

// khoi tao thanh vien static cua lop Box
int Box::biendemDT = 0;

int main(void)
{
  
   // in tong so doi tuong duoc tao truoc khi tao cac doi tuong.
   cout << "So doi tuong ban dau la: " << Box::layBienDem() << endl;

   Box Box1(2.4, 4.2, 2.2);    // khai bao box1
   Box Box2(4.5, 2.0, 3.2);    // khai bao box2

   // in tong so doi tuong duoc tao sau khi tao cac doi tuong.
   cout << "Tong so doi tuong sau khi tao la: " << Box::layBienDem() << endl;

   return 0;
}

Biên dịch và chạy chương trình C++ trên sẽ cho kết quả sau:

Hàm Static Trong Hướng Đối Tượng C++
Hàm Static Trong Hướng Đối Tượng C++

V. Khi Nào Nên Sử Dụng Biến Static

Biến static được dùng lúc chỉ cần một bản sao của biến. vì vậy trường hợp bạn khai báo biến bên trong phương thức thì không dùng biến như vậy, nó sẽ trở thành cục bộ chỉ hoạt động ..

Các biến được khai báo static thường được chia sẻ trên tất cả các trường hợp của một lớp.

Các biến được khai báo static thường được chia sẻ trên tất cả các trường hợp của một lớp. Khi bạn tạo đa dạng phiên bản của lớp VariableTest Biến này vĩnh viễn được chia sẻ trên tất cả chúng. Do đó, tại bất kỳ thời điểm nhất định nào, sẽ chỉ có 1 giá trị chuỗi được chứa trong biến vĩnh viễn.

Vì chỉ có 1 bản sao của biến có sẵn cho tất cả các trường hợp, mã this.permament sẽ dẫn đến lỗi biên dịch vì có thể nhớ lại rằng this.variablename tham chiếu đến tên biến cá thể. Do đó, những biến tĩnh phải được truy cập trực tiếp, như được chỉ ra trong mã.

Khi Nào Nên Sử Dụng Biến Static
Khi Nào Nên Sử Dụng Biến Static

VI. Const Static’ Có Nghĩa Là Gì Trong C Và C ++?

Const static’ có nghĩa là gì trong C và C ++

Tôi đã thấy điều này trong 1 số mã ở đây trên StackOverflow và tôi không thể tìm ra nó làm gì. Sau đó, tôi thấy 1 số câu trả lời nhầm lẫn trên các diễn đàn khác. Tôi đoán tốt nhất là nó được dùng trong C để ẩn hằng số fookhỏi những mô-đun khác. Điều này có đúng không? Nếu vậy, tại sao mọi người sẽ dùng nó trong bối cảnh C ++ nơi bạn có thể tạo ra nó private?

Câu trả lời:

+ Nó có dùng trong cả C và C ++.

Như bạn đã đoán, staticphần giới hạn phạm vi của nó cho đơn vị biên dịch đó. Nó cũng cung cấp cho khởi tạo tĩnh. constchỉ nói với trình biên dịch để không cho phép bất cứ ai sửa đổi nó. Biến này được đặt trong phân đoạn dữ liệu hoặc bss tùy thuộc vào kiến ​​trúc và có thể nằm trong bộ nhớ được đánh dấu chỉ đọc.

Tất cả đó là cách C xử lý các biến này (hoặc phương pháp C ++ xử lý các biến không gian tên). Trong C ++, một thành viên được đánh dấu staticđược chia sẻ bởi tất cả các phiên bản của một lớp nhất định. Cho dù đó là riêng tư hay không không ảnh hưởng đến thực tế là một biến được chia sẻ bởi nhiều trường hợp. Có consttrên đó sẽ cảnh báo bạn nếu có bất kỳ mã nào sẽ tìm mọi cách sửa đổi điều đó.

Nếu nó hoàn toàn riêng tư, thì mỗi phiên bản của lớp sẽ có phiên bản riêng (mặc dù trình tối ưu hóa).

+ Rất nhiều người đã đưa ra câu trả lời cơ bản nhưng không ai chỉ ra rằng trong C ++ constmặc định staticở namespace cấp độ (và một số đã cung cấp thông tin sai). Xem phần tiêu chuẩn C ++ 98 3.5.3.

Đầu tiên một số nền tảng:

Đơn vị dịch: Một tệp nguồn sau bộ xử lý trước (đệ quy) bao gồm tất cả các tệp bao gồm.

Liên kết tĩnh: Một biểu tượng chỉ có sẵn trong đơn vị dịch thuật của nó.

Liên kết ngoài: Một biểu tượng có sẵn từ các đơn vị dịch thuật khác.

– Ở cấp độ chức năng

staticcó nghĩa là giá trị được duy trì giữa các lệnh gọi hàm.
Các ngữ nghĩa của các staticbiến chức năng tương tự như các biến toàn cục ở chỗ chúng nằm trong phân đoạn dữ liệu của chương trình (chứ không phải ngăn xếp hoặc heap), xem câu hỏi này để biết thêm chi tiết về statictuổi thọ của biến.

– Ở classcấp độ

staticcó nghĩa là giá trị được chia sẻ giữa tất cả các thể hiện của lớp và constcó nghĩa là nó không thay đổi.

Const Static' Có Nghĩa Là Gì Trong C Và C ++
Const Static’ Có Nghĩa Là Gì Trong C Và C ++

VII. Bài Tập C++ OOP Giá Trị Của Biến Static

Biến static (static variables)

Biến static được tạo ra bên trong một khối lệnh có khả năng lưu giữ giá trị của nó cho dù chương trình đã chạy ra bên ngoài khối lệnh chứa nó.

Biến static chỉ cần được khai báo một lần duy nhất, và tiếp tục được duy trì sự tồn tại xuyên suốt cho đến khi chương trình kết thúc.

Dưới đây là một ví dụ đơn giản cho thấy sự khác biệt giữa việc sử dụng biến cục bộ và biến static:

#include <iostream>
 
void incrementAndPrint()
{
    int value = 1; // automatic duration by default
    ++value;
    std::cout << value << '\n';
} // value is destroyed here
 
int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
    
    return 0;
}

Mỗi khi hàm incrementAndPrint được gọi, một biến có tên gọi “value” được tạo ra và được gán giá trị 1. Hàm incrementAndPrint sẽ tăng giá trị của biến “value” lên 1 đơn vị, giá trị in ra màn hình là 2. Khi hàm incrementAndPrint thực hiện xong tác vụ, biến “value” bị hủy. Cứ thực hiện liên tiếp như vậy, kết quả in ra màn hình là:

2
2
2

Bây giờ, chúng ta thêm từ khóa static trước nơi biến “value” được khai báo:

#include <iostream>
 
void incrementAndPrint()
{
    static int s_value = 1; // static duration variable
    ++s_value;
    std::cout << s_value << '\n';
} // s_value is not destroyed here, but became inaccessible
 
int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
    
    return 0;
}

Lần này, vì biến “s_value” được khai báo với từ khóa static, nó sẽ chỉ được khởi tạo một lần duy nhất, dù hàm incrementAndPrint được gọi nhiều lần. Mỗi lần hàm incrementAndPrint được gọi, giá trị của biến “s_value” được tăng thêm 1, và còn được lưu giữ lại đó cho đến khi chương trình kết thúc. Kết quả in ra như sau:

2
3
4

Hàm static (static functions)

Từ khóa static cũng có thể được sử dụng cho các hàm, thường là những hàm được đặt bên trong các struct hoặc class (gọi là phương thức). Trong bài học này, mình lấy một ví dụ về việc sử dụng hàm static bên trong một struct mẫu như sau:

#include <iostream>

struct Test
{
   static void foo()
   {
      std::cout << "Called static function: foo" << std::endl;
   }
};

int main()
{
   Test::foo();
   return 0;
}

Các bạn có thể thấy, hàm foo được gọi mà không cần tạo ra thực thể kiểu Test.

Điểm đáng lưu ý trong việc sử dụng hàm static trong struct/class là chúng không thể truy xuất đến non-static members trong struct/class đó:

struct Test
{
   int value;

   void nonstatic_foo()
   {
   }

   static void foo()
   {
      value = 1; // error
      nonstatic_foo(); // error
   }
};
Bài Tập C++ OOP Giá Trị Của Biến Static
Bài Tập C++ OOP Giá Trị Của Biến Static

The post Static Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/static-trong-c/

Memset Trong C++

Trong bài viết này chúng ta sẽ tìm hiểu về hàm memset() trong C++. Đây là một hàm được sử dụng sao chép ký tự trong một chuỗi.

I. Memset Trong C++ Là Gì

Hàm void *memset(void *str, int c, size_t n) sao chép ký tự c (một unsigned char) tới n ký tự đầu tiên của chuỗi được trỏ tới bởi tham số str.

Memset Trong C++ Là Gì
Memset Trong C++ Là Gì

II. Cú Pháp Memset Trong C++

Hàm memset() trong C++ được sử dụng để sao chép một ký tự đơn lẻ trong một khoảng nhất định vào một đối tượng.

Cú pháp:

void* memset( void* dest, int ch, size_t count );

Trong đó:

  • dest: Con trỏ tới đối tượng để sao chép ký tự.
  • ch: Ký tự cần sao chép.
  • count: Số lần sao chép.

Hàm trả về một chuỗi đã được sao chép và lưu vào con trỏ dest.

Cú Pháp Memset Trong C++
Cú Pháp Memset Trong C++

III. Ví Dụ Về Hàm Memset Trong C++

Hàm memset() trong C++ được sử dụng để sao chép một ký tự đơn lẻ trong một khoảng nhất định vào một đối tượng.

Trong phần này mình sẽ thực hiện một ví dụ để mình họa cho hàm memset() trong C++.

Cụ thể mình sẽ tạo một biến dest với độ dài 50 ký tự để lưu các ký tự đã được sao chép. Sau đó sử dụng hàm memset() để sao chép ký tự ‘a’ với số lần là 5.

#include <iostream>
#include <cstring>
using namespace std;
 
int main() {
  //khai báo biến dest có độ dài 50 ký tự, được dùng để lưu các ký tự lặp lại
  char dest[50];
  //biến ch là biến được sử dụng để lặp lại
  char ch = 'a';
  //gọi hàm memset để lặp lại ký tự a 5 lần rồi gán vào biến dest
  memset(dest, ch, 5);
 
  cout << "Sau khi gọi hàm memset để lặp lại" << endl;
  cout << "\tBiến dest = " << dest;
 
  cout<<"\n-------------------------------\n";
  cout<<"Chương trình này được đăng tại Techcademy.edu.vn";
}

Kết quả:

Ví Dụ Về Hàm Memset Trong C++
Ví Dụ Về Hàm Memset Trong C++

IV. Memcpy() Trong C++

Hàm memcpy() trong C

Hàm void *memcpy(void *str1, const void *str2, size_t n) sao chép n ký tự từ str2 tới str1.

Khai báo hàm memcpy() trong C

Dưới đây là phần khai báo cho memcpy() trong C:

void *memcpy(void *str1, const void *str2, size_t n)

Tham số

str1 — Đây là con trỏ tới mảng đích, nơi mà nội dung để được sao chép, ép kiểu thành một con trỏ của kiểu void*.

str2 — Đây là con trỏ tới nguồn dữ liệu để sao chép, ép kiểu thành một con trỏ của kiểu void*.

n — Đây là số byte để được sao chép.

Trả về giá trị

Hàm này trả về một con trỏ tới chuỗi đích, đó là str1.

Ví dụ

Chương trình C sau minh họa cách sử dụng của memcpy() trong C:

#include <stdio.h>
#include <string.h>


struct {
  char name[40];
  int age;
} person, person_copy;

int main ()
{
  char myname[] = "Nguyen Hoang Nam";

  /* su dung ham memcpy de sao chep chuoi: */
  memcpy ( person.name, myname, strlen(myname)+1 );
  person.age = 46;

  /* su dung ham memcpy de sao chep struct: */
  memcpy ( &person_copy, &person, sizeof(person) );

  printf ("person_copy = %s, %d \n", person_copy.name, person_copy.age );

  return 0;
}

Biên dịch và chạy chương trình C trên sẽ cho kết quả:

Memcpy() Trong C++
Memcpy() Trong C++

V. Memcmp() Trong C++

Hàm int memcmp(const void *str1, const void *str2, size_t n)) so sánh n byte đầu của hai chuỗi str1 và str2.

Khai báo hàm memcmp() trong C

Dưới đây là phần khai báo cho memcmp() trong C:

int memcmp(const void *str1, const void *str2, size_t n)

Tham số

str1 — Đây là con trỏ tới một khối bộ nhớ.

str2 — Đây là con trỏ tới một khối bộ nhớ.

n — Đây là số byte để được so sánh.

Trả về giá trị

Nếu trả về giá trị < 0 thì hàm này chỉ rằng str1 là ngắn hơn str2.

Nếu trả về giá trị > 0 thì hàm này chỉ rằng str2 là ngắn hơn str1.

Nếu trả về giá trị = 0 thì hàm này chỉ rằng str1 là bằng str2.

Ví dụ

Chương trình C sau minh họa cách sử dụng của memcmp() trong C:

#include <stdio.h>
#include <string.h>

int main ()
{
   char str1[15];
   char str2[15];
   int ret;

   memcpy(str1, "abcdef", 6);
   memcpy(str2, "ABCDEF", 6);

   ret = memcmp(str1, str2, 5);

   if(ret > 0)
   {
      printf("Chuoi str2 la ngan hon chuoi str1");
   }
   else if(ret < 0) 
   {
      printf("Chuoi str1 la ngan hon chuoi str2");
   }
   else 
   {
      printf("Chuoi str1 la bang chuoi str2");
   }
   
   return(0);
}

Biên dịch và chạy chương trình C trên sẽ cho kết quả:

Memcmp() Trong C++
Memcmp() Trong C++

The post Memset Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/memset-trong-c/

Hàm Gets() Trong C++

Trong bài viết này chúng ta sẽ tìm hiểu về hàm gets() trong C / C++. Đây là một hàm được sử dụng để đọc các ký tự từ stdin.

I. Hàm Gets() Trong C++ Là Gì

Hàm gets() là hàm có sẵn trong thư viện cstdio, vì vậy trước khi sử dụng nó các bạn nhớ khai báo thư viện đã nhé: #include<cstdio>.

Hàm char *gets(char *str) trong Thư viện C chuẩn đọc một dòng từ stdin và lưu trữ nó bên trong chuỗi được trỏ bởi str. Nó dừng khi bắt gặp end-of-file hoặc ký tự newline (dòng mới) được đọc.

Hàm gets() khác hàm scanf() ở chỗ là hàm này chấp nhận các chuỗi có khoảng trống.

Hàm Gets() Trong C++ Là Gì
Hàm Gets() Trong C++ Là Gì

II. Cú Pháp Hàm Gets() Trong C++

Hàm gets() được sử dụng để đọc các ký tự từ stdin và lưu trữ chúng cho đến khi tìm thấy ký tự dòng mới. Hàm get () không cung cấp hỗ trợ để ngăn chặn tràn bộ đệm nếu chuỗi đầu vào lớn.

Cú pháp:

char* gets(char* str);

Trong đó:

  • str: Con trỏ đến một mảng ký tự lưu trữ các ký tự từ stdin.

Hàm trả về:

  • Khi thành công, hàm gets() trả về str.
  • Khi thất bại, nó trả về null.

Ví dụ: khi chúng ta muốn yêu cầu người dùng nhập vào họ và tên thì có thể sử dụng hàm gets() kết hợp với cout.

char str[100];
cout << "Nhập vào họ tên của bạn: ";
gets(str);

Như vậy là khi chạy thì chương trình sẽ yêu cầu bạn nhập vào từ bàn phím.

Cú Pháp Hàm Gets() Trong C++
Cú Pháp Hàm Gets() Trong C++

III. Ví Dụ Hàm Gets() Trong C++

Chương trình C sau minh họa cách sử dụng của hàm gets() trong C. Bạn sẽ thấy rằng hàm gets() chấp nhận chuỗi có chứa khoảng trống, không giống như hàm scanf():

#include <stdio.h>

int main()
{
   char str[50];

   printf("Nhap mot chuoi: ");
   gets(str);

   printf("Ban vua nhap chuoi: %s", str);

   return(0);
}

Biên dịch và chạy chương trình C trên để xem kết quả.

Ví Dụ Hàm Gets() Trong C++
Ví Dụ Hàm Gets() Trong C++

IV. Hướng Dẫn Xử Lý Lỗi Bỏ Qua Khi Dùng Hàm Gets Và Cin.Getline Trong C & C++

Ví dụ với thư viện stdio.h

PHP Code:

#include <stdio.h>
 
struct sinhvien
{
    char hoten[50];
    char ngaysinh[20];
    float cannang;
};
 
void Xuat1SV(sinhvien x)
{
    printf("Ten Sinh Vien: %s\n",x.hoten);
    printf("Ngay sinh: %s\n",x.ngaysinh);
}
void Nhap1SV(sinhvien &x)
{   
    float cn;
    printf("Ten Sinh Vien: ");
    gets(x.hoten);
 
    printf("Ngay sinh: ");
    gets(x.ngaysinh);
     
    printf("Can nang: ");
    scanf("%f",&cn);
    x.cannang=cn;
      
}
 
void main()
{
    sinhvien dssv[3];
    int i=0;
    //-------Nhap SV
    for (i=0;i<3;i++)
    {      
        printf("Nhap sinh vien thu %d\n----------------------------------------\n",i+1);
        Nhap1SV(dssv[i]);
        printf("\n");
    }
}

Ví dụ với iostream.h

PHP Code:

#include <iostream.h>
 
struct sinhvien
{
    char hoten[50];
    char ngaysinh[20];
    float cannang;
};
 
void Xuat1SV(sinhvien x)
{
    cout<<"Ten Sinh Vien: "<<x.hoten<<endl;
    cout<<"Ngay sinh: "<<x.ngaysinh<<endl;
}
void Nhap1SV(sinhvien &x)
{
     
    float cn;
    cout<<"Ten Sinh Vien: ";
    cin.getline(x.hoten,50);
 
    cout<<"Ngay sinh: ";
    cin.getline(x.ngaysinh,20);
     
    cout<<"Can nang: ";
    cin>>cn;
    x.cannang=cn;
     
     
 
}
 
 
void main()
{
    sinhvien dssv[3];
    int i=0;
    //-------Nhap SV
    for (i=0;i<3;i++)
    {
         
        cout<<"Nhap sinh vien thu "<<i+1<<endl<<"---------------------------"<<endl;
        Nhap1SV(dssv[i]);
        cout<<endl;
    }
}

Các bạn chạy thử ví dụ trên nha

Chắc các bạn sẽ thấy rằng sau lúc nhập sinh viên đầu tiên nó bỏ qua bước nhập tên sinh viên 2 mà nhảy tới ngày sinh luôn

Hướng Dẫn Xử Lý Lỗi Bỏ Qua Khi Dùng Hàm Gets Và Cin.Getline Trong C & C++
Hướng Dẫn Xử Lý Lỗi Bỏ Qua Khi Dùng Hàm Gets Và Cin.Getline Trong C & C++

Điều này đc giải thích như sau:

Khi bạn nhập một ký tự từ bàn phím thì ký tự ấy sẽ được chuyển vào 1 trong 2 nơi : bộ đệm bàn phím hoặc tệp stdin .
Làm sao biết lúc ta nhập 1 ký tự bất kỳ thì nó sẽ gửi vào đâu. Câu trả lời là trường hợp ta nhập ký tự trong lúc máy dừng chờ trước những hàm scanf , gets , getchar thì máy sẽ gửi ký tự vào tệp stdin. Nếu ko phải 3 hàm ấy thì là vào bộ đệm bàn phím . Tệp stdin có tính chất như sau :

+ Nếu trên stdin có dư dữ liệu ( tức là bạn bấm nhiều phím bất kỳ ) thì các hàm trên sẽ nhận được 1 phần dữ liệu mà chúng yêu cầu . Các ký tự còn lại vẫn nằm trên stdin .

+ lúc stdin chưa đủ dữ liệu thì máy sẽ chờ cho đến khi người dùng nhập đủ dữ liệu vào .

khi bạn nhập như sau : 45 và bấm ENTER cho hàm cin thì ký tự 45 sẽ được gán cho thuộc tính cân nặng . Nhưng ký tự ENTER vẫn còn lưu lại trên stdin hoặc bộ đệm và hàm cin>> (scanf)sẽ nhận nó bởi vậy sẽ làm trôi hàm cin.getline (gets) đi . Dù bạn rất muốn nhập cái gì đó cho chuỗi hoten nhưng cũng bó tay .

Thế thì cách giải quyết chính là làm sạch tệp stdin và bộ đệm buffer đi

Đối với thư viện stdio.h bị lỗi với gets thì ta chèn

PHP Code:
fflush(stdin);
Sau hàm nhập số liền kề với gets sau nó

Đối với thư viện iostrem.h bị lỗi cin.getline thì ta chèn

PHP Code:
cin.ignore();
Sau hàm nhập số trước cin.getline() liền kề sau nó để xóa bộ đệm đi là ok;

Kết quả sau khi chèn cin.ignore();

PHP Code:

#include <iostream.h>
 
struct sinhvien
{
    char hoten[50];
    char ngaysinh[20];
    float cannang;
};
 
void Xuat1SV(sinhvien x)
{
    cout<<"Ten Sinh Vien: "<<x.hoten<<endl;
    cout<<"Ngay sinh: "<<x.ngaysinh<<endl;
}
void Nhap1SV(sinhvien &x)
{
     
    float cn;
    cout<<"Ten Sinh Vien: ";
    cin.getline(x.hoten,50);
 
    cout<<"Ngay sinh: ";
    cin.getline(x.ngaysinh,20);
     
    cout<<"Can nang: ";
    cin>>cn;
    x.cannang=cn;
     
    cin.ignore();
 
}
 
 
void main()
{
    sinhvien dssv[3];
    int i=0;
    //-------Nhap SV
    for (i=0;i<3;i++)
    {
         
        cout<<"Nhap sinh vien thu "<<i+1<<endl<<"---------------------------"<<endl;
        Nhap1SV(dssv[i]);
        cout<<endl;
    }
}

Kết quả sau khi chèn fflush(stdin);

PHP Code:

#include <stdio.h>
 
struct sinhvien
{
    char hoten[50];
    char ngaysinh[20];
    float cannang;
};
 
void Xuat1SV(sinhvien x)
{
    printf("Ten Sinh Vien: %s\n",x.hoten);
    printf("Ngay sinh: %s\n",x.ngaysinh);
}
void Nhap1SV(sinhvien &x)
{
     
    float cn;
    printf("Ten Sinh Vien: ");
    gets(x.hoten);
 
    printf("Ngay sinh: ");
    gets(x.ngaysinh);
     
    printf("Can nang: ");
    scanf("%f",&cn);
    x.cannang=cn;
    fflush(stdin);
     
 
}
 
 
void main()
{
    sinhvien dssv[3];
    int i=0;
    //-------Nhap SV
    for (i=0;i<3;i++)
    {
         
        printf("Nhap sinh vien thu %d\n----------------------------------------\n",i+1);
        Nhap1SV(dssv[i]);
        printf("\n");
    }
}

V. Bài Tập Về Hàm Gets() Trong C++

Ví dụ

Chương trình C sau minh họa cách sử dụng của hàm gets() trong C. Bạn sẽ thấy rằng hàm gets() chấp nhận chuỗi có chứa khoảng trống, không giống như hàm scanf():

#include <stdio.h>

int main()
{
   char str[50];

   printf("Nhap mot chuoi: ");
   gets(str);

   printf("Ban vua nhap chuoi: %s", str);

   return(0);
}
Bài Tập Về Hàm Gets() Trong C++
Bài Tập Về Hàm Gets() Trong C++

 

The post Hàm Gets() Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/ham-gets-trong-c/

Destructor Trong C++

Trong bài học hôm nay chúng ta tiếp tục tìm hiểu về hàm hủy (Destructor) trong C++. Mục đích của hàm hủy trong C++ là gì? Cách sử dụng hàm hủy như thế nào? Chúng ta sẽ cùng tìm hiểu trong nội dung sau đây.

I. Hàm Destructor Trong C+Là Gì

Hàm hủy (Destructor) trong C++ ngược lại với hàm xây dựng, trong khi hàm xây dựng dùng để khởi tạo giá trị cho đối tượng thì hàm hủy dùng để hủy đối tượng.

Chỉ có duy nhất một hàm hủy trong 1 lớp. Hàm hủy tự động được gọi. Nếu như chúng ta không định nghĩa hàm hủy thì mặc định trình biên dịch sẽ tự tạo ra 1 hàm hủy mặc nhiên

Cũng giống như hàm xây dựng, hàm hủy được định nghĩa có cùng tên với tên lớp, khôn có bất cứ kiểu gì trả về kể cẳ kiểu void, tuy nhiên phải có dấu ~ trước tên của hàm hủy.

Lưu ý: Hàm hủy (Destructor) không có bất cứ tham số nào

Cú pháp

Cú pháp của hàm hủy (Destructor) trong C++ như sau:

Cú pháp
~TenLop() { };

Ví dụ cụ thể là lớp nhân viên, thì chúng ta sẽ tạo hàm hủy cho lớp nhân viên như sau:

Ví dụ
class NhanVien {  
   public:  
        ~NhanVien(){};
};

Ví dụ

Chúng ta cùng xem xét một ví dụ đơn giản nhất về hàm hủy trong C++ như sau:

Ví dụ

#include <iostream>  
using namespace std;  
class NhanVien  {  
   public:  
        NhanVien() {    
            cout << "Ham xay dung duoc goi" << endl;    
        }    
        ~NhanVien() {    
            cout << "Ham huy duoc goi" << endl;    
        }  
};  
int main(void) {  
    NhanVien n1;   
    NhanVien n2; 
    return 0;  
}
Hàm Destructor Trong C+Là Gì
Hàm Destructor Trong C+Là Gì

II. Khi Nào Hàm Destructor Được Gọi

Hàm hủy (Destructor) trong C++ được gọi tự động lúc đối tượng đi ra khỏi phạm vi:

  • Kết thúc hàm
  • Kết thúc chương trình
  • Kết thúc 1 block
  • Toán tử delete được gọi

Có hai hạn chế lúc dùng hàm hủy đó là:

  • Chúng ta không thể lấy địa chỉ của nó
  • Lớp con không có thừa kế hàm hủy từ lớp cha của nó
Khi Nào Hàm Destructor Được Gọi
Khi Nào Hàm Destructor Được Gọi

III. So Sánh Hàm Destructor Với Hàm Constructor

+ Giống Nhau:

Hàm tạo và hàm hủy là những hàm thành viên có cùng tên với lớp của chúng. Loại cũ constructor giúp khởi tạo một đối tượng. Ngược lại, a người phá hủy khác với hàm tạo sẽ xóa hàm tạo đã tạo khi nó không được sử dụng.

Đôi khi nó được yêu cầu khởi tạo 1 số phần của một đối tượng trước lúc nó có thể được sử dụng. Ví dụ, chúng ta đang thao tác trên ngăn xếp, trước khi chúng ta thực hiện bất kỳ hành động nào, đỉnh của ngăn xếp phải luôn được đặt bằng 0. Tính năng khởi tạo tự động này được thực hiện thông qua ‘Constructor’.

Giống như, trường hợp 1 đối tượng cần thực thi 1 số mã trước lúc nó bị phá hủy. Ví dụ: nếu một đối tượng cần đóng một tệp mà nó đã mở, trước khi nó bị phá hủy. Nó có thể được thực hiện với sự trợ giúp của ‘Destructor’. Bây giờ, hãy tổng quan về một số điểm khác biệt cơ bản giữa hàm tạo và hàm hủy với sự trợ giúp của biểu đồ so sánh

+ Khác Nhau:

– Biểu đồ so sánh:

Cơ sở để so sánh Constructor Kẻ hủy diệt
Mục đích Nó cấp phát bộ nhớ cho 1 đối tượng. Nó phân bổ bộ nhớ của một đối tượng.
Tờ khai class_name (các đối số nếu có) {}; ~ class_name (không có đối số) {};
Tranh luận Hàm tạo chấp nhận đối số Trình hủy không chấp nhận bất kỳ đối số nào.
Kêu gọi Hàm tạo được gọi tự động, trong lúc đối tượng được tạo. Bộ hủy được gọi tự động, lúc khối được thoát hoặc chương trình kết thúc.
Đang làm việc Constructor cho phép một đối tượng khởi tạo một số giá trị của nó trước đó, nó được sử dụng. Destructor cho phép một đối tượng thực thi một số mã tại thời điểm nó bị phá hủy.
Thứ tự thực hiện Hàm tạo được gọi theo thứ tự liên tiếp. Hàm hủy được gọi theo thứ tự ngược lại của hàm tạo.
Bằng số Có thể có nhiều hàm tạo trong một lớp. Luôn có một hàm hủy duy nhất trong lớp.
Copy Constructor Copy constructor cho phép một constructor khai báo và khởi tạo một đối tượng từ một đối tượng khác. Không có khái niệm như vậy.
Quá tải Các trình xây dựng có thể bị quá tải. Bộ hủy không thể bị quá tải.

– Định nghĩa của Constructor

A constructor về căn bản là 1 hàm thành viên của lớp, nó khởi tạo đối tượng và cấp phát bộ nhớ cho nó. Các hàm tạo có thể được xác định dễ dàng vì chúng được khai báo và định nghĩa cùng tên với tên của lớp. Một phương thức khởi tạo không có bất kỳ kiểu trả về nào; vì vậy, chúng không trả lại bất cứ thứ gì, thậm chí không phải là ‘void’. Một Constructor luôn được định nghĩa trong phần public của 1 lớp.

Có thể có nhiều hàm tạo trong 1 lớp; chúng có thể được phân biệt dựa trên số lượng và loại đối số được truyền vào. Nếu có nhiều hàm tạo trong một lớp; phương thức khởi tạo ngầm định (hàm tạo không làm gì) phải được định nghĩa cùng với chúng; nó không làm gì ngoài, đáp ứng trình biên dịch.

Các hàm tạo cũng có thể được định nghĩa với những đối số mặc định. Trong khi đó, họ cũng khởi tạo đối tượng “động”. Các hàm tạo không thể được kế thừa, cũng không thể là ảo, nhưng chúng có thể bị quá tải. Họ không thể được giới thiệu đến địa chỉ của họ.

– Các loại cấu tạo

Về cơ bản có ba loại cấu trúc – Cấu trúc mặc định, Tham số và Sao chép.

  • Nhà xây dựng mặc định: Nó là một hàm tạo mà không có đối số nào được đưa ra cho hàm tạo. Hàm tạo mặc định không có tham số, nhưng các giá trị cho hàm tạo mặc định có thể được truyền theo mặc định (động).
  • Trình tạo tham số: Kiểu hàm tạo này nhận các đối số; chúng ta có thể chuyển các giá trị khác nhau cho các thành viên dữ liệu làm đối số.
  • Copy Constructor: Copy constructor khác với các loại constructor khác vì nó chấp nhận địa chỉ của đối tượng khác làm đối số.

– Thực hiện hàm tạo

lớp Const {int a, b; public: Const () // hàm tạo không có tham số {a = 0; b = 0; } Const (int c, int d) {// hàm tạo với tham số a = c; c = d; }}; int main () {Const C1; C2 (10,20); // câu lệnh này gọi hàm tạo}

Khi C1 được tạo, một hàm tạo không có tham số nào được thực thi, vì C1 không truyền bất kỳ tham số nào. Trong khi, khi C2 được tạo, một hàm tạo có tham số sẽ được thực thi, vì nó đang truyền hai số nguyên cho hàm tạo.

– Định nghĩa của Destructor

A Kẻ hủy diệt cũng là một hàm thành viên của một lớp, nó sẽ phân bổ bộ nhớ được cấp phát cho một đối tượng. Nó được định nghĩa cùng tên với tên của một lớp, đứng trước dấu ngã (~) Biểu tượng. Các hàm hủy luôn được gọi theo thứ tự ngược lại của các hàm tạo.

Luôn có một hàm hủy duy nhất trong một lớp, vì nó không chấp nhận bất kỳ đối số nào. Các đối tượng địa phương bị phá hủy ngay sau khi quyền kiểm soát của việc thực hiện thúc đẩy khối; mặt khác, các đối tượng toàn cục bị phá hủy khi toàn bộ chương trình kết thúc.

Một trình hủy được gọi ngầm bởi một trình biên dịch. Nếu các lớp được kế thừa và một lớp được dẫn xuất từ ​​lớp cha và cả lớp con và lớp cha đều có hàm hủy; sau đó, hàm hủy của lớp dẫn xuất được gọi đầu tiên, tiếp theo là hàm hủy của lớp cha.

– Triển khai Trình hủy

lớp Const {int a, b; public: Const (int c, int d) // hàm tạo có tham số. {a = c; c = d; cout “giá trị của a và b là” ab ” n”; } ~ Const () // hàm hủy đang được gọi. {cout “đối tượng C1 bị phá hủy” ” n”; }}; int main () {Const C1 (10,20); }

Khi đối tượng C1 được tạo, một hàm tạo có hai tham số kiểu số nguyên được gọi và thành viên “a, b” được khởi tạo và giá trị của “a, b” được in ra. Sau khi trình hủy đó được gọi và in ra thông báo “đối tượng C1 bị phá hủy”.

Need of Destructor

Việc tạo hàm tạo sẽ tiêu tốn một lượng không gian bộ nhớ, vì nó cuối cùng sẽ cấp phát bộ nhớ cho các đối tượng. Bộ nhớ được cấp phát này phải được phân bổ trước khi hủy các đối tượng để giải phóng tài nguyên cho các tác vụ khác. Bộ hủy cực kỳ hữu ích cho mục đích đã định, nó phá hủy hiệu quả các đối tượng và thực hiện các nhiệm vụ dọn dẹp để giải phóng bộ nhớ.

So Sánh Hàm Destructor Với Hàm Constructor
So Sánh Hàm Destructor Với Hàm Constructor

IV. Bài Tập Hàm Destructor Trong C++

Chúng ta cùng xem một class đơn giản có sử dụng hàm destructor:

/**
* Techacademy.edu.vn - Kênh thông tin IT hàng đầu Việt Nam
*
* @author cafedevn
* Contact: cafedevn@gmail.com
* Fanpage: https://www.facebook.com/cafedevn
* Instagram: https://instagram.com/cafedevn
* Twitter: https://twitter.com/CafedeVn
* Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
*/

#include <iostream>
#include <cassert>
 
class IntArray
{
private:
   int *m_array;
   int m_length;
 
public:
   IntArray(int length) // constructor
   {
      assert(length > 0);
 
      m_array = new int[length]{};
      m_length = length;
   }
 
   ~IntArray() // destructor
   {
      // Dynamically delete the array we allocated earlier
      delete[] m_array;
   }
 
   void setValue(int index, int value) { m_array[index] = value; }
   int getValue(int index) { return m_array[index]; }
 
   int getLength() { return m_length; }
};
 
int main()
{
   IntArray ar(10); // allocate 10 integers
   for (int count{ 0 }; count < ar.getLength(); ++count)
      ar.setValue(count, count+1);
 
   std::cout << "The value of element 5 is: " << ar.getValue(5) << '\n';
 
   return 0;
} // ar is destroyed here, so the ~IntArray() destructor function is called here

Mẹo nhỏ

Nếu bạn thử biên dịch đoạn code trên và bị lỗi sau:

If you compile the above example and get the following error:

error: 'class IntArray' has pointer data members [-Werror=effc++]|
error:   but does not override 'IntArray(const IntArray&)' [-Werror=effc++]|
error:   or 'operator=(const IntArray&)' [-Werror=effc++]|

Để khắc phúc lỗi này, bạn có thể loại bỏ cờ “-Weffc++” khỏi compile settings (những thiết lập về biên dịch) cho ví dụ này, hoặc là bạn có thể thêm hai dòng code sau vào trong class IntArray:

IntArray(const IntArray&) = delete;
IntArray& operator=(const IntArray&) = delete;

Chúng ta sẽ thảo luận về chức năng của hai câu lệnh này trong chương sau.

Đoạn chương trình trên sẽ in ra:

The value of element 5 is: 6

Trên dòng đầu tiên, chúng ta đã khởi tạo 1 đối tượng mới của class IntArray, được gọi là ar, và truyền vào độ dài mảng là 10. Việc khởi tạo này sẽ gọi tới hàm constructor nhằm cấp phát bộ nhớ động cho những biến thành viên của class IntArray.

Chúng ta bắt buộc sử dụng cấp phát động tại đây bởi vì chúng ta không thể biết được tại thời điểm biên dịch (compile time) thì độ dài của mảng là bao nhiêu (caller – đối tượng gọi hàm sẽ quyết định điều đó).

Khi hàm main() kết thúc, lúc này đối tượng ar đã nằm ngoài phạm vi đoạn code mà chương trình đang chạy trên đấy (tức là ar đã goes out of scope). Điều này sẽ làm cho hàm destructor ~IntArray() được gọi, để xóa đi mảng mà chúng ta đã cấp phát bên trong phần thân hàm của constructor!

Bài Tập Hàm Destructor Trong C++
Bài Tập Hàm Destructor Trong C++

The post Destructor Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/destructor-trong-c/

Vẽ Tam Giác Trong C++

Vẽ tam giác trong C++ là một trong những bài tập lập trình về C++ sử dụng vòng lặp khá hay giúp các bạn luyện tư duy code cũng như cách sử dụng vòng lặp. Dưới đây là một số lời giải các bài tập vẽ tam giác trong C++

I. Vẽ Tam Giác Cân Trong C++

Viết chương trình C++ sử dụng ký tự * để vẽ tam giác vuông cân trong C++.Chúng ta sử dụng hai vòng lặp lồng nhau để giải bài toán này.

Lời Giải:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int n; int q = 0;
    printf("Chuong trinh nay se in ra tam giac can\n");
    printf("Nhap chieu cao tam giac cua ban: \n");
    scanf("%d",&n);

    while (n > 0)
    {
        for (int i = 1; i<n; i++)
            printf("%c", ' ');
        for (int k = 0; k <= q; k ++)
            printf("%c", '*');
        n -- ;
        q += 2 ;
        printf("\n");
    }

    return 0;

}

II. Vẽ Hình Tam Giác Trong C++

Viết một chương trình in ra hình tam giác như sau:

Vẽ hình tam giác trong C++

Số dòng được nhập từ bàn phím

Lời Giải:

#include <iostream>
using namespace std;
 
int main()
{
    int soDong;
    cout << "Nhap so dong: ";
    cin >> soDong;
    for(int i = 1; i <= soDong; i++) {
        //in so ky tu sao
        for(int j = 1; j <= i; j++) {
            cout << "*";
        }
 
        //xuong dong ke tiep
        cout << endl;
    }
    return 0;
}

III. Vẽ Tam Giác Vuông Trong C

Bài tập 1: vẽ tam giác vuông cân trong C

Đề bài: Viết chương trình C sử dụng ký tự * để vẽ tam giác vuông cân trong C.

Vẽ một tam giác sao vuông cân trong C thỏa mãn điều kiện:

  • Cạnh góc vuông bên cạnh trái màn hình và cạnh góc vuông còn lại nằm ở phần dưới màn hình.
  • Đỉnh nằm phía trên màn hình.

Lời giải: bài tập vẽ tam giác vuông cân trong C

Chúng ta sử dụng hai vòng lặp lồng nhau. Vòng lặp bên ngoài điều khiển số hàng, vòng lặp bên trong chịu trách nhiệm in dấu sao và khoảng trống.

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao vuông cân trong C:

#include <stdio.h>
 
int main() {
   int n,i,j;
 
   n = 6;
 
   printf("Ve tam giac sao vuong can:\n");
   for(i = 1; i <= n; i++) {
      for(j = 1; j <= i; j++)
         printf("* ");
 
      printf("\n");
   }
   return 0;
}

Kết quả:

Vẽ Tam Giác Vuông Trong C
Vẽ Tam Giác Vuông Trong C

Bài tập 2: vẽ tam giác vuông cân trong C

Đề bài: Viết chương trình C sử dụng ký tự * để vẽ tam giác vuông cân trong C.

Vẽ một tam giác sao vuông cân trong C thỏa mãn điều kiện:

  • Cạnh huyền nằm về phía trái màn hình.

Lời giải: bài tập vẽ tam giác vuông cân trong C (2)

Chúng ta sử dụng hai vòng lặp lồng nhau. Vòng lặp bên ngoài điều khiển số hàng, vòng lặp bên trong chịu trách nhiệm in dấu sao và khoảng trống.

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao vuông cân trong C:

#include <stdio.h>
 
int main() {
   int n,i,j;
 
   n = 6;
 
   printf("Ve tam giac sao vuong can:\n");
   for(i = 0; i < n; i++) {
      for(j=0; j<i; j++)
         printf("  "); 
 
      for(j=i; j < n; j++)
         printf(" *");
 
      printf("\n");
       
   }
    
   return 0;
}

Kết quả:

Vẽ Tam Giác Vuông Trong C
Vẽ Tam Giác Vuông Trong C

Bài tập 3: vẽ tam giác vuông cân trong C

Đề bài: Viết chương trình C sử dụng ký tự * để vẽ tam giác vuông cân trong C.

Vẽ một tam giác sao vuông cân trong C thỏa mãn điều kiện:

  • Một cạnh góc vuông nằm cạnh cạnh trái màn hình.
  • Cạnh góc vuông còn lại nằm phía trên màn hình.

Lời giải: bài tập vẽ tam giác vuông cân trong C

Chúng ta sử dụng hai vòng lặp lồng nhau. Vòng lặp bên ngoài điều khiển số hàng, vòng lặp bên trong chịu trách nhiệm in dấu sao và khoảng trống.

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao vuông cân trong C:

#include <stdio.h>
 
int main() {
   int n, i, j;
 
   n = 6;
 
   printf("Ve tam giac sao vuong can:\n");
   for(i = n; i >= 1; i--) {
      for(j = 1; j <= i; j++)
         printf("* ");
 
      printf("\n");
   }
    
   return 0;
}

Kết quả:

Vẽ Tam Giác Vuông Trong C
Vẽ Tam Giác Vuông Trong C

IV. Vẽ Tam Giác Pascal Trong C

Bài tập vẽ tam giác Pascal là bài tập điển hình của sinh viên trong khi học về các ngôn ngữ lập trình. Bạn theo dõi hình minh họa tam giác Pascal sau:

Vẽ Tam Giác Pascal Trong C
Vẽ Tam Giác Pascal Trong C

Tam giác Pascal có qui tắc sau:

Tất cả các giá trị bên ngoài tam giác được xem như là 0.

Hàng đầu tiên sẽ là 0 1 0, trong đó chỉ có giá trị 1 có được một khoảng trống trong tam giác Pascal, còn 0 là không nhìn thấy.

Hàng thứ hai được tạo bằng cách cộng hai số liên tiếp nhau từ hàng thứ nhất: (0 + 1) và (1 + 0).

Các hàng còn lại cũng được tạo bằng cách cộng như trên. Ví dụ với hàng thứ ba là cộng các số liên tiếp nhau từ hàng thứ hai: (0 + 1), (1 + 1) và (1 + 0).

Từ các qui tắc trên, trước hết chúng ta viết một hàm để tính các giá trị của tam giác Pascal bởi sử dụng đệ qui (tất nhiên là bạn có thể sử dụng cách khác). Trong hàm main(), chúng ta sẽ sử dụng ba vòng lặp. Một vòng lặp bên ngoài để điều khiển số hàng. Hai vòng lặp bên trong: một vòng lặp để in khoảng trống và một vòng lặp để in giá trị.

V. Vẽ Tam Giác Đều Trong C

Bài tập 1: vẽ tam giác đều trong C

Đề bài: Viết chương trình C sử dụng ký tự * để vẽ tam giác đều trong C.

Vẽ một tam giác sao đều trong C thỏa mãn điều kiện:

  • Các cạnh bằng nhau.
  • Một đỉnh nằm phía trên màn hình và cạnh đối diện với đỉnh này nằm dưới.

Lời giải: bài tập vẽ tam giác đều trong C

Chúng ta sử dụng ba vòng lặp lồng nhau. Một vòng lặp bên ngoài để điều khiển số hàng. Hai vòng lặp bên trong: một vòng lặp để in các khoảng trống, một vòng lặp để in các dấu sao.

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao đều trong C:

#include <stdio.h>
 
int main() {
   int n,i,j;
 
   n = 6;   // khai bao so hang.
 
   printf("Ve tam giac sao deu:\n");
   for(i = 1; i <= n; i++) {
      for(j = 1; j <= n-i; j++)
         printf(" ");
 
      for(j = 1; j <= i; j++)
         printf("* ");
 
      printf("\n");
   }
   return 1;
}

Kết quả:

 Vẽ Tam Giác Đều Trong C
Vẽ Tam Giác Đều Trong C

Bài tập 1: vẽ tam giác đều trong C

Đề bài: Viết chương trình C sử dụng ký tự * để vẽ tam giác đều trong C.

Vẽ một tam giác sao đều trong C thỏa mãn điều kiện:

  • Các cạnh bằng nhau.
  • Một đỉnh nằm phía dưới màn hình và cạnh đối diện với đỉnh này nằm trên.

Lời giải: bài tập vẽ tam giác đều trong C

Chúng ta sử dụng ba vòng lặp lồng nhau. Một vòng lặp bên ngoài để điều khiển số hàng. Hai vòng lặp bên trong: một vòng lặp để in các khoảng trống, một vòng lặp để in các dấu sao.

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao đều trong C:

#include <stdio.h>
 
int main() {
   int n,i,j;
 
   n = 6;
 
   printf("Ve tam giac sao deu:\n");
   for(i = 1; i <= n; i++) {
      for(j = 1; j < i; j++)
         printf(" "); 
 
      for(j = i; j <= n; j++)
         printf("* ");
 
      printf("\n");
   }
 
   return 1;
}

Kết quả:

 Vẽ Tam Giác Đều Trong C
Vẽ Tam Giác Đều Trong C

VII. Vẽ Tam Giác Vuông Ngược Trong C

Bài : Vẽ tam giác vuông cân rỗng có chiều cao h. Ví dụ: h = 5
*
* *
* *
* *
* * * * *
Hướng làm: Cách làm tương tự bài 3 nhưng ta phải xác định đoạn in dấu * và vị trí in dấu cách. Nhìn ví dụ ta nhận thấy các đoạn in dấu * là i = h, j = 1 và j = i.

void TamGiacVuongCanRong(int h)
{
   for (int i = 1; i <= h; i++)
   {
      for (int j = 1; j <= i; j++)
      {
         if (j == 1 || j == i || i == h)
            printf("* ");
         else
            printf("  ");
      }
      printf("\n");
   }
}

VIII. Vẽ Tam Giác Cân Rỗng Trong C

Vẽ tam giác cân rỗng có chiều cao h. Ví dụ: h = 5
*
* *
* *
* *
* * * * * * * * *
Hướng làm: Tương tự cách làm của bài 5 nhưng ta chỉ cần xác định đoạn in dấu *. Các đoạn in dấu * là i = h, j = m và j = n.

void TamGiacCanRong(int h)
{
   int m = h, n = h;
   for (int i = 1; i <= h; i++)
   {
      for (int j = 1; j <= 2 * h - 1; j++)
      {
         if (j == m || j == n || i== h)
            printf("*");
         else
            printf(" ");
      }
      m--;
      n++;
      printf("\n");
   }
}

IX. Vẽ Tam Giác Vuông Cân Trong C

Bài tập: Vẽ tam giác sao vuông cân

Vẽ tam giác sao vuông cân trong C thỏa mãn điều kiện:

Cạnh huyền nằm về phía trái màn hình.

Với bài tập C này, chúng ta chỉ cần hai vòng lặp: vòng lặp bên ngoài điều khiển số hàng, vòng lặp bên trong chịu trách nhiệm in dấu sao và khoảng trống.

Lời Giải:

Dưới đây là chương trình C để giải bài tập vẽ tam giác sao vuông cân trong C:

#include <stdio.h>

int main() {
   int n,i,j;

   n = 5;

   printf("Ve tam giac sao vuong can:\n\n");
   for(i = 0; i < n; i++) {
      for(j=0; j<i; j++)
         printf("  "); 

      for(j=i; j < n; j++)
         printf(" *");

      printf("\n");
      
   }
   
   return 0;
}

Biên dịch chương trình C trên sẽ cho kết quả:

Vẽ Tam Giác Vuông Cân Trong C
Vẽ Tam Giác Vuông Cân Trong C

The post Vẽ Tam Giác Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/ve-tam-giac-trong-c/

Strcmp Trong C++

String là 1 một mảng các ký tự được viết liền nhau, trong lập trình thường sử dụng rất nhiều để lưu chuỗi kí tự. Để thao tác với chuỗi có rất nhiều hàm và thư viện string.h hỗ trợ nhiều hàm hữu ích để xử lý chuỗi và hàm hỗ trợ xử lý mảng nhị phân. Bài viết này đề cập tới strcmp trong C++ và sử dụng hàm strcmp trong thư viện string.h hỗ trợ xử lý chuỗi.

I. Strcmp Trong C++ Là Gì

Hàm strcmp() trong C

Hàm int strcmp(const char *str1, const char *str2) so sánh chuỗi được trỏ đến bởi sr1 với chuỗi được trỏ đến bởi srt2.

Khai báo hàm strcmp() trong C

Dưới đây là phần khai báo cho strcmp() trong C:

int strcmp(const char *str1, const char *str2)

Tham số

str1 — Đây là chuỗi thứ nhất để được so sánh.

str2 — Đây là chuỗi thứ hai để được so sánh.

Trả về giá trị

Hàm này trả về các giá trị như sau:

Nếu giá trị trả về < 0 thì hàm này chỉ rằng str1 là ngắn hơn str2.

Nếu giá trị trả về > 0 thì hàm này chỉ rằng str2 là ngắn hơn str1.

Nếu giá trị trả về = 0 thì hàm này chỉ rằng str1 là bằng str2.

Strcmp Trong C++ Là Gì
Strcmp Trong C++ Là Gì

II. Cú Pháp Hàm Strcmp Trong C+

Hàm strcmp() được dùng để so sánh hai chuỗi, việc so sánh được thực hiện về mặt từ vựng.

Cú pháp:

int strcmp( const char* lhs, const char* rhs );

Trong đó:

  • lhs và rhs là hai chuỗi cần so sánh.

Hàm sẽ trả về 1 trong những trường hợp sau:

  • Giá trị dương giả dụ ký tự khác biệt đầu tiên trong lhs lớn hơn ký tự tương ứng trong rhs.
  • Giá trị âm trường hợp ký tự khác biệt trước tiên trong lhs nhỏ hơn ký tự tương ứng trong rhs.
  • Giá trị 0 giả dụ hai chuỗi bằng nhau về mặt từ vựng.
Cú Pháp Hàm Strcmp Trong C+
Cú Pháp Hàm Strcmp Trong C+

III. Cách Dùng Strcmp Trong C++

Trong phần này mình sẽ thực hiện một ví dụ để mình họa cho hàm memchr() trong C++.

Cụ thể mình sẽ khai báo hai chuỗi với hai nội dung khác nhau. Sau đó gọi hàm strcmp() để so sánh và thông báo ra màn hình.

#include <iostream>

#include <cstring>

using namespace std;

int main() {
  //khai báo hai biến lhs và rhs với hai nội dung cần so sánh
  char lhs[] = "Armstrong";
  char rhs[] = "Army";
  int result;
  //sử dụng hàm strcmp để so sánh hai chuỗi rồi gán kết quả cho biến result
  //*lưu ý: hàm strcmp sẽ trả về một số
  result = strcmp(lhs, rhs);
  //nếu hàm trả về số khác không tức là hai chuỗi khác nhau
  if (result != 0)
    cout << lhs << " và " << rhs << " khác nhau";
  //nếu hàm trả về = 0 thì hai chuỗi giống nhau
  else
    cout << lhs << " và " << rhs << " giống nhau";
  cout << endl << endl;
  //tương tự như vậy thử so sánh hai chuỗi giống nhau nhé
  result = strcmp(lhs, lhs);
  if (result != 0)
    cout << lhs << " và " << lhs << " khác nhau";
  else
    cout << lhs << " và " << lhs << " giống nhau";

  cout << "\n-------------------------------\n";
  cout << "Chương trình này được đăng tại Freetuts.net";
}

 

Kết quả:

Cách Dùng Strcmp Trong C++
Cách Dùng Strcmp Trong C++

Như vậy là chúng ta đã tìm hiểu xong hàm strcmp() trong C / C++. Đây là 1 hàm được dùng khá đa dạng trong những bài tâp về string, vì thế hãy luyện tập thật nhiều để sử dụng nó thành thạo nhé. Chúc các bạn thành công !!!

IV. Bài Tập So Sánh 2 Chuỗi Trong C++

+ Hàm strcmp trong string.h

Khi so sánh 2 số trong C thì ta có 1 số phép toán làm vô cùng đơn giản <, >, >=, <=, ==, !=, ngoài ra để so sánh chuỗi thì chúng ta không thể sử dụng những phép toán đó mà bắt buộc phải dùng hàm strcmp nằm trong thư viện string.h.

Hàm strcmp so sánh chuỗi s1 và chuỗi s2 và cho ta kết quả:

  • 1 Nếu s1 lớn hơn s2
  • 0 Nếu s1 giống s2
  • -1 Nếu s1 nhỏ hơn s2

Lưu ý: Trong Linux, hàm này trả về giá trị âm, dương, 0 (là khoảng cách giữa 2 ký tự khác nhau tương ứng trong s1, s2).

Ví dụ chương trình sau:

#include <stdio.h>
#include <string.h>
 
int main()
{
    char s1[20];
    char s2[20];                
 
    do
    {
        printf("Enter s1: ");
        gets(s1);
        printf("Enter s2: ");
        gets(s2);
 
        int x = strcmp(s1, s2);
        printf("x = %d", x);
 
        if(x < 0) printf(" => %s < %s", s1, s2);
        if(x > 0) printf(" => %s > %s", s1, s2);
        if(x == 0)printf(" => %s = %s", s1, s2);
 
        printf("\n\n");
 
    } while ( strcmp(s1, s2) != 0);
 
    return 0;
}

Kết quả:

Enter s1: Hang
Enter s2: Ho
x = -1 => Hang < Ho

Enter s1: Hong
Enter s2: Hon
x = 1 => Hong > Hon

Enter s1: Hung
Enter s2: hung
x = -1 => Hung < hung

Enter s1: Quan
Enter s2: Quan
x = 0 => Quan = Quan

Nguyên tắc so sánh

Nguyên tắc so sánh 2 chuỗi đó là duyệt lần lượt 2 từng ký tự của 2 chuỗi. So sánh mã ACSII của 2 ký tự đó, mã ký tự nào lớn hơn tức là chuỗi lớn hơn và ngừng so sánh. Nếu một chuỗi nào hết ký tự để so sanh trước thì chuỗi đó bé hơn.

s1	s2	Kết quả	Nguyên nhân
Hang	Ho	Hang < Ho	do a < o (97 < 111)
Hong	Hon	Hong > Hon	do s2 hết ký tự để so sánh
Hung	hung	Hung < hung	do H < h (72 < 104)
Quan	Quan	Quan = Quan	2 chuỗi giống nhau hoàn toàn
Bài Tập So Sánh 2 Chuỗi Trong C++
Bài Tập So Sánh 2 Chuỗi Trong C++

The post Strcmp Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/strcmp-trong-c/

Srand Trong C++

Làm sao để sinh số ngẫu nhiên trong C/C++? Hãy cùng Techacademy đi tìm cách để khởi tạo các số ngẫu nhiên sử dụng C/C++ nhé. Mình sẽ hướng dẫn các bạn khởi tạo các số ngẫu nhiên. Không chỉ sinh số nguyên ngẫu nhiên, mình sẽ hướng dẫn tạo 1 số ngẫu nhiên trong C ++ và cả cách phân biệt hàm rand và hàm srand () dùng để sinh số ngẫu nhiên nữa nhé. Các bạn cùng tìm hiểu với mình nhé.

I. Srand C++ Là Gì ?

Hàm srand() trong C

Hàm void srand(unsigned int seed) cung cấp seed cho bộ sinh số ngẫu nhiên được sử dụng bởi hàm rand.

Khai báo hàm srand() trong C

Dưới đây là phần khai báo cho srand() trong C:

void srand(unsigned int seed)

Tham số

seed: là một giá trị nguyên, được sử dụng như là seed bởi giải thuật sinh số ngẫu nhiên.

Trả về giá trị

Hàm này không trả về bất cứ giá trị nào.

Ví dụ

Chương trình C sau minh họa cách sử dụng của srand() trong C:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
   int i, n;
   time_t t;
   
   n = 5;
   
   /* Khoi tao bo sinh so ngau nhien */
   srand((unsigned) time(&t));

   /* in 5 so ngau nhien trong day tu 0 toi 50 */
   for( i = 0 ; i < n ; i++ ) 
   {
      printf("%d\n", rand() % 50);
   }
   
   return(0);
}

Biên dịch và chạy chương trình C trên sẽ cho kết quả:

Srand C++ Là Gì ?
Srand C++ Là Gì ?

II. Tạo 1 Số Ngẫu Nhiên Trong C++

Để tạo ra các số ngẫu nhiên khác nhau tại tất cả thời điểm chạy code, chúng ta sẽ thêm hàm srand() và truyền vào 1 tham số seed kiểu int. Tham số này đổi thay thì hàm srand() sẽ sinh ra các số khác nhau.

Ví dụ:

srand(123456);

Trong trường hợp này, giá trị a vẫn sẽ không đổi ở các lần chạy do 123456 là một hằng số. Vậy, chúng ta sẽ cần truyền vào một giá trị động chứ không phải giá trị tĩnh 😀

Có một giải pháp tốt nhất là chúng ta sẽ truyền cho seed thời gian hiện tại. Bằng phương pháp sử dụng hàm time() trong thư viện time.h. Hàm time() trả về kiểu time_t nhưng chúng ta có thể convert về int.

srand((int) time(0));

Bằng cách thêm hàm này trước lúc gọi hàm rand(), chúng ta đã có thể sinh số ngẫu nhiên khác nhau.

Một thí dụ cụ thể:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main(){
    int r;
    srand((int)time(0));
    for(int i = 0; i < 5; ++i){
        r = rand();
        printf("Rand %d is %d\n",i, r);
    }    
}

Kết quả chạy thử:

// Lần 1
Rand 0 is 5113
Rand 1 is 26832
Rand 2 is 11368
Rand 3 is 11635
Rand 4 is 20552
 
// Lần 2
Rand 0 is 5168
Rand 1 is 12947
Rand 2 is 20147
Rand 3 is 27496
Rand 4 is 32060

Ok, đã giải quyết được bài toán sinh số ngẫu nhiên cơ bản. Nhưng nếu tôi muốn sinh số ngẫu nhiên trong đoạn [min, max] thì phải làm thế nào?

Tạo 1 Số Ngẫu Nhiên Trong C++
Tạo 1 Số Ngẫu Nhiên Trong C++

III. Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++

– Để tạo một số ngẫu nhiêu sau thay đổi khác nhau trong các lần chạy, bạn dùng hàm time (có trong #include<ctime>). Theo như thư viện MSDN (Microsoft Developers Network) đề cập rằng: time trả về số của giây được trôi qua được tính từ nữa đêm (00:00:00), ngày 1, tháng 1, năm 1970 theo UTC . (“The time function returns the number of seconds elapsed since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC), according to the system clock”).

– Các số ngẫu nhiên giả được tạo ra bắt đầu từ thời khắc bạn thiết lập sử dụng hàm srand. Dòng code dưới đây thiết lập điểm bắt đầu của thời gian hiện hành.

srand(time(0));

– Giá trị được trả về từ time là qua srand. Lưu ý rằng số ngẫu nhiên được tạo ra trước lời gọi rand.

Ví dụ với chương trình đầu tiên: chúng ta thêm srand(time(0)) trước lời gọi rand().

// Second example 
#include <iostream> 
#include <cstdlib> 
#include <ctime> 
using namespace std;

int main()
{
    srand(time(0));
    int r0 = rand();
    int r1 = rand();
    int r2 = rand();
    int r3 = rand();
    int r4 = rand();

    cout<< "r0 = "<< r0 << endl;
    cout<< "r1 = "<< r1 << endl;
    cout<< "r2 = "<< r2 << endl;
    cout<< "r3 = "<< r3 << endl;
    cout<< "r4 = "<< r4 << endl;

    system("pause");
    return 0;
}

– Output lần đầu:

Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++
Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++

– Output lần 2:

Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++
Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++

– Output lần 3:

Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++
Tạo Số Ngẫu Nhiên Thay Đổi Theo Thời Gian Trong C++

IV. Tạo Số Ngẫu Nhiên Với Một Khoảng Xác Định Trong C++

Hai cách dùng trên đều cho kết quả giá trị ngẫu nhiên là những giá trị trong khoảng từ 0 đến RAND_MAX. Để tạo ra một giá trị ngẫu nhiên trong khoảng xác định, sử dụng công thức rand() % (max – min + 1) + min để nhận kết quả trong khoảng từ min đến max.

Ví dụ: random giá trị trong khoảng [3, 50]

#include <cstdlib>
#include <ctime>

int main()
{
   srand(time(NULL)); 
   int res = rand() % (50 - 3 + 1) + 3;
   return 0;
}

 

Tạo Số Ngẫu Nhiên Với Một Khoảng Xác Định Trong C++
Tạo Số Ngẫu Nhiên Với Một Khoảng Xác Định Trong C++

V. Phân Biệt Hàm Rand Và Hàm Srand() Dùng Để Sinh Số Ngẫu Nhiên

Để sinh số nguyên trong lập trình C/C++. Chúng ta có thể sử dụng hàm rand(). Hàm này trả về một số nguyên có kiểu dữ liệu là int

Ví dụ:

#include
#include
 
int main(){
    int r;
    for(int i = 0; i < 5; ++i){
        r = rand();
        printf("Rand %d is %d\n",i, r);
    }    
}

Kết quả chạy thử:

0
1
2
3
4
5
6

 
Rand 0 is 41
Rand 1 is 18467
Rand 2 is 6334
Rand 3 is 26500
Rand 4 is 19169

Tuy nhiên, hàm rand() này sẽ không hề random ra những số mới lúc bạn chạy code ở các lần sau. Nghĩa là, kết quả của code trên ở tất cả lần chạy sẽ đều random ra 5 số giống nhau. Bạn có thể thử chạy đoạn code trên nhiều lần để kiếm chứng.
Vậy làm sao để random các số ngẫu nhiên tại mọi thời điểm? Hãy đọc phần tiếp theo nào.

Sinh số ngẫu nhiên trong C/C++ với srand()

Để tạo ra các số ngẫu nhiên khác nhau tại mọi thời điểm chạy code, chúng ta sẽ thêm hàm srand() và truyền vào 1 tham số seed kiểu int. Tham số này đổi thay thì hàm srand() sẽ sinh ra các số khác nhau.

Ví dụ:

0
1
2

 
srand(123456);

Trong trường hợp này, giá trị a vẫn sẽ không đổi ở các lần chạy do 123456 là một hằng số. Vậy, chúng ta sẽ cần truyền vào một giá trị động chứ không phải giá trị tĩnh

Có một giải pháp tốt nhất là chúng ta sẽ truyền cho seed thời gian hiện tại. Bằng cách sử dụng hàm time() trong thư viện time.h. Hàm time() trả về kiểu time_t nhưng chúng ta có thể convert về int.

0
1
2

 
srand((int) time(0));

Bằng cách thêm hàm này trước khi gọi hàm rand(), chúng ta đã có thể sinh số ngẫu nhiên khác nhau.

Một ví dụ cụ thể:

#include
#include
#include
 
int main(){
    int r;
    srand((int)time(0));
    for(int i = 0; i < 5; ++i){
        r = rand();
        printf("Rand %d is %d\n",i, r);
    }    
}

Kết quả chạy thử:

// Lần 1
Rand 0 is 5113
Rand 1 is 26832
Rand 2 is 11368
Rand 3 is 11635
Rand 4 is 20552
 
// Lần 2
Rand 0 is 5168
Rand 1 is 12947
Rand 2 is 20147
Rand 3 is 27496
Rand 4 is 32060
 

Phân Biệt Hàm Rand Và Hàm Srand() Dùng Để Sinh Số Ngẫu Nhiên
Phân Biệt Hàm Rand Và Hàm Srand() Dùng Để Sinh Số Ngẫu Nhiên

The post Srand Trong C++ first appeared on Techacademy.

source https://techacademy.edu.vn/srand-trong-c/