1. Hằng số ký hiệu chuỗi kiểu C

Trong bài học  – Chuỗi kiểu C, chúng ta đã thảo luận về cách bạn có thể tạo và khởi tạo chuỗi kiểu C, như sau:

#include <iostream>
 
int main()
{
    char myName[]{ "Alex" }; // fixed array
    std::cout << myName << '\n';
 
    return 0;
}

C ++ cũng hỗ trợ một cách tạo hằng số biểu tượng chuỗi kiểu C bằng cách sử dụng con trỏ:

/*
Cafedev.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
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/

#include <iostream>
 
int main()
{
    const char *myName{ "Alex" }; // pointer to symbolic constant
    std::cout << myName << '\n';
 
    return 0;
}

Trong khi hai chương trình trên hoạt động và tạo ra kết quả giống nhau, C ++ xử lý việc cấp phát bộ nhớ cho những chương trình này hơi khác một chút.

Trong trường hợp mảng cố định, chương trình cấp phát bộ nhớ cho một mảng cố định có độ dài 5 và khởi tạo bộ nhớ đó bằng chuỗi “Alex \ 0”. Vì bộ nhớ đã được cấp phát cụ thể cho mảng, bạn có thể tự do thay đổi nội dung của mảng. Bản thân mảng được coi như một biến cục bộ bình thường, vì vậy khi mảng vượt ra khỏi phạm vi, bộ nhớ mà mảng sử dụng sẽ được giải phóng cho các mục đích sử dụng khác.

Trong trường hợp hằng số tượng trưng, ​​cách trình biên dịch xử lý điều này được xác định thực hiện. Điều thường xảy ra là trình biên dịch đặt chuỗi “Alex \ 0” vào bộ nhớ chỉ đọc ở đâu đó, và sau đó đặt con trỏ trỏ đến nó. Vì bộ nhớ này có thể ở chế độ chỉ đọc, cách tốt nhất là đảm bảo chuỗi là const.

Vì mục đích tối ưu hóa, nhiều ký tự chuỗi có thể được hợp nhất thành một giá trị duy nhất. Ví dụ:

const char *name1{ "Cafedev" };
const char *name2{ "Cafedev" };

Đây là hai chuỗi ký tự khác nhau có cùng giá trị. Trình biên dịch có thể chọn kết hợp chúng thành một chuỗi chia sẻ duy nhất theo nghĩa đen, với cả name1 và name2 đều được trỏ đến cùng một địa chỉ. Do đó, nếu name1 không phải là const, thì việc thay đổi name1 cũng có thể ảnh hưởng đến name2 (điều này có thể không được mong đợi).

Do các ký tự chuỗi được lưu trữ ở một vị trí cố định trong bộ nhớ, các ký tự chuỗi có thời lượng tĩnh hơn là thời lượng tự động (nghĩa là chúng chết ở cuối chương trình, không phải ở cuối khối mà chúng được xác định). Điều đó có nghĩa là khi chúng tôi sử dụng chuỗi ký tự, chúng ta không phải lo lắng về các vấn đề xác định phạm vi. Do đó, những điều sau là ổn:

const char* getName()
{
    return "Cafedev";
}

Trong đoạn code trên, getName () sẽ trả về một con trỏ đến chuỗi kiểu C “Alex”. Nếu hàm này trả về bất kỳ biến cục bộ nào khác theo địa chỉ, thì biến đó sẽ bị hủy ở cuối getName () và chúng tôi sẽ trả về một con trỏ lơ lửng trở lại trình gọi. Tuy nhiên, vì các ký tự chuỗi có thời lượng tĩnh, “Alex” sẽ không bị hủy khi getName () kết thúc, vì vậy người gọi vẫn có thể truy cập thành công.

Chuỗi kiểu C được sử dụng trong rất nhiều code cũ hoặc code cấp thấp, bởi vì chúng có bộ nhớ rất nhỏ. code hiện đại nên ưu tiên sử dụng std :: string và std :: string_view, vì chúng cung cấp truy cập an toàn và dễ dàng vào chuỗi.

2. std :: cout và con trỏ char

Tại thời điểm này, bạn có thể nhận thấy điều gì đó thú vị về cách std :: cout xử lý các con trỏ của các loại khác nhau.

Hãy xem xét ví dụ sau:

/*
Cafedev.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
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/

#include <iostream>
 
int main()
{
    int nArray[5]{ 9, 7, 5, 3, 1 };
    char cArray[]{ "Hello!" };
    const char *name{ "Alex" };
 
    std::cout << nArray << '\n'; // nArray will decay to type int*
    std::cout << cArray << '\n'; // cArray will decay to type char*
    std::cout << name << '\n'; // name is already type char*
 
    return 0;
}

Trên máy của mình, điều này được in:

003AF738

Xin chào!

Cafedev

3. Tại sao mảng int in một địa chỉ, nhưng các mảng ký tự lại in ra chuỗi?

Câu trả lời là std :: cout đưa ra một số giả định về ý định của bạn. Nếu bạn chuyển cho nó một con trỏ không phải char, nó sẽ chỉ in nội dung của con trỏ đó (địa chỉ mà con trỏ đang giữ). Tuy nhiên, nếu bạn chuyển nó một đối tượng kiểu char * hoặc const char *, nó sẽ cho rằng bạn đang có ý định in một chuỗi. Do đó, thay vì in giá trị của con trỏ, nó sẽ in chuỗi được trỏ tới!

Mặc dù điều này là tuyệt vời 99% thời gian, nhưng nó có thể dẫn đến kết quả không mong đợi. Hãy xem xét trường hợp sau:

#include <iostream>
 
int main()
{
    char c{ 'Q' };
    std::cout << &c;
 
    return 0;
}

Trong trường hợp này, người lập trình đang có ý định in địa chỉ của biến c. Tuy nhiên, & c có kiểu char *, vì vậy std :: cout cố gắng in nó dưới dạng một chuỗi! Trên máy của mình, điều này được in:

Q╠╠╠╠╜╡4; ¿■ A

Tại sao nó làm điều này? Vâng, nó giả định & c (có kiểu char *) là một chuỗi. Vì vậy, nó đã in chữ ‘Q’, và sau đó tiếp tục chạy. Tiếp theo trong trí nhớ là một đống rác. Cuối cùng, nó chạy vào một bộ nhớ nào đó giữ giá trị 0, mà nó hiểu là dấu chấm hết rỗng, vì vậy nó dừng lại. Những gì bạn thấy có thể khác nhau tùy thuộc vào những gì trong bộ nhớ sau biến c.

Trường hợp này hơi khó xảy ra trong thực tế (vì bạn không có khả năng thực sự muốn in địa chỉ bộ nhớ), nhưng nó là minh họa về cách mọi thứ hoạt động dưới mui xe và cách các chương trình có thể vô tình đi chệch hướng.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Nguồn và Tài liệu tiếng anh tham khảo:

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!