C语言 -- 再谈操作符

C语言 -- 再谈操作符

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

[ ]下标引用操作符

操作数:一个数组名 + 一个索引值

#include<stdio.h>
void main()
{
	int arr[10]; //创建数组
	arr[9] = 10; //使用下标引用操作符
	//[ ]的两个操作数是arr和9
}

( )函数调用操作符

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

# include<stdio.h>
void test1()
{
	printf("测试\n");
}

void test2(const char *str) //const限定一个变量不允许被改变,产生静态作用
{ 
	printf("%s\n", str);
}
void main()
{
	test1(); //( )作为函数调用操作符
	const char *str = "hello,world";
	test2(str);  //( )作为函数调用操作符
}

访问一个结构的成员

两种方式:

.结构体.成员名
->结构体指针->成员名

#include<stdio.h>
//访问一个结构体成员
struct Stu
{
	char name[10];
	int age;
	char sex[5];
	double score;
};

void set_age1(struct Stu stu)
{
	stu.age = 18;
}

void set_age2(struct Stu* pStu)
{
	pStu->age = 18; //结构成员访问
}
void main()
{
	struct Stu stu;
	struct Stu* pStu = &stu;//结构成员访问

	stu.age = 20; //结构成员访问
	set_age1(stu);

	pStu->age = 20; //结构成员访问
	set_age2(pStu);
}

表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定的;
有些表达式的操作数在求值过程中可能需要转换为其他类型

隐式类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的;
为了获得这个精度,表达式中的字符和短整型操作数在使用之前需要被转换成普通整型,这种转换称为整型提升

整型提升的意义:

  1. 表达式的整型运算要在CPU的相应运算器件内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int字节长度,同时也是CPU的通用寄存器长度;
  2. 因此,即使两个char类型相加,在CPU执行时实际上也要先转为CPU内整形操作数的标准长度;
  3. 通用CPU是难以直接实现两个8比特直接相加运算的,所以表达式中各种长度可能小于int长度的整型值都需要先转换为int或者unsigned int,然后才能送去CPU执行运算。
void main()
{
	char a, b, c;
	b = 33;
	c = 34;
	a = b + c;
	printf("%c,  %d\n", a, a);
	printf("%c,  %d\n", b, b);
	printf("%c,  %d\n", c, c);
}

在上述代码中,b和c的值会被提升为普通整型,然后再执行加法运算;
加法运算完成之后,结果将被阶段,然后存储到a中。
进行整型提升的方式:

有符号整型提升,高位补充符号位;

char c1 = -1;
char类型一个字节,-1的:
原码:1000 0001;
补码:1111 1111;
提升:1111 1111 1111 1111 1111 1111 1111 1111

char c2 = 1;
原码:0000 0001
提升:0000 0000 0000 0000 0000 0000 0000 0001

无符号整型提升,高位补0.

代码示例:

#include<stdio.h>
//整型提升示例1
void main()
{
	char a = 0xb6;
	//1011 0110
	//1111 1111 1111 1111 1011 0110 -- 提升,补码
	//1111 1111 1111 1111 1011 0101 -- 反码
	//1000 0000 0000 0000 0100 1010 -- 原码
	//-0x4a
	short b = 0xb600;
	//1011 0110 0000 0000
	//1111 1111 1011 0110 0000 0000 -- 提升,补码
	//1111 1111 1011 0101 1111 1111 -- 反码
	//1000 0000 0100 1010 0000 0000 -- 原码
	//-0x4a00

	if (a == -0x4a)
		printf("a提升\n");
	if (b == -0x4a00)
		printf("b提升\n");
}
//整型提升示例2
void main()
{
	char c = 1;
	printf("%d\n", sizeof(c));
	printf("%d\n", sizeof(c+c));
	printf("%d\n", sizeof(!c));
}

算数转换

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

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

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

注:算术转化要合理,否则会存在一些潜在问题

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

操作符的属性

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

  1. 操作符的优先级
  2. 操作符的结合性
  3. 是否控制求值顺序

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

在不同的编译器中,下面代码的执行结果可能不同,这主要由编译器自身的机制来决定。因为函数的调用先后顺序是无法通过操作符的优先级来确定。
问题代码示例:

int fun()
{
	static int count = 1;
	return ++count;
}
void main()
{
	int answer;
	answer = fun() + fun() * fun();
	//由操作数的优先级我们只能得知先算乘法后算加法,但是我们无法得知哪个函数先被执行。
	printf("%d\n", answer);
}
上一篇:位运算(腾讯二面)


下一篇:自增自减运算符、初识Math类、逻辑运算符、位运算符、扩展赋值运算符、三元运算符 2021-11-15