十、操作符详解

目录

1、操作符分类

2、二进制转换

2.1二进制转十进制

2.1.1、十进制转二进制

2.2、二进制转八进制和十六进制

2.2.1、二进制转八进制

2.2.2、二进制转十六进制

3、原码、反码、补码

4、移位操作符(移动的是二进制位)

4.1、左移操作符

4.2、右移操作符

5、位操作符:&、|、^、~

6、单目操作符

7、逗号表达式

8、下标访问[]、函数调用()

8.1、[]下标引用操作符

9、结构成员访问操作符

9.1、结构体

9.1.1结构的声明

10、操作符的属性: 优先级、结合性

10.1、优先级

10.2、结合性

11、表达式求值

11.1、整型提升

11.2、算术转换

11.3、问题表达式解析

11.3.1、表达式1

11.3.2、表达式2

11.3.3、表达式3

11.3.4、表达式4

11.3.5、表达式5

11.4、总结


1、操作符分类

(1)算术操作符:+、-、*、/、%、

(2)移位操作符:<<、>>、(移动的是二进制位)

(3)位操作符:&、|、^、(也是使用二进制位进行计算)

(4)赋值操作符:=、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=、

(5)单目操作符:!、++、--、&、*、+、-、~、sizeof、(类型)、

(6)关系操作符:>、>=、<、<=、==、!=、

(7)逻辑操作符:&&、||、

(8)条件操作符:?=、

(9)逗号表达式:,、

(10)下标引用:[]、

(11)函数调用:()、

(12)结构成员访问:,、->、

上述操作符,我们已经学过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作符,今天继续介绍一部分,操作符中有一些操作符和二进制有关系。

2、二进制转换

我们经常能听到:2进制、8进制、10进制、16进制,其实2进制、8进制、10进制、16进制是数值的不同表示形式,比如数值15的各种进制的表示形式:

①15的2进制:1111

②15的8进制:17

③15的10进制:15

④15的16进制:F

//16进制的数值之前写:0x

//8进制的数值之前写:0

我们重点介绍一下2进制:

首先要讲二进制先从十进制讲起:

十进制

(1)十进制中满10进1

(2)十进制的数字每一位都是0~9的数字组成,其实二进制也差不多

二进制

(1)二进制中满2进1

(2)二进制的数字每一位都是0~1的数字组成

那么1101就是二进制的数字了

十进制123组成:

相加之后得123

二进制1111转十进制:

相加之后得15

八进制17转十进制:

相加之后得15

十六进制转十进制:

2.1二进制转十进制

十进制的123表示的值为123,每一位都是有权重的,每一位权重为10^0、10^1、10^2  ……

二进制同理若有1101(二进制)

2.1.1、十进制转二进制

比如十进制125转二进制:

把余数从下到上拿出来就是125的二进制数1111101

2.2、二进制转八进制和十六进制

2.2.1、二进制转八进制

八进制的数字每一位是0~7的,0~7的数字各自写成二进制,最多有3个二进制位就足够了,比如7的二进制是111,所以在二进制转八进制数的时候,从二进制序列中右边低位开始向左每3个二进制会换算下一个八进制位,剩余不够三个二进制位的直接换算。

若二进制数01101011

2.2.2、二进制转十六进制

3、原码、反码、补码

当我们要把一个数转换成二进制表示时

整数的二进制有3中表示形式(暂时不考虑浮点数)

有符号整数的三种表示方法,均有符号位和数值为两部分,二进制序列中,最高位的1位被当作符号位,剩余的都是数值位

int的长度时四个字节,等于32个bit位

对于unsigned  int来说没有符号位,全都为数值位

正整数的原码、反码、补码都相同;

负整数的三种表示方法各不相同。

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码。

反码:将原码的符号位不变,其他位一次按位取反就可以得到反码。

补码:反码+1得到补码。

补码得到原码也可以使用取反+1的操作:

