当前位置:首页 > 技术 > 正文内容

C语言操作符深度解析与二进制运算原理

访客 技术 2026年7月7日 1

整型数据的内存存储机制

计算机内存以连续字节序列存放整型数据,其占用空间由具体类型决定。常见规格包括1字节、2字节、4字节及8字节,均采用二进制编码方案。以8位无符号数为例,可覆盖0至255的数值区间。

多字节整型遵循特定字节排列规则。小端模式(Little-Endian)将低位字节置于低地址端,高位字节置于高地址端。以32位数值0x12345678为例,其内存分布为:

地址        内容
0x1000      0x78
0x1001      0x56
0x1002      0x34
0x1003      0x12

大端模式(Big-Endian)则相反,高位字节存入低地址。该模式在当代主流硬件中已较少采用。

原码、反码与补码

整型以补码形式驻留内存。三种编码均包含符号位与数值位,符号位0标识正数,1标识负数。

以32位系统为例:

数值 1:  00000000 00000000 00000000 00000001
数值 -1: 11111111 11111111 11111111 11111111

补码转换规则:

  • 正数:原码、反码、补码三者一致
  • 负数:原码 → 符号位不变、数值位取反得反码 → 反码加一得补码

1. 算术操作符

基础运算符:+ - * / %

  • %仅适用于整型操作数,返回除法余数
  • 其余运算符可同时处理整型与浮点型

1.1 除法运算特性

整型除法:两侧均为整数时执行,结果截断小数部分。

浮点除法:任一侧为浮点数即触发,结果保留小数。

#include <stdio.h>
int main(void) {
    int m = 7, n = 2;
    double p = 7.0, q = 2.0;
    
    printf("%d\n", m / n);      /* 输出: 3 */
    printf("%f\n", p / q);      /* 输出: 3.500000 */
    printf("%f\n", m / q);      /* 输出: 3.500000 */
    
    /* 除数为零将导致运行时异常 */
    /* printf("%d", m / 0); */
    
    return 0;
}

2. 移位操作符

操作对象限定为整型,不可移动负数个位。

2.1 左移 <<

规则:左侧位舍弃,右侧补零。

/* 数值5的二进制 */
00000000 00000000 00000000 00000101

/* 左移2位后 */
00000000 00000000 00000000 00010100  /* 结果为20 */

2.2 右移 >>

实现方式分为两类:

  • 逻辑右移:右侧位舍弃,左侧补零
  • 算术右移:右侧位舍弃,左侧填充原符号位

C标准未指定具体方式,主流编译器采用算术右移。

#include <stdio.h>
int main(void) {
    signed int val = -8;  /* 补码: 11111111...11111000 */
    
    printf("%d\n", val >> 1);  /* 算术右移得 -4 */
    
    unsigned int uval = 8;
    printf("%u\n", uval >> 1);  /* 逻辑右移得 4 */
    
    return 0;
}

⚠️ 移位位数为负属于未定义行为。


3. 位操作符

针对二进制位逐位运算,操作数须为整型。

3.1 按位与 &

对应位全1则结果为1,否则为0。

#include <stdio.h>
int main(void) {
    int x = 6;   /* 0110 */
    int y = 10;  /* 1010 */
    
    printf("%d\n", x & y);  /* 0010 = 2 */
    
    return 0;
}

3.2 按位或 |

对应位存1则结果为1,双0方为0。

    0110  (6)
  | 1010  (10)
  --------
    1110  (14)

3.3 按位异或 ^

对应位相异为1,相同为0。

#include <stdio.h>
int main(void) {
    int a = 12;   /* 1100 */
    int b = 10;   /* 1010 */
    
    int res = a ^ b;  /* 0110 = 6 */
    printf("%d\n", res);
    
    /* 经典应用:无临时变量交换 */
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
    
    return 0;
}

4. 赋值操作符

基础形式:变量 = 表达式

支持连续赋值(可读性较差,建议拆分):

int k;
int j;
k = j = 15 + 3;  /* 先计算18赋给j,再将j值赋给k */

复合赋值符集合:

+=  -=  *=  /=  %=
<<=  >>=  &=  |=  ^=

5. 单目操作符

5.1 逻辑非 !

真值转假,假值转真。常用于条件判断:

if (!(ptr == NULL)) { /* 指针有效 */ }

5.2 sizeof 运算符

  • 返回类型或对象的字节大小
  • 编译期运算符,非函数调用
  • 变量可省略括号:sizeof var合法
#include <stdio.h>
int main(void) {
    short s;
    
    printf("%zu\n", sizeof(s));      /* 合法 */
    printf("%zu\n", sizeof s);       /* 合法,证明非函数 */
    printf("%zu\n", sizeof(short));  /* 类型须加括号 */
    
    return 0;
}

5.3 自增自减

形式行为
++i / --i先增减,后使用值
i++ / i--先使用值,后增减
#include <stdio.h>
int main(void) {
    int cnt = 5;
    
    printf("%d\n", ++cnt);  /* 6,先加后用 */
    
    cnt = 5;
    printf("%d\n", cnt++);  /* 5,先用后加 */
    printf("%d\n", cnt);    /* 6 */
    
    return 0;
}

5.4 其他单目运算符

  • - 负号
  • + 正号(通常省略)
  • & 取地址
  • ~ 按位取反(0变1,1变0)

6. 关系操作符

>   >=   <   <=   ==   !=

注意区分赋值=与相等判断==


7. 逻辑操作符

  • && 逻辑与:两侧表达式全真则真,存在短路求值特性
  • || 逻辑或:两侧表达式有真则真,存在短路求值特性

8. 条件操作符

语法:条件 ? 表达式A : 表达式B

条件为真取A值,为假取B值。

#include <stdio.h>
int main(void) {
    int score = 78;
    
    /* 替代简单if-else */
    char grade = (score >= 60) ? 'P' : 'F';
    printf("%c\n", grade);
    
    /* 嵌套使用 */
    int level = (score >= 90) ? 3 : (score >= 60) ? 2 : 1;
    printf("%d\n", level);
    
    return 0;
}

9. 逗号表达式

形式:exp1, exp2, ..., expN

按序执行各子表达式,整体结果为最末表达式值。

#include <stdio.h>
int main(void) {
    int m, n;
    
    /* 先执行m=3,再执行n=m+2,整体值为5 */
    int result = (m = 3, n = m + 2, n * 2);
    
    printf("m=%d, n=%d, result=%d\n", m, n, result);
    /* 输出: m=3, n=5, result=10 */
    
    return 0;
}

10. 特殊操作符

10.1 下标引用 []

数组声明与元素访问中的方括号含义不同:

int dataset[10];      /* 声明:定义含10元素的数组 */
dataset[7] = 100;      /* 访问:引用第8个元素(下标从0起) */

[]接受两个操作数:数组名与索引值,等价于*(dataset + 7)

10.2 函数调用 ()

首操作数为函数标识符,后续为实参列表。参数数量可变,最少为零个。

#include <stdio.h>
void greet(void) {           /* 无参函数 */
    printf("Hello\n");
}

double compute(double base, int exp) {
    double acc = 1.0;
    while (exp-- > 0) acc *= base;
    return acc;
}

int main(void) {
    greet();                          /* 零实参 */
    printf("%f\n", compute(2.5, 3));   /* 两实参 */
    
    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。