C语言操作符超详细讲解下篇

目录
  • 前言
  • 赋值操作符
  • 单目操作符
    • 单目操作符介绍
    • sizeof 和 数组
  • 关系操作符
  • 逻辑操作符
  • 条件操作符
  • 逗号表达式
  • 下标引用与函数调用和结构成员
    • [ ] 下标引用操作符
    • ( ) 函数调用操作符
    • 访问一个结构的成员
  • 表达式求值
    • 隐式类型转换-整形提升
    • 算术转换
    • 操作符的属性
  • 总结

前言

本文接着学习操作符的内容。

赋值操作符

赋值操作符就是能够重新赋值

int weight = 120;//体重
weight = 89;//不满意就赋值
double salary = 10000.0;
salary = 20000.0;//使用赋值操作符赋值
//赋值操作符可以连续使用
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//连续赋值,不规范 不友好

//
x = y+1;
a = x;
//这样的写法更加清晰而且易于调试
//复合赋值符
	+=
	-=
	*=
	/=
	%=
	>>=
	<<=
	&=
	|=
	^=
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁

单目操作符

单目操作符介绍

!         逻辑反操作
-         负值
+         正值
&         取地址
sizeof     操作数的类型长度(以字节为单位)
~         对一个数的二进制按位取反
--         前置、后置--
++         前置、后置++
*         间接访问操作符(解引用操作符)
(类型)     强制类型转换

int main()
{
	int a = -10;
	int *p = NULL;
	printf("%d\n", !2);
	printf("%d\n", !0);
	a = -a;
	p = &a;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof a);//可以,不建议
	printf("%d\n", sizeof int);//写法不行
	return 0;
}

sizeof 和 数组

void test1(int arr[])//直接受到首元素的地址。接受的数组只要1个首元素
{
	printf("%d\n", sizeof(arr));//对地址求长度,4个字节
}
void test2(char ch[])
{
	printf("%d\n", sizeof(ch));对地址求长度,4个字节
}
int main()
{
	int arr[10] = {0};
	char ch[10] = {0};
	printf("%d\n", sizeof(arr));//40
	printf("%d\n", sizeof(ch));//10
	test1(arr);//数组传参,只传首元素的地址
	test2(ch);
	return 0;
}

结果见下图,函数中,数组传参,只传首元素的地址。函数接收到首元素的地址,此时数组只有1个首元素。

对地址求长度的结果为4。这是固定的,和数组类型无关。

int main()
{
	int a = 0;
	printf("%d\n", ~a);
	//00000000 00000000 00000000 00000000   补码
	//11111111 11111111 11111111 11111111 取反后的补码
	//11111111 11111111 11111111 11111110  反码
	//10000000 00000000 00000000 00000001 -> 原码 -1
	int b = 11;
	//00000000000000000000000000001111
	//11111111111111111111111111111011
	//00000000000000000000000000000100
	//1<<2
	b |= (1<<2);
	printf("%d\n", b);//15
	int c = 11;
	c &= (~(1 << 2));
	printf("%d\n", c);//11

	return 0;
}
int main()
{
	int a = 3;
	int b = ++a;//前置++,先++,后使用//a=a+1,b=a
	int b = a++;//后置++,先使用,后++。//b=a,a=a+1
	int b = --a;//前置--,先--,后使用 //a=a-1,b=a
	int b = a--;//后置--,先使用,再-- //b=a,a=a-1

	printf("%d\n", b);
	return 0;
}

关系操作符

>
>=
<
<=
!=     用于测试“不相等”
==     用于测试“相等”

int main()
{//判断字符串相同,不能这样判断
	if ("abcdef" == "abbq")//字符相同不能这样判断
	{

	}
	return 0;
}

逻辑操作符

&&         逻辑与
||         逻辑或

要区分逻辑与和按位与
1&2----->0
1&&2---->1

要区分逻辑或和按位或
1|2----->3
1||2---->1