比如说-10:

整数在内存中存储的是补码的二进制序列,原因在于,使用补码,可以将符号位和数值位统一处理,同时(CPU只有加法器)此外,补码与原码相互转换,其运算过程相同,不需要额外的硬件电路。

在计算机系统中,数值一律用补码来表示和存储。

4、移位操作符(移动的是二进制位)

<<左移操作符

>>右移操作符

注:移位操作符的操作数只能是整数。

4.1、左移操作符

移位规则:左边抛弃,右边补0

  int main()
{
int a = 10;//移动的是存储在内存中的二进制位(补码)
int b = a << 1;//左移一位
printf("b = %d\n",b);
printf("a = %d\n",a);
return 0;
}

 int main()
{
int a = -1;
int b = a << 1;
printf("b = %d\n",b);
printf("a = %d\n",a);
return 0;
}

左移一位有乘二的效果。

4.2、右移操作符

移位规则:首先右移运算分两种:

①逻辑右移:左边用0填充,右边丢弃。

②算术右移:左边用原该值的符号位填充,右边丢弃。

int main()
{
int a = -10;
int b = a >> 1;
printf("a = %d\n",a);
printf("b = %d\n",b);
return 0;
}

右移到底采用算术右移还是逻辑右移,是取决于编译器的!

通常采用的都是算术右移

浮点数不能移位

对于移位运算符,不要移动负数位,这个时标准未定义的。

例如:

int a = 10;
a >>= -1;//error

5、位操作符:&、|、^、~

① & //按位与

② | //按位或

③ ^ //按位异或

④ ~ //按位取反

注:他们的操作数必须是整数

& --- 按(二进制)位与                &&  --- 逻辑与

| ---按(二进制)位或                   || ---逻辑或

 &(按位与):

int main()
{
int a = 6;
int b = -7;
int c = a & b;//a和b的补码的二进制序列进行运算
printf("c = %d\n",c);
return 0;
}

00000000 00000000 00000000 00000110   -- 6的补码

11111111 11111111 11111111 11111001  -- -7的补码

00000000 00000000 00000000 00000000  -- 6 & -7 = 0

规则:二进制位对应的补码如果有0则为0,同时为1才为1;

记忆技巧:&(按位与)长得像&&(并且)所以都为1才为1,有一个0就为0。

|(按位或):

int main()
{
int a = 6;
int b = -7;
int c = a | b;
printf("c = %d\n",c);
return 0;
}

6的补码 00000000 00000000 00000000 00000110

- 7的补码 11111111 11111111 11111111 111111001

c的补码 11111111 11111111 11111111 11111111

c的原码  1000000 00000000 00000000 00000001

规则:只要有1就为1,两个同时位0才为0。

记忆技巧:|(按位或)长得像||(或者)所以有一个1就为1,有两个0才为0。

^(按位异或)

int main()
{
int a = 6;
int b = -7;
int c = a ^ b;
printf("c = %d\n",c);
return 0;
}

6的补码 00000000 00000000 00000000 00000110

-7的补码 11111111 11111111 11111111 11111001

c的补码 11111111 11111111 11111111 11111111

c的原码 10000000 00000000 00000000 00000001

规则:a和b补码的二进制位进行运算,相同为0,相异为1。

~(按位取反)

int main()
{
int a = 0;
printf("~a = %d\n",~a);
return 0;
}

~按二进制位取反:

0的补码 00000000 00000000 00000000 00000000

不管符号位,全部取反:11111111 11111111 11111111 11111111


小汇总:

&(按位与)-- 有0为0,同1为1

|(按位或)-- 有1为1,同0为0

^(按位异或)-- 相同为0,相异为1

~(按位取反)-- 不管符号位,全部取反

重点:

0 ^ a = a        3 ^ 3 ^ 5 = 5        3 ^ 5 ^ 3 = 5

异或是支持交换律的:

a ^ b ^ a = b        b ^ a ^ b = a

