5

我正在尝试学习 C 编程,我正在研究一些源代码,有些东西我不明白,尤其是关于位运算符。我阅读了一些有关此的网站,并且对它们的作用有所了解,但是当我回头查看这些代码时,我无法理解它们为什么以及如何使用它们。

我的第一个问题与按位运算符无关,而是与一些 ascii 魔术有关:

  1. 有人可以向我解释以下代码是如何工作的吗?

    char a = 3;
    int x = a - '0';
    

    我知道这样做是为了将 char 转换为 int,但是我不明白它背后的逻辑。为什么/它是如何工作的?

  2. 现在,关于按位运算符,我觉得这里真的很迷茫。

    • 这段代码有什么作用?

      if (~pointer->intX & (1 << i)) { c++; n = i; }
      

      我在某处读到〜反转位,但我看不到这个语句在做什么以及为什么这样做。

      与此行相同:

      row.data = ~(1 << i);
      
    • 其他问题:

      if (x != a)
        {
          ret |= ROW;
        }
      

      |= 运算符到底在做什么?根据我的阅读,|= 是 OR,但我不太明白这个语句在做什么。

      有什么方法可以重写此代码以使其更易于理解,以便它不使用此按位运算符?我发现它们很难理解,所以希望有人能指出正确的方向来理解它们如何更好地工作!


我现在对位运算符有了更好的理解,整个代码现在更有意义了。

最后一件事:显然没有人回应是否会有一种“更清洁”的方式来重写此代码,使其更易于理解,并且可能不是“位级”。有任何想法吗?

4

10 回答 10

17

这将产生垃圾:

char a = 3; 
int x = a - '0';

这是不同的 - 请注意引号:

char a = '3'; 
int x = a - '0';

char数据类型存储一个标识字符的数字。数字 0 到 9 的字符在字符代码列表中都是相邻的,所以如果你从 '9' 的代码中减去 '0' 的代码,你会得到答案 9。所以这将变成一个数字字符代码转换为数字的整数值。

(~pointer->intX & (1 << i))

if如果它不为零,则该语句将其解释为真。使用了三种不同的位运算符。

~ 运算符翻转数字中的所有位,因此如果pointer->intX01101010,那么~pointer->intX将是10010101。(请注意,自始至终,我都在说明一个字节的内容。如果它是一个 32 位整数,我必须写 32 位 1 和 0)。

& 运算符通过分别处理每个位将两个数字组合成一个数字。如果两个输入位均为 1,则结果位仅为 1。因此,如果左侧为00101001,右侧为00001011,则结果将为00001001

最后,<<表示左移。如果从 00000001 开始并将其左移三个位置,则将得到 00001000。因此表达式 (1 << i) 产生一个值,其中第 i 位打开,其他位全部关闭。

将它们放在一起,它会测试位i是否在pointer->intX.

所以你也许能够弄清楚是什么~(1 << i)。如果i4,括号中的内容将是00010000,因此整个内容将是11101111

ret |= ROW;

那相当于:

ret = ret | ROW;

|运算符类似于,&但如果任一输入位为 ,则结果位为 1 1。所以如果retis00100000ROWis 00000010,结果将是00100010

于 2009-03-24T19:27:37.780 回答
0

ret |= ROW;

相当于

ret = ret | ROW;

于 2009-03-24T19:22:41.287 回答
0

单引号用于指示使用单个字符。因此,'0' 是字符 '0',其 ASCII 代码为 48。3-'0'=3-48

'1<<i' 将 1 i 位向左移动,因此只有右侧的第 i 位为 1。
~pointer->intX 否定字段 intX,因此当 intX 具有逻辑 AND 时返回真值(非 0)除了右边的第 i 位之外的每一位都没有设置。

于 2009-03-24T19:24:06.637 回答
0
char a = '3';  
int x = a - '0';

你在这里有一个错字(注意 3 周围的 '),这将字符 3 的 ascii 值分配给 char 变量,然后下一行取 '3' - '0' 并将其分配给 x,因为ascii 值的工作方式,x 将等于 3(整数值)

在第一次比较中,我从来没有见过 ~ 以前那样用在指针上,可能是另一个错字?如果我要读出以下代码:

(~pointer->intX & (1 << i))

我会说“(从指针取消引用的值 intX)和(1 左移 i 次)”

1 << i 是将 1 乘以 2 的幂的快速方法,即如果 i 为 3,则 1 << 3 == 8

在这种情况下,我不知道为什么要反转指针的位..

在第二次比较中,x |= y 与 x = x | 相同。是的

于 2009-03-24T19:25:43.073 回答
0

因为char a = 3; int x = a - '0';我认为你的意思是char a = '3'; int x = a - '0';。如果您意识到 ASCII 中的数字是按顺序排列的,那就很容易理解了,例如“0”、“1”、“2”……所以如果“0”是 48,“1”是 49,那么“1” ' - '0' 是 1。

对于按位运算,在您开始查看位之前很难掌握它们。当您查看二进制数的这些操作时,您可以确切地看到它们是如何工作的......

010 & 111 = 010
010 | 111 = 111
010 ^ 111 = 101
~010 = 101
于 2009-03-24T19:27:20.887 回答
0

我想你可能有一个错字,意思是:

char a = '3';

这样做的原因是所有数字都按顺序排列,而“0”是第一个。显然,'0' - '0' = 0。'1' - '0' = 1,因为 '1' 的字符值比 '0' 的字符值大 1。等等。

于 2009-03-24T19:27:33.663 回答
0

1) char 实际上只是一个 8 位整数。'0' == 48,这意味着。

2) (~(pointer->intX) & (1 << i)) 评估是否未设置任何指针指向的 intX 成员中的第 i 位(从右边开始)。~ 反转位,因此所有 0 变为 1,反之亦然,然后 1 << i 将单个 1 放在所需位置, & 组合这两个值,以便只保留所需位,整个事情评估如果该位开始时为 0,则为 true。

3) | 是按位或。它获取两个操作数中的每一位并执行逻辑或,如果任一操作数设置了该位,则产生一个结果,其中设置了每个位。0b11000000 | 0b00000011 == 0b11000011。|= 是赋值运算符,就像 a+=b 表示 a=a+b,a|=b 表示 a=a|b 一样。

在某些情况下,不使用位运算符可以使事情更容易阅读,但如果没有强大的编译器优化,它通常也会使您的代码显着变慢。

于 2009-03-24T19:28:30.963 回答
0

您引用的减法技巧有效,因为 ASCII 数字按升序排列,从零开始。因此,如果 ASCII '0' 是 48 的值(确实如此),那么 '1' 是 49 的值,'2' 是 50,等等。因此 ASCII('1') - ASCII('0') = 49 - 48 = 1。

就位运算符而言,它们允许您对变量执行位级操作。

让我们分解你的例子:

(1 << i)-- 这是将常数 1 左移 i 位。因此,如果 i=0,则结果为十进制 1。如果 i = 1,则将第 1 位向左移动,用零回填,产生二进制 0010,或十进制 2。如果 i = 2,则将第 2 位移动到左,用零回填,产生二进制 0100 或十进制 4 等。

~pointer->intX-- 这是取指针的 intX 成员的值并将其位反转,将所有零设置为 1,反之亦然。

&-- & 运算符进行按位与比较。如果表达式的左侧和右侧均为 1,则此结果将为 1,否则为 0。

pointer->intX因此,如果在右侧第 i 个位置有一个 0 位,则测试将成功。

此外,|=表示进行按位或比较并将结果分配给表达式的左侧。对于相应的左侧或右侧位为 1 的每个位,按位或的结果为 1,

于 2009-03-24T19:28:41.023 回答
0

1)有人可以向我解释以下代码是如何工作的吗?字符 a = 3; int x = a - '0'; 我知道这样做是为了将 char 转换为 int,但是我不明白它背后的逻辑。为什么/它是如何工作的?

当然。变量a是 char 类型,并且通过在 0 周围加上单引号导致 C 也将其视为 char。最后,整个语句被自动类型转换为其等效整数,因为x被定义为整数。

2)现在,关于按位运算符,我觉得这里真的很迷茫。--- 这段代码是做什么的?if (~pointer->intX & (1 << i)) { c++; n = 我; 我在某处读到 ~ 反转位,但我看不到这个语句在做什么以及为什么这样做。

(~pointer->intX & (1 << i)) is saying:

否定 intX,并将其与 1 左移 i 位

因此,如果 intX = 1011 且 i = 2,您将得到什么,等于

(0100 & 0100) 

-negate 1011 = 0100

-(1 << 2) = 0100

0100 & 0100 = 1 :)

然后,如果 AND 操作返回 1(在我的示例中,它确实如此){ c++; n = 我; }

因此,将 c 加 1,并将变量 n 设置为 i

与此行相同: row.data = ~(1 << i);

Same principle here.
Shift a 1 to the left by i places, and negate.

So, if i = 2 again

(1 << 2) = 0100

~(0100) = 1011

**--- 其他问题:

if (x != a) { ret |= ROW; }

|= 运算符到底在做什么?根据我的阅读,|= 是 OR,但我不太明白这个语句在做什么。**

if (x != a) (希望这对你来说很明显......如果变量 x 不等于变量 a)

ret |= ROW;

equates to

ret = ret | ROW;

这意味着,二进制 OR ret 与 ROW

对于 AND 和 OR 操作完成的确切示例,您应该对二进制逻辑有一个很好的理解。

检查维基百科的真值表......即

位运算

于 2009-03-24T19:36:18.837 回答
0
  1. 我假设你的意思是 char a='3'; 对于第一行代码(否则你会得到一个相当奇怪的答案)。基本原则是数字的ASCII码是连续的,即'0'=48的代码,'1'=49的代码,等等。减去“0”只是将 ASCII 码转换为实际数字,例如“3”-“0”=3,依此类推。请注意,这仅在您从中减去“0”的字符是实际数字时才有效 - 否则结果将没有什么意义。

  2. 一个。如果没有上下文,就无法说出这段代码的“为什么”。至于它在做什么,当指针->intX 的位 i 未设置时,if 语句的计算结果似乎为真,即该特定位为 0。我相信 & 运算符在 ~ 运算符之前执行,因为 ~运算符的优先级很低。该代码可以更好地使用括号来使预期的操作顺序更清晰。在这种情况下,操作的顺序可能并不重要——我相信结果是一样的。

    湾。这只是创建一个除第 i 位之外的所有位都设置为 1 的数字。为第 i 位创建掩码的一种简便方法是使用表达式 (1<<i)。

  3. 本例中的按位或运算用于将 ROW 常量指定的位设置为 1。如果未设置这些位,则设置它们;如果它们已经设置,则没有效果。

于 2009-03-24T19:42:21.240 回答