为什么char不是单个字符

Why is char not a single character?

本文关键字:单个 字符 char 为什么      更新时间:2023-10-16

在Microsoft的DEV_BROADCAST_DEVICEINTERFACE_A文档中,dbcc_name的类型定义如下:

char  dbcc_name[1];

但如这个StackOverflow问题所示,它原来是一个具有多个字符的string

char不是一个16位值的单个字符吗?这是怎么回事?

(我最初认为char是16位。可能是因为这是它在c#中的大小。实际上,这可能是因为我在看DEV_BROADCAST_DEVICEINTERFACE_W,它实际上是2字节。)

char不是16位值吗?

在什么平台上?在大多数平台上(包括所有运行Windows、AFAIK的平台),char是8位。

这是如何工作的?

类型文档对此进行了解释:

dbcc_size

此结构的大小,以字节为单位。这是成员的大小加上dbcc_name字符串的实际长度(通过将dbcc_name声明为一个字符数组来解释空字符)

换句话说,_DEV_BROADCAST_DEVICEINTERFACE_A的定义利用了数组在C++中衰减为指针的事实,因此在大多数上下文中,具有数组类型的dbcc_name可以用作以零结尾的字符串。实际字符串与_DEV_BROADCAST_DEVICEINTERFACE_A对象连续存储在从dbcc_name偏移量开始的地址处。

值得注意的是,数组(1)的大小与其内容的长度无关;它只是C++中最小的合法静态数组大小(遗留代码偶尔会使用char[0]类型的结构成员。然而,这是编译器扩展,不是合法的C++)。

这就是所谓的"结构破解"。这是一个技巧,允许您将大小可变的数据存储在struct实例中。

您将最后一个成员设置为大小为1的数组,如下所示:

struct foo { int i; char c[1] };

假设一个4字节的int,这种类型是5字节宽的(尽管它可能需要8个字节才能满足任何对齐要求),struct foo的实例看起来是这样的:

+---+
i: |   |
+---+
|   | 
+---+
|   |
+---+
|   | 
+---+
c: |   |
+---+

但是,如果使用malloccalloc为其动态分配内存,则可以分配比struct类型所需更多的内存,并且额外的内存将被视为数组的一部分(因为struct元素保证按声明的顺序排列,数组类型不强制大小)。

struct foo *p = malloc( sizeof *p + strlen( "hello" )); 
p->i = 1;
strcpy( p->c, "hello" );

因此,我们为struct类型分配了足够的内存(5字节)加上足够的内存来存储"hello",这给了我们(假设小端)

+---+ ----+
i: | 1 |     |
+---+     |
| 0 |     |
+---+     |
| 0 |     +---- size of struct foo
+---+     |
| 0 |     |
+---+     |
c: |'h'|     |
+---+ ----+
|'e'|     |
+---+     |
|'l'|     |
+---+     |
|'l'|     +---- Extra memory for "hello"
+---+     |
|'o'|     |
+---+     |
| 0 |     |
+---+ ----+

为什么我们将c设为大小为1的数组而不是指针?如果我们把c作为一个指针,就像一样

struct foo { int i; char *c };

那么这个技巧不起作用,因为所有c可以存储的都是地址,而不是数据。

C允许"灵活的数组成员",其中声明中不需要大小:

struct foo { int i; char c[] };

但是,C++(还)不支持这一点,所以您必须指定一个非零大小。

这是一个非常古老的技巧。

typedef struct 
{
size_t size;
char str[1];
} mystring_t;
mystring_t *allocate(size_t size)
{
return malloc(sizeof(mystring_t) + size -1);
}

然后您可以很容易地重新分配它,因为灵活的部分总是在结构的末尾。

在较新的C版本中

typedef struct 
{
size_t size;
char str[];
} mystring_t;

或使用gcc

typedef struct 
{
size_t size;
char str[0];
} mystring_t;