int main()
{
	int a = 0;
	int b = 0;
	/*int y = 0;
	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
	{

	}*/
	if (a || b)
	{
		printf("hehe\n");
	}
	return 0;
}
int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	//从左边开始算,如果为0,后面不会算的,直接结束
	//如果为1,接着算后面的
	i = a++ && ++b && d++;
	//i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	return 0;
}

int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	//从左边开始算,如果为1,后面不会算的,直接结束
	//如果为0,接着算后面的
	i = a++||++b||d++;
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
	return 0;
}

条件操作符

exp1 ? exp2 : exp3

int main()
{
	int a = 3;
	int b = 0;
	int m = (a > b ? a : b);//效果等同于下面的选择语句
	//if (a > 5)
	//	b = 3;
	//else
	//	b = -3;
	//b = (a > 5 ? 3 : -3);
	return 0;
}

逗号表达式

exp1, exp2, exp3, …expN

/代码1
int a = 1;
int b = 2;
//从左向右此次执行,
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
printf("a=%d b=%d\n", a, b);
printf("c=%d\n", c);

//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
a = get_val();
count_val(a);
while (a > 0)
{
	//业务处理
	a = get_val();
	count_val(a);
}
//如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
	//业务处理
}

下标引用与函数调用和结构成员

[ ] 下标引用操作符

操作数:一个数组名 + 一个索引值
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9

( ) 函数调用操作符

//接受一个或者多个操作数:
//第一个操作数是函数名,剩余的操作数就是传递给函数的参数
void test1()
{
	printf("hehe\n");
}
void test2(const char *str)
{
	printf("%s\n", str);
}
int main()
{
	test1(); //实用()作为函数调用操作符。
	test2("hello bit.");//实用()作为函数调用操作符。
	return 0;
}

访问一个结构的成员

有两种方式:

  • . 结构体.成员名
  • -> 结构体指针->成员名
struct Stu
{
	char name[20];
	int age;
	float score;
};
void print1(struct Stu ss)
{//结构体变量.成员名
	printf("%s %d %f\n", ss.name, ss.age, ss.score);
}
void print2(struct Stu* ps)
{
	//printf("%s %d %f\n", (*ps).name, (*ps).age, (*ps).score);
	//结构体指针->成员名,与上面的方式效果一样
	printf("%s %d %f\n", ps->name, ps->age, ps->score);
}
int main()
{
	struct Stu s = {"张三", 20, 90.5f};
	strcpy(s.name, "张三丰");
	//scanf("%s", s.name);
	// 这是错误的方式 s.name是地址  字符串也是地址
	//*(s.name) = "张三丰" //两个不同的地址赋值有问题的
	print1(s);
	printf("\n");
	print2(&s);
	return 0;
}

表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。

同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

隐式类型转换-整形提升

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

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

提升。

整型提升的意义:

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

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

//整形提升是按照变量的数据类型的符号位来提升的
int main()
{//字符只占一个字节,先提升到4个字节
	char c1 = 3;
	//00000000000000000000000000000011 补码
	//00000011 - c1
	char c2 = 127;
	//00000000000000000000000001111111
	//定义c3是一个字节,先提升到4个字节
	//01111111 - c2
	char c3 = c1 + c2;
	//00000000000000000000000000000011 补码
	//00000000000000000000000001111111 补码
	//00000000000000000000000010000010 补码相加
	//10000010 - c3 字符型结果只能装下8位
	//再提升到4个字节
	//11111111111111111111111110000010 补码
	//11111111111111111111111110000001 反码
	//10000000000000000000000001111110 原码
	//-126 打印原码
	printf("%d\n", c3);//
	return 0;
}
int main()
{
	char a = 0xb6;//10110110  整型提升后都是前面都是1
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)//所以不可能相等
		printf("a");
	if (b == 0xb600)//所以不可能相等
		printf("b");
	if (c == 0xb6000000)
		printf("c");

	return 0;
}

实例1中的a、b要进行整形提升,但是c不需要整形提升。a、b整形提升之后,变成了负数,所以表达式 a == 0xb6,b == 0xb600 的结果是假,但是c不发生整形提升,则表达式 c == 0xb6000000 的结果是真。

所程序输出的结果是:

c

