Intro C++ programming language
Authors: Trần Đức Mạnh, Trần Đức Khánh, Nguyễn Ngọc Đức, Lê Ngọc Thiện
Buổi 5: Con trỏ
Bản chất của Biến (Variables):
Khái niệm về biến, giá trị của biến, địa chỉ của biến
int digit = 42;
RAM
Hằng, biến cục bộ, biến toàn cục
3. Biến toàn cục (Global variables)
2. Biến cục bộ (Local variables)
Biến được định nghĩa bên trong khối lệnh được gọi là các biến cục bộ (local variables). Những biến này chỉ có thể được truy cập bên trong các khối lệnh mà nó được định nghĩa (bao gồm các khối lệnh lồng nhau), và bị hủy ngay sau khi các khối lệnh kết thúc.
Hằng (constant) là từ chỉ những thứ không thay đổi và lặp đi lặp lại.
const <kiểu dữ liệu> <tên biến> = <giá trị>;
hoặc
<kiểu dữ liệu> const <tên biến> = <giá trị>;
Các biến khai báo bên ngoài của khối lệnh được gọi là biến toàn cục. Biến toàn cục có thời gian tĩnh, nghĩa là chúng được tạo ra khi chương trình bắt đầu và bị hủy khi nó kết thúc. Các biến toàn cục có phạm vi tập tin (file scope), hay gọi là "phạm vi toàn cầu" (global scope).
Con trỏ:
2. Các khái niệm về con trỏ
Là biến trỏ tới 1 địa chỉ khác, tức giá trị nó lưu là 1 địa chỉ của 1 ô nhớ khác.
RAM
“Con trỏ thường được nêu ra trong những cuộc tranh cãi là phần khó hiểu nhất của C++. Nhưng nó lại là tính năng mà khiến C++ trở thành một ngôn ngữ mạnh mẽ.”
Con trỏ:
2. Các khái niệm về con trỏ
3. Làm việc với con trỏ
RAM
int *ptr_int; // khai báo con trỏ để trỏ tới biến kiểu int
char *ptr_char; // khai báo con trỏ để trỏ tới biến kiểu char
float *ptr_float;//khai báo con trỏ để trỏ tới biến kiểu float
Con trỏ:
3. Làm việc với con trỏ
Ví dụ:
int *ptr; // khai báo con trỏ
int val = 5; // khai báo biến digit mang giá trị là 42
ptr = &val; // gán giá trị của con trỏ = địa chỉ của biến digit
Hoặc:
char c = ‘a’; // khai báo biến c kiểu char mang giá trị là ‘a’
char *ptr = &c; // khai báo con trỏ kiểu char đồng thời khởi tạo giá trị của nó bằng địa chỉ của biến c
Lưu ý:
- Con trỏ được khai báo là kiểu dữ liệu gì thì chỉ có thể trỏ tới biến có cùng kiểu giá trị đó� (trừ con trỏ mang kiểu void mà chúng ta sẽ đề cập ngay sau đây)
Con trỏ:
3. Làm việc với con trỏ
Ví dụ:
#include <iostream>
using namespace std;
int main(){
int digit = 42;//khai báo biến kiểu int mang giá trị là 42
int *ptr = &digit; // khai báo con trỏ kiểu int đồng thời khởi tạo giá trị của nó bằng địa chỉ của biến digit
cout << "Gia tri khi tham chieu nguoc = "<< *ptr<<"\n";
cout<< "Gia tri bien ptr: " << ptr<< "\n";
cout<< "Gia tri dia chi bien digit: " << &digit<< "\n";
}
Con trỏ:
4. Con trỏ đặc biệt
Hãy ghi nhớ rằng chúng ta không nên để một con trỏ là rác ( tức là không được khởi tạo giá trị). Một con trỏ rác là con trỏ không trỏ tới cái gì cả, nếu bạn sử dụng nó thì nó sẽ trỏ tới 1 địa chỉ ` ngẫu nhiên ` nào đó và sẽ thật là nguy hiểm nếu địa chỉ đó đang được sử dụng với 1 mục đích khác
int *ptr = NULL;
Hoặc
int *ptr = nullptr;
int *null_ptr; // con tro rac
int digit = 42;
int *ptr = &digit;
Một con trỏ void được sử dụng để trỏ tới biến có bất kỳ kiểu dữ liệu nào. Nó có thể tái sử dụng với bất kỳ biến nào mà chúng ta muốn
void *ptr_void = nullptr;
void *ptr_void = nullptr;
int number = 54;
ptr_void = &number;
printf("Gia tri cua number = %d\n", *ptr_void); // bien dich loi
printf("Gia tri cua number = %d\n", *(int *)ptr_void);
Con trỏ:
5. Bản chất của con trỏ
void setValueToFive(int x)
{
std::cout << &x << "\n";
x = 5;
}
int x = 3;
setValueToFive( x );
std::cout << "The value of x is " << x \n";
// Outputs: The value of x is 3
void setPointerValueToFive(int *x)
{
*x = 5;
}
int x = 3;
setPointerValueToFive( &x );
std::cout << "The value of x is " << x ;
// Outputs: The value of x is 5
void setValueToFiveWithReference(int& x)
{
x = 5;
}
int x = 3;
setPointerValueToFive( &x );
std::cout << "The value of x is " << x ;
// Outputs: The value of x is 5
Con trỏ:
6. Các lỗi thường gặp
int number = 54;
int *ptr = number; // sai vi number la gia tri
int *ptr = &number; // dung vi number la dia chi
int number = 54;
int *ptr = &number; // *ptr mang y nghia la khai bao con tro
*ptr = 100; // *ptr mang y nghia la truy cap gia tri cua dia chi ma con tro dang tro toi
Truyền giá trị cho Hàm
Nhắc lại về hàm:
Gọi hàm bằng tham trị tức là truyền bản sao của biến vào hàm để xử lý. Bản sao của một biến mang giá trị bằng giá trị của biến đó.
Truyền giá trị cho Hàm
Nhắc lại về hàm:
#include <stdio.h>
int multiply(int x, int y){
int z;
z = x * y;
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(x,y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
Truyền giá trị cho Hàm
Gọi hàm bằng tham chiếu:
#include <stdio.h>
int multiply(int *x, int *y){
int z;
z = (*x) * (*y);
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(&x,&y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
Gọi hàm bằng tham chiếu. Chúng ta truyền địa chỉ hoặc tham chiếu của biến vào hàm, lúc này không có bản sao chép nào được tạo cả, đảm bảo việc không bị lãng phí bộ nhớ. Chúng ta có thể truy cập vào giá trị lưu trong các địa chỉ này bằng toán tử tham chiếu ngược `*`.
Con trỏ và mảng
Con trỏ và mảng 1 chiều
Truy cập phần tử của mảng khi sử dụng chỉ số:
int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{
printf("Chi so = %d, Dia chi= %d, Gia tri= %d\n", i, &prime[i], prime[i]);
}
Output:
Chi so= 0, Dia chi= 6422016, Gia tri= 2
Chi so= 1, Dia chi= 6422020, Gia tri= 3
Chi so= 2, Dia chi= 6422024, Gia tri= 5
Chi so= 3, Dia chi= 6422028, Gia tri= 7
Chi so= 4, Dia chi= 6422032, Gia tri= 11
Con trỏ và mảng 1 chiều
Điều gì xảy ra nếu chúng ta viết int myArrray[5]?
Đáp án:
Hãy cùng xem xét ví dụ sau:
int prime[5] = {2,3,5,7,11};
printf("Ket qua khi dung &prime = %d\n",&prime);
printf("Ket qua khi dung prime = %d\n",prime);
printf("Ket qua khi dung &prime[0] = %d\n",&prime[0]);
/* Output */
Ket qua khi dung &prime = 6422016
Ket qua khi dung prime = 6422016
Ket qua khi dung &prime[0] = 6422016
Như vậy, &prime, prime và &prime[0] tất cả cùng trỏ đến một địa chỉ !
Con trỏ và mảng 1 chiều
Hãy cùng cộng vào mỗi con trỏ &prime, prime, và &prime[0] thêm 1
printf("Ket qua khi dung &prime = %d\n",&prime + 1);
printf("Ket qua khi dung prime = %d\n",prime + 1);
printf("Ket qua khi dung &prime[0] = %d\n",&prime[0] + 1);
/* Output */
Ket qua khi dung &prime = 6422036
Ket qua khi dung prime = 6422020
Ket qua khi dung &prime[0] = 6422020
Nhận xét:
Giải thích:
Kết luận:
Truy cập phần tử của mảng
Truy cập phần tử của mảng
Dùng con trỏ:
int prime[5] = {2,3,5,7,11};
for( int i = 0; i < 5; i++)
{
printf("Chi so = %d, Dia chi= %d, Gia tri= %d\n", i, prime + i, *(prime + i));
}
Con trỏ và chuỗi (string)
Định nghĩa: Mỗi chuỗi (string) là 1 mảng 1-chiều gồm các ký tự và được kết thúc bởi ký tự null (\0). Khi chúng ta viết char name[] = “ITMO_BRAIN”; , mỗi ký tự chiếm 1 byte trong bộ nhớ và mặc định ký tự cuối cùng luôn luôn phải là \0.
Con trỏ và chuỗi (string)
Mảng các con trỏ
Định nghĩa:
Cú pháp khai báo:
dataType *variableName[size];
/* Examples */
int *example1[5]; // khai báo mảng example1 chứa 5 con trỏ kiểu int
char *example2[8];// khai báo mảng example2 chứa 8 con trỏ kiểu char
Con trỏ trỏ tới mảng
Định nghĩa:
Note: Kiến thức ở phần trước, &arrayName trỏ tới toàn bộ mảng.
Khai báo:
dataType (*variableName)[size];
/* Examples */
int (*ptr1)[5];
char (*ptr2)[15];
Lưu ý:
Con trỏ trỏ tới mảng
int goals[] = { 85,102,66,69,67};
int (*pointerToGoals)[5] = &goals;
printf("Địa chỉ lưu trong pointerToGoals %d\n", pointerToGoals);
printf("giá trị %d\n",*pointerToGoals);
/* Output */
Địa chỉ lưu trong pointerToGoals 6422016
Giá trị 6422016
Chúng ta thử in chúng ra bằng cách sử dụng pointerToGoals nhé.