简体   繁体   English

返回给定 C 中的指针的 16 位

[英]Returning 16-bits given a pointer in C

A chunk is represented by a 64-bit long integer, which is broken into 4 16-bit sections.一个块由一个 64 位长的 integer 表示,它被分成 4 个 16 位的部分。

I need to return a 16-bit section using the function below.我需要使用下面的 function 返回一个 16 位部分。

unsigned short get_16bitsection(unsigned long *start, int index) {
// Fill this in 
}

It is tempting to use casts to achieve this, but it is a common misconception that "everything is just bytes" and thus that you can do that safely.使用强制转换来实现这一点很诱人,但一个常见的误解是“一切都只是字节”,因此您可以安全地做到这一点。 A rule called strict aliasing actually prohibits doing so.一个称为严格别名的规则实际上禁止这样做。 Your code may appear to work, particularly on older and less sophisticated compilers, but in the age of heavy optimisations you are really playing with fire by violating the language rules like that.您的代码可能看起来可以工作,特别是在较旧和不太复杂的编译器上,但在重度优化时代,您违反了这样的语言规则,实际上是在玩火。

Instead, you should copy the bytes you need into a uint16_t , then return it:相反,您应该将所需的字节复制uint16_t中,然后返回它:

uint16_t get_16bitsection(uint64_t *start, int index) {
  uint16_t result;
  memcpy(&result, (char*)start + index*sizeof(uint16_t), sizeof(uint16_t));
  return result;
}

Here I cast to char* so that we can navigate byte-wise through your chunk (this aliasing is a specifically permitted exception to the usual strict-aliasing rule), then apply an offset of index*sizeof(uint16_t) to reach the desired index (assuming little endian, which you have specified).在这里,我转换为char*以便我们可以按字节导航您的块(此别名是通常严格别名规则的特别允许的例外),然后应用index*sizeof(uint16_t)的偏移量以达到所需的索引(假设您已指定小端序)。 Finally, we copy the bytes into result , and return it.最后,我们将字节复制到result中,并返回它。

If you're concerned about performance, don't be.如果您担心性能,请不要担心。 You were already copying a uint16_t from local scope into the calling scope;您已经将uint16_t从本地 scope 复制到调用 scope 中; just now it has a name.刚才它有了名字。 And if this function is any slower than the aliasing-violating version, then that's evidence that you've confused the optimiser into going too far.如果这个 function混叠违规版本慢,那么这证明你已经混淆了优化器走得太远。

Just use a union.只需使用工会。

long int x=0x123456789abcdef0;

union {
    long int x;
    unsigned short arr[4];
} c;

c.x = x;
printf("%04x %04x %04x %04x\n", c.arr[0], c.arr[1], c.arr[2], c.arr[3]);

Result:结果:

def0 9abc 5678 1234 def0 9abc 5678 1234

Returning 16-bits given a pointer给定指针返回 16 位
A chunk is represented by a 64-bit long integer, which is broken into 4 16-bit sections一个块由一个 64 位长的 integer 表示,它分为 4 个 16 位段

To access the data in a endian independent portable way and retrieve the 0:LS 16-bit to 3:MS 16-bit, use >> .要以独立于字节序的可移植方式访问数据并检索 0:LS 16 位到 3:MS 16 位,请使用>>


As unsigned long may only be 32-bit, recommend unsigned long long or uint_least64_t .由于unsigned long可能只有 32 位,建议使用unsigned long longuint_least64_t
Consider making pointer const to allow this function use on const data.考虑使指针const以允许此 function 用于const数据。

unsigned short get_16bitsection(const unsigned long long *start, int index) {
  #define MASK_16BIT 0xFFFFu
  return MASK_16BIT & (*start >> (16*index));
} 
  • Mask useful on rare machines where unsigned short is not 16 bit.掩码在unsigned short不是 16 位的稀有机器上很有用。 IAC, I prefer mask over casts - gentler way to reduce range. IAC,我更喜欢面具而不是演员 - 减少范围的更温和的方式。
  • Alternatively use a cast: (unsigned short) or (uint16_t) though this is slightly less portable as uint16_t may not exist and unsigned short may be > 16-bit.或者使用强制转换: (unsigned short)(uint16_t)虽然这稍微不那么可移植,因为uint16_t可能不存在并且unsigned short可能 > 16 位。

Maybe I'm missing the point here but it could be as easy as this:也许我在这里错过了重点,但它可能就像这样简单:

unsigned short get_16bitsection_be(unsigned long *start, int index) {
  unsigned short *p = (unsigned short*) start;

  return p[3 - index];
}

unsigned short get_16bitsection_le(unsigned long *start, int index) {
  unsigned short *p = (unsigned short*) start;

  return p[index];
}

Where the difference between big and little endian is relevant here.大端和小端之间的区别在这里是相关的。

Note you should consider using stdint.h to give these types more meaningful names and make it clear what you're actually doing:请注意,您应该考虑使用stdint.h为这些类型赋予更有意义的名称,并明确您实际在做什么:

uint16_t get_16bitsection_le(uint64_t *start, int index) {
  uint16_t *p = (uint16_t*) start;

  return p[index];
}

uint16_t get_16bitsection_be(uint64_t *start, int index) {
  uint16_t *p = (uint16_t*) start;

  return p[3 - index];
}

You were on the right track with your second approach, but that code is heavily cluttered by a lot of things that don't matter, plus the * 8 offset which makes no sense.您的第二种方法走在了正确的轨道上,但是该代码被许多无关紧要的东西弄得一团糟,再加上没有意义的* 8偏移量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM