C语言初阶——操作符

一、算术操作符

在C语言中,算术操作符是用来执行基本数学运算的符号,包括加法、减法、乘法、除法以及取模(求余数)等。这种操作符有两个操作数,位于操作符两端的数字就是操作数,这种操作符也叫双目操作符。

+        -        *        /        %        

  1. 除了%之外的操作符都可以作用于整数和浮点数。
  2. 对于/操作符,如果两个操作数都为整数,那么执行整数除法。只有要浮点数就执行浮点数除法。
  3. %的两个操作数必须是整数,返回的是整除之后的余数。
#include<stdio.h>
int main()
{
	int num1 = 1;
	double num2 = 3.2;
	int num3 = 2;
	int num4 = 7;
	printf("%f\n", num1 + num2);
	printf("%d\n", num1 + num3);
	printf("%f\n", num2 - num1);
	printf("%f\n", num2 * num1);
	printf("%f\n", num2 / num1);
	printf("%d\n", num4 % num3);
}

运行结果为:

在计算机中执行算数运算一个不可避免的话题是操作精度问题,如果将一个浮点数和整数进行运算,结果将会是一个浮点数。浮点数可以表示小数部分而整数不可以,因此直接将一个浮点数的结果存储到整型变量中是不可行,这会导致数据丢失——浮点数的小数部分会直接被截断。

#include<stdio.h>
int main()
{
	int num1 = 1;
	double num2 = 3.2;
	printf("%d\n", num2 / num1);//这样写对吗?
	printf("%f\n", num2 / num1);
}

 输出结果为:

直接讲浮点数存储到整型变量中,编译器可能会给出警告,如果想要完整的存储这个结果应当采用一个浮点数变量。如果你想将浮点数转换为整数并存储在整型变量中,可以使用强制类型转换或者像floor,ceil,round这样的函数来处理。

#include<stdio.h>
#include<math.h>
int main()
{
	double k = 3.57;
	int num1 = (int)k;//强制类型转换
	int num2 = floor(k);//向下取整,但不会改变变量的类型
	int num3 = ceil(k);//向上取整
	int num4 = round(k);//四舍五入
	printf("%d\n", num1);
	printf("%d\n", num2);
	printf("%d\n", num3);
	printf("%d\n", num4);
}

输出结果为:

 另外负数求模的规则是余数按照无符号整数的取模运算,但是最后的正负号取决于第一个操作数。

#include<stdio.h>
#include<math.h>
int main()
{
	printf("%d\n", -10 % 3);
	printf("%d\n", 10 % -3);
	printf("%d\n", -10 % -3);
}

 输出结果为:

二、移位操作符

在讲移位操作符之前,先简单讲讲数据在内存中的存储。整数的二进制表示方法有补码,原码,反码三种。

有符号整数的三种表示方法由符号位和数值位两部分组成。在二进制序列中,最高的以为被当作符号位,其余被当作数值位。符号位用"0"表示正,用"1"表示负。

正整数的原码、补码、反码都相同。负数的三种表示方法各不相同:

  • 原码:直接将数值按照正负数的形式转换成二进制即可。
  • 补码:将原码的符号位不变,其他位依次按位取反即可。
  • 补码:反码+1可得到补码。

将补码按数值位取,反末尾加1可以得到原码。

int a = 1;//int型整数在内存中占4字节,也就是32bit
原码:00000000 00000000 00000000 00000001//最高位是符号位,正整数的符号位用‘0’表示
//正整数的原、反、补码都相同,所以无需转换

int b = -1;
原码:10000000 00000000 00000000 00000001//最高位是符号位,负整数的符号位用‘1’表示
反码:11111111 11111111 11111111 11111110//原码符号位不变,数值位按位取反,得到反码
补码:11111111 11111111 11111111 11111111//反码+1,得到补码

在C语言中,移位操作符用于对二进制数进行左移或右移操作。这些操作符可以用来高效地实现乘法、除法、位掩码等操作。

<<        >>

左移操作符(<<):

左移操作符将一个数的二进制表示向左移动指定的位数,并在右边填上0。左移操作通常相当于将数字乘以2^{n},其中 n 是移动的位数。

int a = 5; // 二进制表示为 0000 0101
int b = a << 2; // b 的值现在为 20,二进制表示为 0001 0100

右移操作符(>>):

右移操作符将一个数的二进制表示向右移动指定的位数。右移操作通常相当于将数字除以2^{n},其中n是移动的位数。有右移运算分为两种:

  1. 逻辑移位,左边用0填充,右边丢弃。
  2. 算术移位,左边用符号位数值填充,右边丢弃。
int a = 20; // 二进制表示为 0001 0100
int b = a >> 2; // b 的值现在为 5,二进制表示为 0000 0101

注意:

  1. 符号扩展:对于有符号整数,右移时最高位(符号位)会被复制以保持原来的符号(正数还是负数)。这意味着如果一个负数被右移,新的高位将被设置为1(表示负数)。
  2. 溢出问题:移位操作可能会导致溢出,尤其是在左移操作时,如果超过了整数的最大表示范围,结果将是未定义的。
  3. 位数限制:移位的位数不能超过整数的位宽。如果移位的位数超过了整数的位宽,结果也是未定义的。

三、位操作符

&        |        ^        ~

按位与操作符(&):

当&用作按位与操作符时,它会对两个操作数进行按位逻辑与操作。按位与操作符逐位比较两个操作数的补码形式,对应位置上的比特位只有都为1时,结果才为1,否则为0。

#include<stdio.h>
int main()
{
	int m = 1;
	int n = -1;
	printf("%d\n", m & n);
	//1的补码:00000000000000000000000000000001
   //-1的补码:11111111111111111111111111111111
 //1&-1的结果:00000000000000000000000000000001
  //结果的补码是正整数,正整数的原、反、补码都相同,所以这也是结果的原码,转换成10进制就是1
}

按位或操作符(|):

当|用作按位或操作符时,它会对两个操作数进行按位逻辑或操作。按位或操作符逐位比较两个操作数的二进制表示,对应位置上的比特位只要有一个为1,结果就为1,否则为0。

#include<stdio.h>
int main()
{
	int m = 1;
	int n = -1;
	printf("%d\n", m | n);
	//1的补码:00000000000000000000000000000001
   //-1的补码:11111111111111111111111111111111
 //1|-1的结果:11111111111111111111111111111111
  //对结果的补码进行转换得到其原码:10000000000000000000000000000001(转换成10进制就是-1)
}

按位异或操作符(^):

^操作符被称为按位异或操作符。它用于对两个整数进行按位异或操作。按位异或操作符逐位比较两个操作数的二进制表示,对应位置上的比特位只有不同才为1,相同则为0。

#include<stdio.h>
int main()
{
	int m = 1;
	int n = -1;
	printf("%d\n", m^n);
	//1的补码:00000000000000000000000000000001
   //-1的补码:11111111111111111111111111111111
 //1^-1的结果:11111111111111111111111111111110
  //对结果的补码进行转换得到其原码:10000000000000000000000000000010(转换成10进制就是-2)
}

按位取反操作符(~):

~操作符用于对整数的二进制表示中的每一位进行取反操作,即将1变为0,将0变为1。

#include<stdio.h>
int main()
{
	printf("%d\n", ~0);
	//0的补码:00000000000000000000000000000000
   //~0的结果:11111111111111111111111111111111
	//对结果的补码进行转换得到其原码:10000000000000000000000000000001(转换成10进制就是-1)
;
}

四、赋值操作符

在变量创建的时候给它一个初始值叫做初始化,在变量创建好以后,再给一个值叫做赋值。

#include<stdio.h>
int main()
{
	int a = 0;//初始化
	int b;
	b = 1;//赋值
    a = b = 1;//赋值操作符可以连续使用
}

五、复合赋值符

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

#include<stdio.h>
int main()
{
	int a = 0;
	a += 10;//复合赋值符写法
	a = a + 10;//展开写法
    //其它的复合赋值符类似
}

六、单目操作符

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

这里讲那些没有讲过的以及与前面意义不同的单目运算符。 

自增操作符(++):

自增操作符分为前置++与后置++,它们共同的效果是使操作数的数值+1,他们的区别是前置++是先+1,后使用;后置++是先使用,后+1。

#include <stdio.h>
int main()
{
    int a = 0;
    int b = ++a;//先使a+1,再赋值给b
    printf("a=%d b=%d\n", a, b);
    int c = 0;
    int d = c++;//先赋值给d,再自增
    printf("c=%d d=%d\n", c, d);
    return 0;
}

自减操作符(--):

与自增操作符的两种用法的区别类似 ,前置- -是先-1,后使用;后置- -是先使用,后-1。

#include <stdio.h>
int main()
{
    int a = 1;
    int b = --a;//先自减,再赋值给b
    printf("a=%d b=%d\n", a, b);
    int c = 1;
    int d = c--;//先赋值给d,再自减
    printf("c=%d d=%d\n", c, d);
    return 0;
}

取地址操作符(&):

取地址操作符用于获取一个变量或表达式的内存地址。取地址操作符通常与指针一起使用,用于存储和操作变量的地址。

#include <stdio.h>
int main()
{
    int a = 1;
    int* p = &a;//创建一个指针变量存储a的地址
    printf("%p", p);
    return 0;
}

解引用操作符(*):

它用于访问通过指针指向的变量或内存区域的内容。解引用操作符*通常与指针一起使用,以获取指针所指向的数据。

#include <stdio.h>
int main()
{
    int a = 1;
    int* p = &a;
    *p = 20;//通过解引用操作符修改指向的数据
    printf("%d", *p);
    return 0;
}

sizeof操作符:

sizeof是一个操作符,用于获取数据类型或变量的大小(以字节为单位)。它可以用于确定各种数据类型、结构体、联合体、数组等的大小。sizeof操作符返回的结果是一个无符号整数类型的值。

#include <stdio.h>
int main()
{
    int a = 5;
    double b = 0.0;
    printf("%zd\n", sizeof a);//int类型的大小是4字节
    printf("%zd\n", sizeof b);//double类型的大小是8字节
    printf("%zd\n", sizeof(a = 8 + 5));//把8+3的值赋给a,表达式的最终结果就是a,a的类型是int
    printf("%zd\n", sizeof(int));//int类型的大小是4字节
    printf("%zd\n", sizeof(char));//char类型的大小是1字节
    printf("%zd\n", sizeof(double));//double类型的大小是8字节
    return 0;
}

注意,printf函数中的占位符%zd会根据CPU的不同,判断数据的长度。另外,sizeof后的操作数如果不是数据类,而是表达式或者变量时,是可以省略掉后边的括号的。但是由于操作符的优先级问题,有时省略掉括号会出现问题。

七、强制类型转换

强制类型转换是将一种数据类型强制转换为另一种数据类型的方法。

int a = 4.5;//a是整型变量,4.5是double类型,编译器会报错
int a = (int)4.5;//将4.5强制转换为int类型,这种强制转换只取整数部分

八、关系操作符

>        >=        <        <=        !=        ==

关系操作符用于比较两个值,并返回一个布尔结果(即true或false)。在C语言中,通常使用整数0表示false,非零值表示true。关系操作符的结果通常用于条件判断、循环和其他逻辑控制结构中。这里并没有很多东西可讲,只是要注意的是,注意=与==的混淆使用。

九、逻辑操作符

&&        || 

逻辑与操作符&&的作用是将两个或多个条件表达式连接起来,所有条件都必须为真,最终结果才为真。如果有一个条件为假,则结果为假。 

逻辑或操作符||的作用是将两个或多个条件表达式连接起来,只要有一个条件为真,最终结果就为真。只有当所有条件都为假时,结果才为假。

#include <stdio.h>
int main()
{
    int a = 2;
    int b = 0;
    printf("%d\n", (a > 1) && (a > b));//&&两侧的表达式都为真,则整个逻辑表达式为真,用1表示
    printf("%d\n", (a < 1) && (a > b));//&&左侧的表达式为假,则整个逻辑表达式为假,用0表示
    printf("%d\n", (a < 1) || (a > b));//||右侧的表达式为真,则整个逻辑表达式为真,用1表示
    printf("%d\n", (a > 3) || (a < b));//||两侧的表达式都为假,则整个逻辑表达式为假,用0表示
    return 0;
}

十、条件操作符

exp1 ? exp2 : exp3

 也叫三目操作符,它的运算逻辑是如果exp1为真,则exp2的运算结果为表达式的结果,否则exp3是表达式的结果。

#include <stdio.h>
int main()
{
    int a = 1;
    int b = 0;
    int c = a > b ? a : b;//a>b为真,a的值是整个表达式的结果
    int d = a < b ? (a + b) : 2;//a<b为假,2是整个表达式的结果
    printf("%d\n", c);
    printf("%d\n", d);
    return 0;
}

十一、逗号表达式

exp1,exp2,....,expn

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

#include <stdio.h>
int main()
{
    int a = 2;
    int b = 0;
    int c = (b = b + a, a > 0, a = a * 3);
    int d = (b = b + a, a > 0);
    printf("%d %d %d %d",a, b, c, d);//会输出什么呢?
}

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

下标引用操作符([]):

它的操作数是一个数组名+一个索引值

int arr[10];
arr[9] = 10;
//[]的操作数是arr和9

函数调用操作符(()):

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

结构成员访问操作符:

.        ->

 结构体成员可以通过操作符"."直接访问,使用方式为结构体变量.成员。

#include <stdio.h>
struct stu
{
	int age;
	int sex;
};
int main()
{
	struct stu s;
	s.age = 10;
	s.sex = 1;
}
结构体成员也可通过->间接访问,使用方式为正向结构体变量的指针变量->成员名。
#include <stdio.h>
struct stu
{
	int age;
	int sex;
};
int main()
{
	struct stu s;
	s.age = 10;
	s.sex = 1;
	struct stu* p = &s;
	p->age = 20;
	printf("%d", p->age);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值