//实例2
int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));
	printf("%u\n", sizeof(+c));
	printf("%u\n", sizeof(-c));
	return 0;
}

实例2中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节。

表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof© ,就是1个字节。

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。

但是算术转换要合理,要不然会有一些潜在的问题

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

操作符的属性

复杂表达式的求值有三个影响的因素。

  • 操作符的优先级
  • 操作符的结合性
  • 是否控制求值顺序

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

一些问题表达式:

//表达式的求值部分由操作符的优先级决定。
//表达式1
a*b + c*d + e*f//不明确的
//表达式2
c + --c;//不明确的
//代码3-非法表达式
int main()
{
	int i = 10;
	i = i-- - --i * ( i = -3 ) * i++ + ++i;//不明确的
	printf("i = %d\n", i);
	return 0;
}
//代码4
int fun()
{
	static int count = 1;
	return ++count;
}
int main()
{
	int answer;
	answer = fun() - fun() * fun();//不明确的
	printf( "%d\n", answer);//输出多少?
	return 0;
}
//代码5
int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);//不明确的
	printf("%d\n", ret);
	printf("%d\n", i);
	return 0;
}

写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。有时候同样的表达式在不编译器的结果是不同的。

总结

逻辑操作符种类较多,以上都是较常用的。这部分内容学习基本结束了。

下一篇开始学习指针相关内容了。

