C語言可否自定義數值類型(或是任意個位元組的數值類型)?
不只是char short int long..
例如3個位元組的數值類型(取值範圍為0~0xFFFFFF,存儲長度3個位元組)或是兩個12bit的連續放置的數值類型(單個取值範圍為0~0xFFF,存儲總長度3個位元組)
可以用 Bit fields,但單獨使用時要做 packing:
#include &
#pragma pack(push)
#pragma pack(1)
typedef struct {
unsigned u : 24;
}Foo;
typedef struct {
unsigned x : 12;
unsigned y : 12;
}Bar;
#pragma pack(pop)
int main() {
printf("sizeof(Foo) = %u
", (unsigned)sizeof(Foo));
Foo a = { 123 }, b = { 321 };
Foo c = { a.u + b.u };
printf("c = %u
", c.u);
Foo d = { 0xFFFFFF};
Foo e = { 0x1000000 }; // warning: large integer implicitly truncated to unsigned type [-Woverflow]
printf("d = %u
", d.u);
printf("e = %u
", e.u);
printf("sizeof(Bar) = %u
", (unsigned)sizeof(Bar));
Bar z = { 10, 20 };
printf("z.x + z.y = %d
", z.x + z.y);
}
輸出:
sizeof(Foo) = 3
c = 444
d = 16777215
e = 0
sizeof(Bar) = 3
z.x + z.y = 30
.
不能。
C自身的類型系統沒有這樣的功能,而其提供的封裝性也不足以讓用戶自定義出這樣的類型,只能很彆扭地實現題主的需求。寫一大堆宏或者函數的彆扭實現方式肯定不是題主真的想要的效果,所以說不能。========================================================
題主想要的東西是類似Ada里的constrained subtype,例如:type UInt24 is range 0 .. 16#FFFFFF#
或顯式指定T"Size屬性:
type UInt24 is range 0 .. 16#FFFFFF#; for UInt24"Size use 24;
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
type UInt24 is range 0 .. 16#FFFFFF#;
type UInt24Array8 is array (1 .. 8) of UInt24;
pragma Pack(UInt24Array8);
I : constant UInt24 := 123;
begin
Put_Line(Item =&> "Size of UInt24: " Integer"Image(UInt24"Size)); -- 24
Put_Line(Item =&> "Size of I: " Integer"Image(I"Size)); -- 32
Put_Line(Item =&> "Size of UInt24Array8: " Integer"Image(UInt24Array8"Size)); -- 192 == 24*8
end Hello;
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
type UInt12 is range 0 .. 16#FFF#;
type UInt12Array2 is array (1 .. 2) of UInt12;
pragma Pack(UInt12Array2);
begin
Put_Line(Item =&> "Size of UInt24: " Integer"Image(UInt12"Size)); -- 12
Put_Line(Item =&> "Size of UInt12Array2: " Integer"Image(UInt12Array2"Size)); -- 24 == 12*2
end Hello;
========================================================
@Milo Yip 的答案用bit field,是C里最接近題主想要的功能的做法。基礎代碼可以參考他的回答。通常可以用一個「封裝」用的宏來讓代碼看起來更正常,例如:#include &
#pragma pack(push)
#pragma pack(1)
typedef struct {
unsigned int u : 24;
} uint24_t;
#pragma pack(pop)
#define val(x) (x).u
void foo() {
uint24_t i, j;
val(i) = 127;
val(j) = 42;
val(i) = val(i) + val(j);
printf("sizeof(uint24_t) = %lu
",
sizeof(uint24_t)); // 3
printf("i = %d
", val(i)); // 169
}
Clang 3.5.0在x86-64在-O2下會生成:
foo(): # @foo()
push rax
mov edi, .L.str
mov esi, 3
xor eax, eax
call printf
mov edi, .L.str1
mov esi, 169
xor eax, eax
pop rdx
jmp printf # TAILCALL
.L.str:
.asciz "sizeof(uint24_t) = %lu
"
.L.str1:
.asciz "i = %d
"
可見變數i最後的值被分配到了esi上(mov esi, 169),而esi還是4位元組的——當然,x86-64沒有3位元組的寄存器嘛。
========================================================
如果要像 C語言可否自定義數值類型(或是任意個位元組的數值類型)? - 匿名用戶的回答 那樣自定義存儲:
struct ThreeBytes
{
char first;
char second;
char third;
};
那在C里這個ThreeBytes類型就無法使用int原本可以用的內建運算符。它的look and feel就會跟普通的整型差許多。
如果是C++的話,好歹還可以通過類與運算符重載來把運算符加回來,但C沒這福利。已經有人回答了 Bit fields ,在這裡我補充一下,使用 Bit fields 要注意位元組序的問題。How Endianness Effects Bitfield Packing上面鏈接比較詳細的說明了位元組序對 Bit fields 的影響 。下面的鏈接則給出了一些檢測位元組序的技巧。Pre-defined Compiler Macros / Wiki / Endianness
struct { int nVal1 : 1; // 改變成員佔位 int : 1; // 間距無法直接使用
int : 0; // 間距可以為0位元組,但有名稱的欄位無法設置為0位元組
} myStruct;@Milo Yip 大神,我照抄您的代碼運行,但結果與期待的不同,請問這是機器的原因嗎?
問一句,那個【unsigned u : 24】中,那個【 : 】號的作用是什麼?附言:那個112和211是我為了排除巧合改了一下,第一次的123和321也是和您機器的運行效果不同的。當然可以,不過實現起來麻煩一些而已。
你完全可以用多個 char 拼成一個數據結構,比如三位元組數值。struct ThreeBytes
{
char first;
char second;
char third;
}
推薦閱讀:
※利用異或方式交換兩個變數值的原理是什麼?
※為什麼計算機里加上存儲功能可以代替改變電路?
※為什麼開源軟體絕大部分都是C語言寫的,而商業軟體大多數是C++開發的?
※成為一個優秀的程序員,一定要精通C/C++嗎?
※如今存在用機器語言編寫出的程序么?