不创建新的变量实现a和b的值互换:

方法一:

int main()
{
int a = 3;
int b = 5;
printf("a = %d\n,b = %d\n",a,b);
a = a + b;
b = a - b;
a = a - b;
printf("a = %d\n,b = %d\n",a,b);
return 0;
}

方法二:

int main()
{
int a = 3;
int b = 5;
printf("a = %d\n,b = %d\n",a,b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d\n,b = %d\n",a,b);
return 0
}

 练习1:编写代码实现:求一个整数存储在内存中的二进制中1的个数

int count_bit_one(int n);int main()
{
int input = 0;
scanf("%d",&input);
int ret = count_bit_one(input);
printf("%d",ret);
return 0;
}int count_bit_one(int num)
{
int count = 0;while(num){if((num % 2)== 1)count++;num /= 2;}
return count;
}

这样只能计算正整数,如果给一个负数就会出错。

这里我们可以把输入的值,在传参是转换成无符号整型就可以解决当下的问题。

我们换一种思路,是否可以不考虑正负数的问题,比如 -1:

-1:11111111 11111111 11111111 11111111

1:00000000 00000000 00000000 00000001

-1 & 1 :00000000 00000000 00000000 00000001

若n&1 == 1则其最低位原本就是1

若n&1 == 0则其最低位原本就是0

如果想知道这个数最低位的前一位就可以使用右移符,若想知道全部,可以右移32位

请看如下代码:

int count_bit_one(int num)
{int i = 0;int count = 0;for(i = 0; i < 32; i++){if(((num >> i)& 1)== 1){count++;}}
return count;
}int main()
{
int input = 0;
scanf("%d",&input);
int ret = count_bit_one(input);
printf("%d",ret);
return 0;
} 

n & (n - 1)

若 n = 11        n = n&(n - 1)

可以发现每执行一次n = n&(n - 1),n就去掉一个1,这个表达式执行了几次这个n就有几个1.

把n的二进制序列中最右边的1去掉了,下面看代码:

int count_bit_one(int x)int main()
{
int input = 0;
scanf("%d",&input);
int ret = count_bit_one(input)
printf("%d\n",ret);
return 0;
}int count_bit_one(int num)
{
int count = 0;
while(num){n = n & (n - 1);count++;}
return 0;
}

写一个代码判断n是否是2的次方数:

void count_bit_one(int x)int main()
{
int input = 0;
scanf("%d",&input);
count_bit_one(input);
return 0;
}void count_bit_one(int n)
{
if(n &(n - 1)== 0)
printf("yes\n");
}

练习二:二进制位置0或置1:编写代码将13的二进制序列的第5位修改为1,然后再改回0。

int main()
{
int input = 0;
int n = 0;
printf("请输入数字,以及要改为1的位数\n");
scanf("%d%d",&input,&n);
input = input | (1 << (n -1));
printf("%d\n",input);
input = input & (~(1 << (n - 1)));
printf("%d\n",input);
return 0;
}

6、单目操作符

!、++、--、&、*、+、-、~、sizeof、(类型)、

单目操作符的特点是只有一个操作数,在弹幕操作符中只有&和*没有介绍,这两个操作符,我们放在学习指针的时候学习。

& -- 取地址操作符               

* -- 解引用操作符

7、逗号表达式

逗号表达式,就是用逗号隔开的多个表达式

逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a , b = a + 1);
printf("%d\n",c);
return 0;
}

8、下标访问[]、函数调用()

8.1、[]下标引用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

int main()
{
printf("hello\n");
return 0;
}

上述代码的括号就是函数调用操作符,操作数为printf、“hello\n”

int Add(int x, int y)
{
return x + y;
}int main()
{
int ret = Add(3,4);
printf("%d\n"ret);
return 0;
}

上述代码中Add函数的操作数是Add、3、4。

函数调用操作符最少一个操作数。

9、结构成员访问操作符

9.1、结构体

C语言已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设想描述学生,描述一本书,这时单一的内置类型是不行的。

结构是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同的变量,如:标量、数组、指针,甚至是其他结构体。

9.1.1结构的声明
struct tag //tag自定义
{
member - list;//成员列表
}variable - list;//变量列表
struct Student
{
//成员变量
char name[20];
int age;
float score;
} s4,s5,s6;struct Student s3;//全局变量int main()
{
int a = 0;
struct Student s1;//局部变量
struct Student s2 = {"张三",20,97.0};
return 0;
}

 

//结构体变量.结构体成员名

//->这个是依赖指针的,所以在后面给大家介绍

10、操作符的属性: 优先级、结合性

C语言的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。

10.1、优先级

优先级指的是,如果一个表达式包含多个运算符,哪个操作符优先执行。各种运算符的优先级是不一样的。

//相邻的操作符,优先级高的先执行

比如说 int a = 3 + 4 * 5

那么就会限制性4 * 5,而不是 3 + 4,是因为 *的优先级更高。

10.2、结合性

相邻的操作符的优先级相同的情况下,结合性说了算。

比如说 int b = 3 + 4 + 5,优先级相同,加号的结合性从左到右,所以先3 + 4,而不是4 + 5。

11、表达式求值

11.1、整型提升

C语言中整型算术运算重视至少以默认整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换被称为整型提升。

就比如:

char a = 10;
char b = 20;
char c = a + b;

这样就会发生整型提升。

整型提升的意义:

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。通用CPU是难以直接实现两个8比特字节直接相加运算。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int ,然后才能进入CPU去执行运算。

如何进行整型提升?

1.有符号整数提升是按照变量的数据类型的符号位来提升的。

2.无符号整数提升,高位补0。

11.2、算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。

整型提升讨论的是:

表达式中char和收人头类型的值。

算术转换讨论的是:

类型大于等于整形类型的类型

比如:

int a;        double b;

a + b;

a就会进行算术转换,转换成double类型,如果某个操作数在上表排名靠后,那就首先要转换为另外一个操作数的类型后才执行运算。

11.3、问题表达式解析

11.3.1、表达式1
a * b + c * d + e * f

由于*比+的优先级高,只能保证*的计算比+早,但优先级并不能决定第三个*比第一个+早执行。

所以运算顺序可能是:

 

11.3.2、表达式2
c + --c;
11.3.3、表达式3
{
int i = 10;
i = i-- - --i *(i = -3)* i++ + ++i;
}
11.3.4、表达式4
int fun()
{
static int count = 1;
return ++count;
}int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n",answer);
return 0;
}
11.3.5、表达式5
int i = 1;
ret = (++i)+(++i)+(++i)
printf("%d\n",ret);
printf("%d\n",i);

我们用稿纸计算结果位9、4,而使用VS2022计算结果位12、4,结果不一致,使用反汇编来看一下计算过程。

int ret = (++i) + (++i) + (++i)

mov eax,dword ptr [i]

把i的值放到eax中(eax是寄存器)

add eax,1

eax自增1

这时用监视器看到eax由1变为2

mov dword ptr [i],eax

上面代码其实就是++i

mov ecx dword ptr [i]

add ecx,i

mov dword ptr [i],ecx

mov edx,dword ptr [i]

有完成一次++i

add edx,i

这时监视器中

eax = 2        i = 3        ecx = 3        edx = 4

mov dword ptr [i],edx

edx值放到i中

mov eax,dword ptr [i]

add eax,dword ptr [i]

add eax,dword ptr [i]

mov dword ptr [ret],eax

三次++i放到eax中去了,监视器中此时eax = 12

ret = 12

其实VS中先完成三次(++i)再把三个i相加 

11.4、总结

即使有了操作符的优先级和结合性,写出的表达式依然不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在风险的,建议不要写出特别复杂的表达式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47091.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMware虚拟机下安装Ubuntu(详细教程,最小系统的安装,含VMware Tools)

1.VM的下载安装 VMware的下载安装教程_vm16 pro下载-CSDN博客 2. Ubuntu 下载 在官网或者镜像站下载所需版本的.ios镜像&#xff0c;这个镜像在接下来的步骤中会用到&#xff1a; Ubuntu 22.04.4 LTS 下载 和 清华大学开源软件镜像站 - Ubuntu 22.04.4 下载 3. 创建虚拟机 […

【C语言】深入解析希尔排序

文章目录 什么是希尔排序&#xff1f;希尔排序的基本实现代码解释希尔排序的优化希尔排序的性能分析希尔排序的实际应用结论 在C语言编程中&#xff0c;希尔排序是一种高效的排序算法&#xff0c;是插入排序的一种更高效的改进版本。它通过比较相距一定间隔的元素来进行排序&am…

【STM32嵌入式系统设计与开发---拓展】——1_10矩阵按键

这里写目录标题 1、矩阵按键2、代码片段分析 1、矩阵按键 通过将4x4矩阵按键的每一行依次设为低电平&#xff0c;同时保持其它行为高电平&#xff0c;然后读取所有列的电平状态&#xff0c;可以检测到哪个按键被按下。如果某列变为低电平&#xff0c;说明对应行和列的按键被按下…

【Java】详解抽象类和接口的区别

一、抽象类和接口的主要区别表格 特性抽象类接口声明关键字abstractinterface声明访问修饰符public、protected、default&#xff08;不能用private&#xff09;public、default继承关键字extendsimplements变量跟普通类一样&#xff0c;可以包含实例变量、静态变量等 只能包含…

excel表怎么增乱序单词表 和正序单词表四六级要来了?!Excel帮你构建自己的单词库

excel表怎么增乱序单词表 和正序单词表四六级要来了&#xff1f;&#xff01;Excel帮你构建自己的单词库 1.背单词的第一步&#xff0c;当然是先上网找电子版的单词集。 盘搜搜 2. 建立 xls 格式的表格 3. 把下载的单词数据 复制到 表格 粘贴 4.新建一列 辅助列 生成随机数来…

网络编程-TCP/IP

网络概述 网络采用分而治之的方法设计&#xff0c;将网络的功能划分为不同的模块&#xff0c;以分层的形式有机组合在一起。 每层实现不同的功能&#xff0c;其内部实现方法对外部其他层次来说是透明的。每层向上层提供服务&#xff0c;同时使用下层提供的服务 网络体系结构…

SpringMVC注解全解析:构建高效Web应用的终极指南 (上)

SpringMVC 是一个强大的 Web 框架&#xff0c;广泛应用于 Java Web 开发中。它通过注解简化了配置&#xff0c;增强了代码的可读性。本文将全面解析 SpringMVC 中常用的注解及其用法&#xff0c;帮助你构建高效的 Web 应用。 一. MVC介绍 MVC 是 Model View Controller 的缩写…

数字通云平台 智慧政务OA PayslipUser SQL注入漏洞复现

0x01 产品简介 数字通云平台智慧政务OA产品是基于云计算、大数据、人工智能等先进技术,为政府部门量身定制的智能化办公系统。该系统旨在提高政府部门的办公效率、协同能力和信息资源共享水平,推动电子政务向更高层次发展。 0x02 漏洞概述 数字通云平台 智慧政务OA Paysli…

使用百度语音技术实现文字转语音