到此这篇关于C语言操作符超详细讲解下篇的文章就介绍到这了,更多相关C语言 操作符内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C语言之操作符

    目录 1.加减乘 2.除(/) 注意: 3.取余(%) 注意: 4.移位操作符(>> <<) 注意 5.位操作符(| ,& ,^) 6.逻辑操作符(&& , ||) 7.单目操作符 7.1正负号(+ -) 7.2sizeof() 7.3按位取反(~) 7.4逻辑反操作(!) 8.赋值操作符 9.复合操作符.等式左边不是常量 补充: 总结 1.加减乘 c里的加减乘同我们实际生活功能相同,我们不做探究,看例子即可 2.除(/) c语言里的除法实质上是求商操作(零

  • C语言 操作符分类解析与使用

    目录 操作符的分类 算术操作符 移位操作符 位操作符 逻辑操作符 逗号表达式 表达式求值 隐式类型转换 算术转换 操作符的属性 xwg今天就带各位大佬来了解一波C语言的操作符. 操作符的分类 常见的操作符分别如下: 算术操作符 移位操作符 位操作符 逻辑操作符 逗号表达式 算术操作符 算术操作符是我们最常用的操作符:+ - * / % 注: 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数. 对于 / 操作符如果两个操作数都为整数,执行整数除法,而只要有浮点数执行的就是浮点数除法.

  • C语言操作符进阶教程(表达式求值隐式类型转换方法)

    目录 结构体 表达式求值 隐式类型转换 意义: 方法 算术转换 操作符属性 结构体 结构体变量的声明需要在主函数之上或者主函数中声明,如果在主函数之下则会报错,而且c语言中的结构体不能直接进行强制转换,只有结构体指针才能进行强制转换. 涉及结构体的操作符这里讲两个:. (结构体访问操作符)-> () 首先写一段代码: int main() { struct Stu s = {"me",19,60}; prinft("%s %d %lf",s.who,s.age

  • C语言操作符超详细讲解上篇

    目录 前言 1.操作符的分类 2.算术操作符 3.移位操作符 3.1 左移操作符 3.1.1 正数左移1位 3.1.2 负数左移1位 3.2 右移操作符 3.2.1 正数右移1位 3.2.2 负数右移1位 3.3 移位操作符说明 4.位操作符 4.1 练习 1 4.2 练习 2 总结 前言 操作符主要内容包括:各种操作符的介绍,用表达式求值. 1.操作符的分类 算术操作符 移位操作符 位操作符 赋值操作符 单目操作符 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用.函数调用和结构成员

  • C语言各种操作符透彻理解上篇

    前言:在我们程序编写领域,操作符给我们提供了很多的运算便利,但操作符琳琅满目,我们要怎样用好它们呢,下面就带你来熟悉熟悉这些多样的操作符. 操作符分类: 算术操作符 .移位操作符 .位操作符 .赋值操作符 .单目操作符 .关系操作符 .逻辑操作符 .条件操作符 .逗号表达式 .下标引用.函数调用和结构成员 1.算数操作符(+.--.*.%./) 这里我们着重讲一下取余(模)%操作符和除法/操作符 #include<stdio.h> int main() { //取余(模).除法 int ret

  • 一篇文章带你入门C语言:操作符

    目录 操作符 分类 算术操作符 移位操作符 整数存储规则 左右移位规则 赋值操作符 单目操作符 取地址操作符& 解引用操作符* 类型长度操作符sizeof 按位取反操作符~ ++ -- 操作符 强制类型转换操作符(type) 关系操作符= 逻辑操作符 短路运算 条件操作符 逗号表达式 下标引用.函数调用和结构成员 下标引用操作符[] 函数调用操作符() 结构成员操作符. -> 结构体定义 结构体使用 结构体地址 表达式求值 隐式类型转换 整型提升 如何整型提升 有符号数 无符号数 算术转换

  • C语言各种操作符透彻理解下篇

    1.单目操作符 之前有了解到的三目操作符(?:),指的是有三个操作数 例如:3+5 其中,+是一个操作符 3是左操作数 5是有操作数 +则是一个双目操作符 那么什么是单目操作符呢,也就是只有一个操作数的 我们常见的操作符有:  这里我们来详细介绍一下sizeof.~.++与-- 1.sizeof sizeof常用来计算类型的长度,比如数组类型,int.char.short等等.同时,sizeof是操作符,不是函数,所以后面的括号可以省略.但算类型长度的时候不能省,这是语法要求. 下面就是size

  • C语言操作符基础知识详解

    目录 一.单目操作符: 二.关系操作符 三.条件操作符 四.逗号表达式 五.逻辑操作符 总结 一.单目操作符: !:逻辑反操作符: -:负数操作符: +:整数操作符: &:取地址操作符: sizeof:操作数的类型长度操作符: sizeof(数组名)--数组名表示整个数组,sizeof(数组名)求的是整个数组的大小,单位是字节 例如: int a[10] = { 0 }; printf("%d\n",sizeof(a)); suzeof(a[0]);-->(4) 不同的表

  • C语言操作符超详细讲解下篇

    目录 前言 赋值操作符 单目操作符 单目操作符介绍 sizeof 和 数组 关系操作符 逻辑操作符 条件操作符 逗号表达式 下标引用与函数调用和结构成员 [ ] 下标引用操作符 ( ) 函数调用操作符 访问一个结构的成员 表达式求值 隐式类型转换-整形提升 算术转换 操作符的属性 总结 前言 本文接着学习操作符的内容. 赋值操作符 赋值操作符就是能够重新赋值 int weight = 120;//体重 weight = 89;//不满意就赋值 double salary = 10000.0; s

  • C语言指针超详细讲解下篇

    目录 前言 指针运算 指针±整数 4.1 指针±整数 4.2 指针-指针 4.3 指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 7.1 举例 1 7.2 举例 2 总结 前言 本文接着上一篇内容,继续学习指针相关知识点. 指针运算 指针±整数 指针-指针 指针的关系运算 4.1 指针±整数 #define VALUE 5 int main() { float values[VALUE]; float *vp; //指针+-指针,关系运算 for (vp = &values[0];

  • C语言函数超详细讲解下篇

    目录 前言 函数的声明和定义 函数声明 函数定义 举例 简单的求和函数 把加法单独改写成函数 添加函数声明 带头文件和函数声明 静态库(.lib)的生成 静态库文件的使用方法 函数递归 什么是递归? 递归的两个必要条件 练习1 一般方法 递归的方法 练习2 一般方法 递归方法 练习3 一般方法 递归方法 练习4 一般方法 递归方法 递归与迭代 递归隐藏的问题 如何改进 选递归还是迭代 总结 前言 紧接上文,继续学习函数相关内容. 函数的声明和定义 函数声明 告诉编译器有一个函数叫什么,参数是什么

  • C语言数组超详细讲解下篇扫雷

    目录 前言 1.扫雷是什么? 2.程序框架 2.1 主函数 2.2 函数menu 2.3 函数game 2.3.1 函数init_board 2.3.2 函数show_board 2.3.3 函数set_mine 2.3.4 函数find_mine 2.3.5 函数get_mine_count 3.头文件.h 4.游戏试玩 总结 前言 本文接着复习前面所学知识,以扫雷游戏为例. 1.扫雷是什么? 百度百科:<扫雷>是一款大众类的益智小游戏,于1992年发行.游戏目标是在最短的时间内根据点击格子

  • C语言数据的存储超详细讲解下篇浮点型在内存中的存取

    目录 前言 浮点型在内存中的存储 浮点数存储的例子 浮点数存储规则 IEEE 754规定 IEEE 754对有效数字M的特别规定 IEEE 754对指数E的特别规定 存入内存是E的规定 从内存取出时E的规定 举例 1 举例 2 举例 3 判断两个浮点数是否相等? 总结 前言 本文接着学习数据的存储相关的内容,主要学习浮点型数在内存中的存储与取出. 浮点型在内存中的存储 常见的浮点数:3.14159.1E10 浮点数家族包括: float.double.long double 类型 浮点数表示的范

  • C语言指针超详细讲解上篇

    目录 前言 1.指针是什么 1.1 指针变量 1.2 指针是内存中一个最小单元的编号 2.指针和指针类型 2.1 指针±类型 2.2 指针的解引用 2.2.1 int* 类型的解引用 2.2.2 char* 类型的解引用 3.野指针 3.1 野指针成因 3.1.1 指针未初始化 3.1.2 指针越界访问 3.1.3 指针指向的空间释放 3.2 如何规避野指针 总结 前言 本文开始指针相关内容的学习,主要内容包括: 指针是什么 指针和指针类型 野指针 指针运算 指针和数组 二级指针 指针数组 1.

  • C语言数组超详细讲解上

    目录 前言 1.一维数组的创建和初始化 1.1 一维数组的创建 1.2 一维数组的初始化 1.3 一维数组的使用 1.4 一维数组在内存中的存储 2.二维数组的创建和初始化 2.1 二维数组的创建 2.2 二维数组的初始化 2.3 二维数组的使用 2.4 二维数组在内存中的存储 3.数组越界 4.数组作为函数参数 4.1 冒泡排序函数的错误设计 4.2 数组名是什么? 4.3 对数组名的用法进行总结 4.4 冒泡排序函数的正确设计 总结 前言 本文主要介绍数组相关的内容,主要内容包括: 一维数组

  • C语言数据结构超详细讲解单向链表

    目录 1.链表概况 1.1 链表的概念及结构 1.2 链表的分类 2. 单向链表的实现 2.1 SList.h(头文件的汇总,函数的声明) 2.2 SList.c(函数的具体实现逻辑) 2.2.1 打印链表 2.2.2 搞出一个新节点(为其他函数服务) 2.2.3 链表尾插 2.2.4 链表头插 2.2.5 链表尾删 2.2.6 链表头删 2.2.7 查找节点 2.2.8 在pos位置之前插入 2.2.9 在pos位置之后插入 2.2.10 删除pos位置 2.2.11 删除pos之后位置 2.

  • C语言数组超详细讲解中篇三子棋

    目录 前言 1.三子棋是什么? 1.1 百度百科 1.2 游戏编程准备工作 2. 程序实现 2.1 搭建程序框架 2.2 模块化编程 2.2.1 源文件test.c 2.2.2 源文件play.c 2.2.3 头文件play.h 2.3 程序实现—拓展play函数 2.3.1 棋盘初始化与打印函数 2.3.2 玩家下棋函数 PlayMover 2.3.3 电脑下棋函数 ComputerMove 2.2.4 判断赢家函数 WhoIsWin 总结 前言 本文主要是对前面所学内容进行复习和练习,学习内

随机推荐