对结构进行操作并将结果分配给同一结构是否是一种不好的做法?为什么
Is it bad practice to operate on a structure and assign the result to the same structure? Why?
我不记得看到过像这个假设片段这样的代码示例:
cpu->dev.bus->uevent = (cpu->dev.bus->uevent) >> 16; //or the equivalent using a macro
其中,大型结构中的成员使用指针取消引用,对其进行操作,并将结果分配回结构的同一字段。
内核似乎是一个经常出现如此大型结构的地方,但我还没有看到它的例子,并对原因产生了兴趣。
这是否有性能原因,可能与遵循指针所需的时间有关?是不是好风格,如果是,首选方式是什么?
该语句在语法上没有错,但像这样编码更容易:
cpu->dev.bus->uevent >>= 16;
这更像是一个历史问题:内核大多是用C语言编写的(而不是C++),并且在最初的开发意图中(K&R时代)被认为是"高级汇编程序",其语句和表达式应该在C和ASM中具有字面对应关系。 在这种环境中,++i
i+=1
和i=i+1
是完全不同的东西,它们转换为完全不同的CPU指令
当时,不是那么先进和流行,所以遵循指针链两次的想法经常被避免,首先将结果目标地址存储在局部临时变量(很可能是register
)中,然后做赋值。
(如int* p = &a->b->c->d; *p = a + *p;
)
或尝试使用像 a->b->c >>= 16;
)
对于现在的计算机(多核处理器,多级缓存和管道),在寄存器内部执行锥体的速度可以比内存访问快十倍,遵循三个指针比在内存中存储地址更快,从而恢复了"商业模式"的优先级。
然后,编译器优化可以自由更改生成的代码,使其适合大小或速度,具体取决于保留的内容更重要以及您使用的处理器类型。
所以 - 现在 - 无论你写++i
还是i+=1
或i=i+1
都无关紧要:编译器很可能会生成相同的代码,尝试只访问i
一次。 并且遵循指针链两次很可能会被重写为等效于(cpu->dev.bus->uevent) >>= 16
>>=
对应于 x86 衍生处理器中的单个机器指令。
也就是说("这并不重要"),代码风格也确实倾向于反映它最初编写的时代的风格和时尚(因为进一步的开发人员倾向于保持一致性)。
你的代码本身并不"坏",它只是在通常编写的地方看起来很"奇怪"。
只是为了让您了解什么是管道和预测。 考虑两个向量的比较:
bool equal(size_t n, int* a, int *b)
{
for(size_t i=0; i<n; ++i)
if(a[i]!=b[i]) return false;
return true;
}
在这里,一旦我们发现不同的东西,我们就会说它们是不同的。
现在考虑一下:
bool equal(size_t n, int* a, int *b)
{
register size_t c=0;
for(register size_t i=0; i<n; ++i)
c+=(a[i]==b[i]);
return c==n;
}
没有捷径可走,即使我们发现差异,也会继续循环和计数。但是从循环内部移除if
后,如果n
不是那么大(假设少于 20),这可能会快 4 或 5 倍!
优化的编译器甚至可以识别这种情况 - 证明没有不同的副作用 - 可以在第二个代码中返工第一个代码!
我认为这样的事情没有错,它看起来无害:
i = i + 42;
如果您经常访问数据项,则可以考虑如下内容:
tSomething *cdb = cpu->dev.bus;
cdb->uevent = cdb->uevent >> 16;
// and many more accesses to cdb here
但是,即便如此,我还是倾向于将其留给优化器,无论如何,它往往比大多数人做得更好:-)
做这件事本身并没有什么错
cpu->dev.bus->uevent = (cpu->dev.bus->uevent) >> 16;
但根据uevent
的类型,在像这样向右移动时需要小心,这样您就不会意外地将意想不到的位转移到您的值中。例如,如果它是一个 64 位值
uint64_t uevent = 0xDEADBEEF00000000;
uevent = uevent >> 16; // now uevent is 0x0000DEADBEEF0000;
如果您认为您移动了一个 32 位值,然后将新uevent
传递给采用 64 位值的函数,那么您并没有像预期的那样传递0xBEEF0000。由于大小合适(64 位值作为 64 位参数传递),因此此处不会收到任何编译器警告(如果将 64 位值作为 32 位参数传递,则会收到此警告)。
同样有意思的是,上述操作虽然类似于
i = ++i;
这是未定义的行为(有关详细信息,请参阅 http://josephmansfield.uk/articles/c++-sequenced-before-graphs.html),仍然定义良好,因为右侧表达式中没有副作用。
- C++,您能否设计一种数据结构,将指针保存在连续内存中并且不会使它们失效?
- 一种有效的数据结构,用于按 ID 访问和查找加权随机项
- 我想直接在结构中插入,但没有一种方法可以正确避免填充问题
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 在 c++ 中将一种结构类型分配给另一种类型
- c++ 是否提供了一种使整个结构常量(不可修改)的方法?
- cpp 中是否存在一种数据结构,可以轻松地提供一种基于已存在的实例构建新结构的方法
- 类中的结构不是一种类型
- 错误将结构复制到同一类型的另一种
- 将指向结构的指针转换为具有较少字段数的另一种结构类型
- 有没有一种方法可以在基于枚举的可变参数模板函数之间进行选择,这比将函数包装在结构中更简单
- 模板结构可容纳两种类型,但当时只有一种
- 是否有一种更简单的方法可以使用GDB将C捕获的C结构捕获
- 一种按排序顺序保持元素的数据结构,支持快速插入和计算连续元素之间的最大差异
- 在cuda中使用静态成员函数模板结构的另一种方法
- 将C++结构写入文件并使用另一种编程语言读取文件
- C++ 开关结构(运行最后一种情况两次)
- 对结构进行操作并将结果分配给同一结构是否是一种不好的做法?为什么
- 寻找一种提供随机和"sequential"访问的数据结构
- 一种用于整数的下界和上界查询的快速数据结构