我目前正在用 C 编写一个新的 String 结构,如下所示,并练习指针算术。
struct String {
uint32_t check;
uint32_t capacity;
uint32_t length;
char data[1];
} String;
我对这部分代码有点困惑
define STRING(s) ((String*)(s - 3*sizeof(uint32_t)))
void utstrfree(char* self) {
// if(*(self - sizeof(uint32_t) * 3) == SIGNATURE) WHY DOES THIS NOT >WORK?
assert((STRING(self)->check) == SIGNATURE); //check if it's a >utstring
free(self-sizeof(uint32_t) * 3); //properly free the malloc and alternative could have been free(STRING(self))
}
我了解我的 STRING 宏如何工作并且它工作正常,但我不太确定为什么我的 if 语句不起作用。谁能给我解释一下吗?
最佳答案
正如 @AslakBerby 已经观察到的,我假设您的断言中的表达式确实有效,但它并不等于注释掉的 if
语句中的条件表达式。特别是,由于 self
是 char *
,因此表达式 *(self - sizeof(uint32_t) * 3)
的类型为 char
,而表达式 STRING(self)->check
的类型为 uint32_t
。假设评估后者已经定义了行为,则前者仅产生后者的第一个字节,按照系统特定的存储顺序。
但总的来说,就这个通用方案的工作而言,它是脆弱且不安全的:
- 它对 struct String 类型的表示进行了假设,但实现不需要满足这些假设;
- 它经常超出数组范围,从而调用未定义的行为;
- 它没有提供表示就地子字符串的机制;
- 它提供了与标准库的字符串函数的足够互操作性,让用户期望获得比实际提供的更高的互操作性。
建议:
- 依靠
offsetof()
计算结构内的相对位置。它更安全、更清晰。 - 甚至不要尝试与纯
char
数组字符串进行直接互操作 - 按实际情况处理这些对象。毕竟,此类练习的重点通常不在于数据结构本身,而在于随之而来的改进函数,例如strlen()
的 O(1) 版本。无需费尽心力来适应这些对象与标准库函数的使用。 - 考虑使用指向数据的指针,而不是将数据直接嵌入到对象中。这肯定会更好地适应调整大小,并且它也可以支持就地子字符串。
- 如果您确实将数据直接嵌入到对象中,请使用灵活的数组成员来执行此操作。
关于C 结构体指针算术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42260674/