使用百度语音技术实现文字转语音 SpringBootVue前后端分离项目 调用api接口需要使用AK和SK生成AccessToken,生成getAccessToken的接口有跨域限制,所以统一的由后端处理了 部分参数在控制台->语音技术->在线调试里面能找到 Controller RestController RequestMapping(&q…

PostgreSQL的引号、数据类型转换和数据类型

一、单引号和双引号&#xff08;重要&#xff09;&#xff1a; 1、在mysql没啥区别 2、在pgsql中&#xff0c;实际字符串用单引号&#xff0c;双引号相当于mysql的,用来包含关键字&#xff1b; -- 单引号&#xff0c;表示user_name的字符串实际值 insert into t_user(user_nam…

浏览器跨tab页面通信方式总结

需求&#xff1a; 浏览器不同 tab 标签页之间是独立的&#xff0c; 如果要通信必须通过特殊手段来实现跨标签页通信。 1.StorageEvent 事件 当一个标签页 localStorage 变化时&#xff08;sessionStorage 无效&#xff09;&#xff0c;同源下另一个或其他所有标签页使用 DO…

python多级表头汇总

需求&#xff1a;将图一的数据展示为图二样式 图一&#xff1a; 图二&#xff1a; 图一具体的Excel截图 图二具体样式 python解决办法&#xff1a; # 导入 pandas 库&#xff0c;用于数据处理 import pandas as pd# 加载 Excel 文件 file_path 多级表头读取实例.xl…

科研绘图系列:R语言circos图(circos plot)

介绍 Circos图是一种数据可视化工具,它以圆形布局展示数据,通常用于显示数据之间的关系和模式。这种图表特别适合于展示分层数据或网络关系。Circos图的一些关键特点包括: 圆形布局:数据被组织在一个或多个同心圆中,每个圆可以代表不同的数据维度或层次。扇区:每个圆被划…

【BUG】已解决:SyntaxError invalid syntax

SyntaxError invalid syntax 目录 SyntaxError invalid syntax 【常见模块错误】 错误原因&#xff1a; 解决办法&#xff1a; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于…

图书馆定位导航:RFID、VR与AR技术在图书馆中的应用

图书馆作为知识的宝库&#xff0c;承载着无数求知者的梦想与期待&#xff0c;随着馆藏书籍数量的激增与图书馆布局的日益复杂&#xff0c;读者在寻找目标书籍往往有许多困难。传统的索引号查询方式虽能提供书籍的基本信息&#xff0c;但在寻找过程中&#xff0c;因不熟悉图书馆…

【Android】使用视图绑定ViewBinding来代替findViewById

文章目录 介绍作用用法开启ViewBinding功能自动生成绑定类在Activity中使用访问视图控件 区别 介绍 ViewBinding 是 Android 开发中的一个功能&#xff0c;它简化了访问视图的过程&#xff0c;避免了使用 findViewById 的繁琐步骤。它通过生成与布局文件相对应的绑定类&#xf…

CentOS 7 安装Jenkins2.346.1(war方式安装)

既然想要安装Jenkins&#xff0c;肯定是先要从官网解读所需环境配置信息&#xff0c;如需了解更多自行查阅 https://www.jenkins.io/doc/book/installing/linux/ JDK17&#xff0c;Maven3.9 安装 先从官网分别下载JDK17与Maven3.9 下载好之后上传至服务器、并解压&#xff1a…

算法学习day13(动态规划)

一、打家劫舍III 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 …

ubuntu 24 PXE Server (bios+uefi) 批量部署系统

pxe server 前言 PXE&#xff08;Preboot eXecution Environment&#xff0c;预启动执行环境&#xff09;是一种网络启动协议&#xff0c;允许计算机通过网络启动而不是使用本地硬盘。PXE服务器是实现这一功能的服务器&#xff0c;它提供了启动镜像和引导加载程序&#xff0c;…

dev c++ 添加osg包含目录失败

在dev c 中添加osg的包含目录&#xff1b; 编译程序&#xff0c;出现下图错误&#xff1b; 打开出错文件&#xff0c;修改编码为UTF-8&#xff0c; 错误还是没有消除&#xff1b; 根据资料&#xff0c;osg是基于opengl开发的&#xff1b;不过我也没在osg的目录下看到opengl的头…