0

Tại sao cần phải ép kiểu con trỏ

Trả lời

Ép kiểu con trỏ cho phép ta:

  • Đọc một vùng nhớ theo kiểu khác.
  • Truy cập phần cứng đúng với kiểu dữ liệu
  • ...

Một số ví dụ để bạn có thể hình dung được lỗi xảy ra khi sử dụng con trỏ sai kiểu dữ liệu

Ví dụ 1

#include <stdio.h>
#include <stdint.h>

int main()
{
  uint8_t *p;
  int arr[3] = {1, 2, 3};
  
  p = arr;
  
  //  in ra màn hình các phần tử của mảng
  for(int i = 0; i < 3; i++) {
    printf("arr[%d] = %d\n", i, *(p++));
  }
  
  return 0;
}

Ouput

arr[0] = 1
arr[1] = 0
arr[2] = 0

Chà! Kết quả ở đây là không mong muốn rồi, arr[1] = 2, arr[2] = 3 thì mới chính xác. Vậy lí do ở đây là gì ?

  • Nếu để ý kỹ bạn sẽ thấy, kiểu dữ liệu của con trỏ là uint8_t (một kiểu dữ liệu 8bit).
  • Lúc này nếu ta sử dụng các phép toàn cho con trỏ, p++ hay p--,... nó sẽ tăng địa chỉ tương ứng với kích thước của con trỏ. image.png Note: kiểu dữ liệu được sắp xếp theo thiểu little-endian.

uint8_t *p thì mỗi lần p++ sẽ tăng địa chỉ tương ứng 1 byte. Nếu bây giờ ta tiếp tục tăng con trỏ, tới lúc nào đó nó sẽ trỏ tới số 2.

printf("%d\n", *(p++));
printf("%d\n", *(p++));

Thêm 2 đoạn mã này, vào code trên thì ta sẽ được:

arr[0] = 1
arr[1] = 0
arr[2] = 0
0
2 // Đây chính là giá trị phần tử của 2.

Thay vì màu mè như trên bây giờ, ta chỉ việc ép kiểu con trỏ lại thành kiểu int thì mọi thứ sẽ hoạt động đúng.

#include <stdio.h>
#include <stdint.h>

int main()
{
  uint8_t *p;
  int arr[3] = {1, 2, 3};
  
  p = arr;
  
  //  in ra màn hình các phần tử của mảng
  for(int i = 0; i < 3; i++) {
    printf("arr[%d] = %d\n", i, *((int *)p));
    p += sizeof(int); // cần phải tăng đúng kích thước của kiểu dữ liệu.
  }
  
  return 0;
}

Ouput

arr[0] = 1
arr[1] = 2
arr[2] = 3

image.png

Đây chỉ là ví dụ đơn giản để mô phỏng lại lỗi xảy ra trong thực tế nếu bạn không ép kiểu chính xác của con trỏ khi xử lí.

Ví dụ 2

Đọc dữ liệu từ việc căng chỉnh con trỏ.

Có một biến data nắm giữ các số từ 1 -> 4. In các số này ra màn hình
#include <stdio.h>
#include <stdint.h>

int main()
{
  unsigned int data = 16909060;
  int *p;
  p = &data;
  
  for(int i = 0; i < 4; i++)
  {
    printf("%d\n", *(p++));
  }
}

Ouput

16909060
1
-1925391180
32765

Đây là kết quả mà ta không mong đợi.

Điều này xảy ra là do ta đã tăng địa chỉ của con trỏ theo kích thước int - 4 byte trong 1 lần. Trong khi dữ liệu lại được lưu trữ liên tiếp nhau. image.png

Bây giờ ta cần chính sửa con trỏ lại theo kiểu uint8_t.

#include <stdio.h>
#include <stdint.h>

int main()
{
  unsigned int data = 16909060; // 0x01020304
  uint8_t *p;
  p = &data;
  
  for(int i = 0; i < 4; i++)
  {
    printf("%d\n", *(p++));
  }
  
}

Output

4
3
2
1

Tại vì sao nó lại hiện 1 -> 4 mà không phải là 4 -> 1. Hãy phản hồi bên dưới bài viết.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí