Bài 32.224 phút đọc
const là hợp đồng
Sau bài này bạn sẽ làm được
- Đặt const đúng chỗ cho con trỏ và cho thứ nó trỏ tới
- Giải thích vì sao ép bỏ const rồi ghi là hành vi không xác định
- Dùng const cho tham số hàm để nói rõ hợp đồng
- Kết hợp const với volatile cho thanh ghi chỉ đọc
constnói hai điều với hai người khác nhau: với trình biên dịch là "hãy chặn tôi nếu tôi ghi vào đây", và với người đọc là "hàm này không sửa dữ liệu của bạn". Nó không nói với hệ điều hành rằng vùng nhớ này chỉ đọc.
#Bốn dạng khai báo
const int x = 5; /* hang so */
const char *p; /* con tro toi const char:
KHONG sua duoc *p, sua duoc p */
char *const q = bo_dem; /* con tro const toi char:
sua duoc *q, KHONG sua duoc q */
const char *const r = ten; /* ca hai deu khong sua duoc */| Khai báo | Sửa được *p | Sửa được p | Dùng khi |
|---|---|---|---|
| char *p; | Được | Được | Bộ đệm bạn sở hữu và sửa |
| const char *p; | Không | Được | Tham số chỉ đọc. Trường hợp thường gặp nhất |
| char *const p; | Được | Không | Con trỏ tới thanh ghi cố định, hoặc biến cục bộ không muốn gán lại |
| const char *const p; | Không | Không | Bảng tra chỉ đọc |
#const là hợp đồng, không phải khóa
co.c
#include <stdio.h>
int main(void) {
const int x = 5;
int *p = (int *)&x; /* ep bo const: hop le ve CU PHAP */
*p = 10; /* HANH VI KHONG XAC DINH */
printf("x = %d\n", x);
printf("*p = %d\n", *p);
printf("&x = %p, p = %p\n", (void *)&x, (void *)p);
return 0;
}terminal
gcc -std=c11 -O2 -o co co.c && ./co
x = 5 *p = 10 &x = 000000000061FE4C, p = 000000000061FE4C
gcc -std=c11 -O0 -o co co.c && ./co
x = 10 *p = 10 &x = 000000000061FE44, p = 000000000061FE44
#const trong tham số hàm
Không có const
size_t do_dai(char *s);
int so_sanh(char *a, char *b);
void in(SinhVien *sv);
/* Doc nguyen mau khong biet ham co sua du lieu cua ban khong.
Phai doc than ham, va phai doc lai moi lan no thay doi. */const cho mọi tham số chỉ đọc
size_t do_dai(const char *s);
int so_sanh(const char *a, const char *b);
void in(const SinhVien *sv);
/* Doc nguyen mau la biet ngay: ba ham nay khong sua gi ca. */Quy tắc đặt const cho mọi loại tham số
/* Doc, khong sua */
size_t sv_dem(const SinhVien *ds, size_t n);
/* Sua noi dung, khong doi con tro */
void sv_chuan_hoa(SinhVien *sv);
/* Ghi ket qua ra, tham so ra */
int sv_doc(const char *dong, SinhVien *ra);
/* Mang chi doc */
int tim(const int a[], size_t n, int x);
/* Mang con tro chi doc, ca hai tang */
void in_het(const char *const ten[], size_t n);
/* Con tro toi con tro, tang trong chi doc */
int gom(const SinhVien *const *ds, size_t n);#const không lan truyền qua một tầng
const không phải hằng số biên dịch
const int N = 10;
int a[N]; /* o pham vi tep: error: variably modified
'a' at file scope */
switch (x) { case N: } /* error: case label does not reduce to
an integer constant */
/* Bai 19.2 da noi ky: trong C, const nghia la "khong duoc GHI",
khong phai "gia tri biet luc bien dich".
Dung enum cho so nguyen can lam hang so bien dich: */
enum { N = 10 };
int a[N]; /* dung */
switch (x) { case N: } /* dung */#Kết hợp với volatile
const volatile uint32_t *ID_CHIP = (const volatile uint32_t *)0x1FFF7A10;
uint32_t id = *ID_CHIP; /* doc duoc */
*ID_CHIP = 0; /* error: assignment of read-only location */| Khai báo | Chương trình ghi được | Đọc lại mỗi lần | Ví dụ thật |
|---|---|---|---|
| uint32_t x | Được | Không | Biến thường |
| const uint32_t x | Không | Không | Hằng số |
| volatile uint32_t *p | Được | Có | Thanh ghi điều khiển, ví dụ GPIO ODR |
| const volatile uint32_t *p | Không | Có | Thanh ghi chỉ đọc: mã chip, cờ trạng thái, bộ đếm |
Ví dụ thật từ CMSIS
typedef struct {
volatile uint32_t CR1; /* doc va ghi */
volatile uint32_t CR2;
const volatile uint32_t SR; /* CHI DOC: co trang thai */
volatile uint32_t DR;
volatile uint32_t BRR;
} USART_TypeDef;
#define USART1 ((USART_TypeDef *)0x40011000UL)
while (!(USART1->SR & (1u << 7))) { } /* doc duoc */
USART1->DR = b; /* ghi duoc */
USART1->SR = 0; /* error: read-only.
Va do la dieu ban muon:
thanh ghi nay xoa co bang
cach doc DR, khong bang
cach ghi vao SR. */Tự làm thử
- Viết bốn dạng khai báo con trỏ
constvà thử gán để xem cái nào bị từ chối. - Chạy chương trình ép bỏ
constở-O0và-O2, so hai kết quả. - Đưa biến
constra phạm vi tệp rồi chạy lại, xem chương trình sập ở đâu. - Chạy
nmtrên tệp đối tượng và xác nhận biếnconstcó chữR. - Viết hàm nhận
const Vung *rồi sửa dữ liệu qua con trỏ bên trong, xác nhận trình biên dịch không nói gì. - Khai báo một thanh ghi
const volatilevà thử ghi vào nó.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
constáp dụng cho thứ bên trái nó, và nếu không có gì bên trái thì cho thứ bên phải.constlà hợp đồng với trình biên dịch, không phải bảo vệ bộ nhớ. Ép bỏ rồi ghi là hành vi không xác định.- Biến
constở phạm vi tệp nằm trong vùng chỉ đọc thật, nên ghi vào gây lỗi phân đoạn. Biếnconsttrong hàm thì không. consttrên struct không lan sang dữ liệu mà con trỏ bên trong nó trỏ tới.const volatilekhông mâu thuẫn: một cái nói về quyền của bạn, cái kia nói về hành vi của thế giới.