【问题标题】:C function replace a byte in a specific index of a parameterC函数替换参数的特定索引中的字节
【发布时间】:2021-03-04 07:45:54
【问题描述】:

所以我使用以下代码:

unsigned long replaceByte(unsigned long original,unsigned char newByte,int indexToReplace)
{
    int shift = 8 * indexToReplace;
        unsigned long value = newByte << shift;
        unsigned long mask = 0xff << shift;

        return (~mask & original) | value;
}

我有一个带有 |w| 的单词字节。

  • 字节从 0(最不重要)到 w/8-1(最重要 重要)。

例如:

replaceByte(unsigned long original, unsigned char newByte, int indexToReplace)
correct answer:
replaceByte(0x12345678CDEF3456, 0xAB, 2) --> 0x1234AB78CDEF3456
                       (my code's output is: 0x12345678CDAB3456)
correct answer:
replaceByte(0x12345678CDEF3456, 0xAB, 0) --> 0xAB345678CDEF3456
                       (my code's output is: 0x12345678cdef34AB)

我认为我需要检查系统是大端还是小端,因为我的代码更改了完全相反的字节。例如,它改变了 MSB 而不是 LSB。 但是......我意识到这并不重要,因为我正在使用比特。

如您所见,这里的代码更改了错误的索引:

(!) Error in index: 0. Output: 0x123456789abcdeff
Answer: 0xff3456789abcdeab

 (!) Error in index: 1. Output: 0x123456789abcffab
Answer: 0x12ff56789abcdeab

(!) Error in index: 2. Output: 0x123456789affdeab
Answer: 0x1234ff789abcdeab

 (!) Error in index: 3. Output: 0xffffffffffbcdeab
Answer: 0x123456ff9abcdeab

 (!) Error in index: 4. Output: 0x123456789abcdeff
 Answer: 0x12345678ffbcdeab

(!) Error in index: 5. Output: 0x123456789abcffab
 Answer: 0x123456789affdeab

 (!) Error in index: 6. Output: 0x123456789affdeab
 Answer: 0x123456789abcffab

好吧,我考虑将我的代码更改为带有数组的东西,只是为了获得一个数字 -> 作为数组在其上运行 -> 更改所需的索引 -> 就是这样。 但是..我不能正确地写它所以我坚持移动的东西(我也不能正确地写)。 这是我的尝试:

    unsigned long replaceByte(unsigned long original, unsigned char newByte, int indexToReplace){
    int size = (sizeof(unsigned long));
char a[size];
for (int i=0; i<size; i++){
if (i=0)
a[0] = original & 0xff;
else
a[i] = original>>(8*i) & 0xff;
}
a[indexToReplace] = newByte;
......// stuck
 }

我不允许使用 long long、uint_fast64_t 或 reinterpret_cast 或任何其他“外部”东西。

如果代码在 32 位系统或 64 位系统上运行,我还认为我需要以某种方式进行更改,以确定哪个大小是 unsigned long(4 或 8 字节)。

【问题讨论】:

  • 您应该删除一个不太必要的标签,并用您的编程语言标记您的问题。这将确保它被最多可能能够回答它的用户看到。
  • 首先,maskvalueint。它们截断了值并且需要为unsigned long(与original 相同的类型)。
  • 在 64 位系统上工作,因为 long 是 64 位的。对于 32 位系统,请使用 unsigned long long。这适用于 32 和 64
  • 尝试0xffL 而不是0xff。由于提升规则,后者将执行 32 位移位和截断。修复强制 64 位
  • @DimaCiun 字节序在这里完全无关,因为它是值在内存中存储的方式。由于您没有任何指针并且只是对值进行操作,因此字节序不会影响结果

标签: c bit-manipulation byte bitmask byte-shifting


【解决方案1】:

这是由 [我的] 顶级 cmets 开头的。

valuemask 必须是 unsigned long

此外,在进行移位时,两个值由于表达式提升规则而被/正在被截断[到 32 位]。

在上面,我忘了value有同样的问题。


这是强制正确换档的另一种方法:

unsigned long
replaceByte(unsigned long original,unsigned char newByte,int indexToReplace)
{
    int shift = indexToReplace*8;
    unsigned long value = newByte;
    unsigned long mask = 0xff;

    value <<= shift;
    mask <<= shift;

    return (~mask & original) | value;
}

以上是通常做的事。但是,以下可能也有效:

unsigned long
replaceByte(unsigned long original,unsigned char newByte,int indexToReplace)
{
    int shift = indexToReplace*8;
    unsigned long value = ((unsigned long) newByte) >> shift;
    unsigned long mask = ((unsigned long) 0xff) >> shift;

    return (~mask & original) | value;
}

更新:

嘿,谢谢。提供的代码为我带来以下输出:0x12345678cdef34AB 而不是 0xAB345678CDEF3456。我很确定这与小端有关,因为取代 MSB 而不是 LSB 并不是巧合。

不是字节序的东西。这就是indexToReplace 需要被解释的方式。

处理器根据有效的字节序模式取数据,所以当我们尝试进行移位时,处理器寄存器中的值总是大字节序[所以,不用担心]

正常/通常是索引从右侧开始。但是,根据 [正确] 数据,问题希望索引从左侧开始。

所以,我们只需要调整 index/shift:

unsigned long
replaceByte(unsigned long original,unsigned char newByte,int indexToReplace)
{
#if 0
    int shift = indexToReplace * 8;
#else
    int shift = ((sizeof(unsigned long) - 1) - indexToReplace) * 8;
#endif
    unsigned long value = newByte;
    unsigned long mask = 0xff;

    value <<= shift;
    mask <<= shift;

    return (~mask & original) | value;
}

更新 #2:

它识别“int shift = indexToReplace * 8;”出于某种原因作为评论,但它仍然有效。

那是因为#if 0 是一个 CPP [预处理器] 语句。它以类似于#ifdef NEVERWAS 的方式解释,我们从不执行#define NEVERWAS,因此包含#else 下的代码。

您可能希望在编译时使用-E 和/或-P 选项以查看预处理器阶段的输出

在这种情况下,编译器将看到的唯一是:

int shift = ((sizeof(unsigned long) - 1) - indexToReplace) * 8;

但是,如果我尝试将“#if 0”更改为“#if (is_big_endian == 0)”,当我使用“0”作为 indexToReplace 时,我会得到错误的结果。

请尽量避免将此称为与字节序相关。再一次,不是正在发生的事情。我发布的代码不管处理器端模式如何。

请重新阅读有关正确/正确解释字节索引的部分。这就是人们选择对字节进行编号的方式。

再一次,99.44% 的时间,它是从(LSB 到 MSB)定向的。从图形上看,大多数人使用:

| MSB |     |     |     |     |     |     | LSB |
|  01 |  23 |  45 |  67 |  89 |  AB |  CD |  EF | DATA
|   7 |   6 |   5 |   4 |   3 |   2 |   1 |   0 | INDEX

但是,对于您的确切问题陈述,它是从 left(MSB 到 LSB)定向的:

| MSB |     |     |     |     |     |     | LSB |
|  01 |  23 |  45 |  67 |  89 |  AB |  CD |  EF | DATA
|   0 |   1 |   2 |   3 |   4 |   5 |   6 |   7 | INDEX

这是不寻常的。它也更慢,因为移位的计算更复杂。

它给出:0x12345678CDEF34FF 而不是 0xFF345678CDEF3456

最终,无论你对#if做了什么,它选择了不正确的等式。

【讨论】:

  • 嘿,谢谢。提供的代码为我带来以下输出:0x12345678cdef34AB 而不是 0xAB345678CDEF3456 用于此输入:(0x12345678CDEF3456, 0xAB, 0)。我很确定它与小端相关,因为 LSB 被替换而不是 MSB 并不是巧合。
  • 哇!非常感谢克雷格,它有效! “if 0”检查什么?我的 is_big_endian 函数?所以基本上你已经在这里植入了检查它是小端机器还是大端机器的条件。如果是小端:shift = indexToReplace * 8,如果是大端,则负 1 减去 indexToRplace。我没听错吗?
  • 它识别“int shift = indexToReplace * 8;”出于某种原因作为评论,但它仍然有效。但是,如果我尝试将“#if 0”更改为“#if (is_big_endian == 0)”,当我使用“0”作为 indexToReplace 时,我会得到错误的结果。它给出:0x12345678CDEF34FF 而不是 0xFF345678CDEF3456。
  • 太棒了,我从您的回答中学到了很多,感谢您投入了这么多时间,谢谢!最后一个问题,您告诉我“处理器根据有效的字节序模式获取,所以当我们尝试进行移位时,处理器寄存器中的值总是大字节序”。其中说,如果我错了,请纠正我,这里有一个字节序的东西,尽管你告诉我忘记字节序的东西。你刚刚告诉我它总是把它当作一个大端,或者我错过了什么?
  • Endian only 指的是 memory 中存储的字节顺序。由于您正在处理unsigned long,您可以知道内存的字节序。请参阅:en.wikipedia.org/wiki/Endianness,尤其是示例部分中的图表。
猜你喜欢
  • 2017-02-11
  • 2020-10-23
  • 2018-09-28
  • 1970-01-01
  • 1970-01-01
  • 2011-10-20
  • 2017-08-06
  • 1970-01-01
相关资源
最近更新 更多