Thiết kế một hàm đơn giản trả về mọi kiểu dữ liệu trong C
Trong bài hướng dẫn này, tôi sẽ nói về việc một hàm có thể trả về nhiều kiểu dữ liệu mong muốn
Xem ví dụ
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/*
Trả về mọi loại kiểu
*/
void *func(void) {
int a = 0x01020304; // Theo kiểu little-endian
float b = 3.14;
static uint8_t buffer[4];
memcpy(buffer, &a, sizeof(buffer));
return buffer;
}
int main()
{
int value_int = func();
// float value_float = *((float *)func());
// Hiển trị giá trị
printf("gia tri cua value_int: %d\n", value_int);
// printf("gia tri cua value_float: %0.2f\n", value_float);
// Debug - với ví dụ là value_int
// printf("%d\n", *(func())); Error
printf("%p\n", func());
printf("%d\n", *((uint8_t *)func()));
printf("%p\n", func() + 1);
printf("%d\n", *((uint8_t *)(func() + 1)));
printf("%d\n", *((int *)func()));
return 0;
}
Đầu ra của chương trình
gia tri cua value_int: -594980847
0x558adc895011
4
0x558adc895012
3
16909060
Trước tiên hãy suy nghĩ xem tại sao lại có những kết quả đầu ra như thế này
Mở đầu, sẽ nói về hàm void *func(void) là hàm trả về void *, ở đây là tôi trả về buffer. Có thể thấy được đầu ra của giá trị biến value_int = -594980847. Bởi hàm func()
trả về địa chỉ bắt đầu của buffer mà ta gán cho kiểu int nên nó sẽ có giá trị như vậy.
Điều đó có nghĩa là khi gọi func(), thực chất là con trỏ. Vậy để muốn lấy giá trị tại một địa chỉ thông qua con trỏ ta cần sử dụng toán tử * để giải tham chiếu.
printf("%d\n", *(func())) // Điều này đã thực sự chính xác khi làm như này chưa ?
Nếu bạn chạy dòng mã trên, compiler sẽ báo lỗi bởi vì nó sẽ không biết lấy bao nhiêu byte để tiến hành giải tham chiếu khi sử dụng *. (Vì hàm func() trả về kiểu void *).
Vậy ta cần tiến hành ép kiểu để cho compiler có thể hiểu là cần bao nhiêu byte để hiển thị dữ liệu.
printf("%p\n", func());
printf("%d\n", *((uint8_t *)func()));
printf("%p\n", func() + 1);
printf("%d\n", *((uint8_t *)(func() + 1)));
Ở đây, tôi ép kiểu void * thành uint8_t *, do đó compiler sẽ lấy 1 byte tại địa chỉ đó để hiển thị.
Câu hỏi đặt ra: "Tại sao nó lại hiển thị giá trị như kia ?". Gợi ý: hãy suy nghĩ về Little-endian và Big-endian.
Tương tự, nếu ép kiểu void * thành int *, compiler sẽ lấy 4 byte, kể từ địa chỉ bắt đầu để hiển thị
printf("%d\n", *((int *)func()));
// Output: 16909060
All rights reserved