C语言–CH06–操作符(下)
四、赋值操作符
1、 赋值和初始化的区别
赋值和初始化有显著的区别
int a = 10; //这是初始化
a = 20; //这是赋值
2、连续赋值
赋值是一种从左往右的运算,并且可以连续赋值:
int a = 0;
int b = 10;
int c = 20;
a = b = c+1;
printf("%d\n",a);
猜一猜a此时的输出为几呢?
推演:
因为赋值运算符是从左往右计算的,所以,首先计算c+1,并把c+1的值赋给b
,之后再把b的值赋给a。即把21的值赋给b,再赋给a。
*赋值运算的连续计算关键记住从左往右的原则
故我们推测输出结果为21。实际运行一下输出为:
21
警告⚠:我们不建议这样写代码,这样写不利于调试,并且可读性差。
3、复合赋值符
+= %=
-= &=
*= |=
/= >>=
<<= ^=
相信大家都对这些符号很熟悉了,本文只以+=为例,其他的符号类比
a = a + 10;
a += 10;//两者等价
两者的含义一样,下面更简短。
五、单目操作符
单目操作符即只有一个操作数的操作符,双目操作符有两个操作数
日常所用单目操作符有:
! //逻辑反
- //负值
+ //正值
& //取地址
sizeof //算操作数的类型长度
~ //对一个数的二进制按位取反
++ //++a,a++
-- //--a,a--
* //解引用操作符
(类型) //强制类型转换
1、!
早期C语言没有bool类型,可以用!来表示真假。我们已经知道,0为假,
非零为真。我们可以写这样一行代码
#include<stdio.h>
int main()
{
int a = 3;//a不为0,即a为真
if(a)
{
printf("你是个好人\n");
}
if(!a)
{
printf("你不是个好人\n");
}
return 0;
}
这段代码是什么意思呢?一旦a非零,则打印你是个好人,如果a为0,
则a为假,则!a为真,进入第二个if,打印你不是个好人,一次达到bool
类型相同的作用。
2、+ -
这两个符号没什么实际意义。但是还是要讲一下。
int a = -10;
int b = +a;
请问:b = 10还是b = -10?
答案是:-10。
+a并不能改变a的值。
3、取地址操作符&
&是取地址操作符,他的作用就是取出一个变量在内存中的地址。
一般是用一个指针变量来储存地址。
int a = 10;
int* p = &a;
printf("%p",p);
printf("%p",&a);//%p是打印地址时使用
注意:a是一个整型变量,占四个字节,&a取的是第一个字节的地址。
之后的指针环节会详细介绍。
4、sizeof
sizeof有两种用法:(单位是字节Byte)
int a = 10;
int c = sizeof(a);
int n = sizeof(int);
printf("%d \n %d",c,a);
1、计算一个变量所占内存的大小:sizeof(a)
2、计算类型所创建变量所占内存大小:sizeof(int)
3、计算数组的大小
sizeof(数组名)计算的是整个数组的大小
#include<stdio.h>
int main()
{
int arr[5] = {0};
printf("%d\n",sizeof(arr));
return 0;
}
输出:
20
整型数组,共5个元素,共20个字节。
sizeof与strlen()有区别:
sizeof不是函数,而是一个单目操作符。能适用于各种类型。
strlen()是函数,只能用来求字符串的长度。
5、二进制按位取反 ~
什么是按位取反?
按位取反即按二进制位取反,是一个整数的补码某一位上为0则变成1,为1则变成0。
如:3的补码为:
00000000000000000000000000000011
按位取反之后为:
11111111111111111111111111111100
因为取反之后也为补码,但是打印出来的是原码,所以类似上一章所讲的:
反码:
11111111111111111111111111111011
原码:
10000000000000000000000000000100
整数:
-4
即~3 = -4
我们来验证一下:
int a = 3;
int c = ~a;
printf("%d\n",c);
输出:
-4
大招:如何把一个二进制的随便一位由0变成1,再把它变回来呢?
我们需要四个武器:按位或|、按位与&、按位取反~、对于数字1的运用。
a = 13
a的补码为:
00000000000000000000000000001101
比如我想要把a的补码中倒数第五位变成1:
00000000000000000000000000011101
怎么做呢?
我们邀请嘉宾————— 1
1的二进制补码为:
00000000000000000000000000000001
如果1<<4:
00000000000000000000000000010000
再将a按位或(1<<4):
00000000000000000000000000011101
实现了我们想要的操作。此时整数输出为:29
那我们想要变回去怎么办呢?还记得(1<<4)等于多少吗?
00000000000000000000000000010000
如果对(1<<4)按位取反~(1<<4):得到:
11111111111111111111111111101111
再使29按位与~(1<<4):
00000000000000000000000000001101
又回到了13。
我们来验证一下:
#include<stdio.h>
int main()
{
int a = 13;
a |= (1<<4);
int b = a;
a &= (~(1<<4));
int c = a;
printf("%d\n%d",b,c);
return 0;
}
输出:
29
13
6、++c和c++
直接看例子
int a = 12;
int b = ++a;
printf("%d\n%d",a,b);
输出:
13
13
int a = 12;
int b = a++;
printf("%d\n%d",a,b);
输出
12
13
++a叫做前置++,他的特点是先加后使用。
这里的意思是,先对a进行操作,a+1 = 12 + 1 = 13。
把13这个值给a,再把a的值给b。
a = a + 1
b = a
a++叫做后置++,他的特点是先使用,再加。
这里的意思是,先把a的值给b,即b= 12,再执行b++,即b+1。所以b = 13
b = a
a = a + 1
另一比较难得例子:
#include<stdio.h>
void test(int n)
{
int b = n+1;
printf("%d\n",b);
}
int main()
{
int a = 13;
int c = 13;
test(a++);
test(++c);
return 0;
}
输出:
14
15
因为test(a++)传的参是a,不是a++。test(++c)传的是c = c + 1。
7、解引用操作符 *
首先看一段代码:
int a = 10;
int* p = &a;
*p = 20;
printf("%d\n",a);
输出:
20
什么意思呢?这里的p是一个指针变量。他相当于a的地址。
而*p相当于访问a地址里的值,即a的值。
*p = 20;就相当于一个指针p,指向了一个房子,*相当于房子的钥匙
*p操作相当于“找到一个房子,并且用钥匙打开门”这一个动作。这个
时候就可以随意更改房子里的东西 。
之后的指针会更详细地讲解。
8、强制类型转换
小学三年级以上都知道3.14是个浮点数。但是一个小学二年级学生写了这样一行抽象的代码:
int a = 3.14;
整型变量怎么能存储浮点数呢?
这个时候,热心大哥哥XXXGOASTDIE决定帮助这个小学二年级学生:我帮他多打了两个符号:
int a = (int)3.14;
这样就变成了一个合法的代码。这种操作叫强制类型转换。即系统默认的double类型3.14被我强制转化为了3.14。这就是强制类型转换。
我们之前学到过生成随机数地函数:
#include<stilib.h>
#include<time.h>
srand((unsigned int)time(NULL));
就用到过强制类型转换。
六、关系操作符
关系操作符很简单,没什么可讲的
>
<
>=
<=
==
!=
只需要注意:
1、==和=别搞混淆了!!!
2、不是什么都可以比较
"abc"== "abcde";
这里比较的只是首元素的地址。
两个字符串是否相等应该用string.h库里的strcmp()来比较。
3、老手都应该注意到if(i==1) if(1 == i)的区别,该链接里有讲到:
if(i==1)和if(1==i)的区别
七、逻辑运算符
&& 逻辑与
|| 逻辑或
逻辑与逻辑或和按位与按位或有什么区别?按位与按位或是在二进制层面应用。而逻辑与逻辑或只关注逻辑上的真假。
int a = 5;
int b = 8;
printf("%d\n",a&&b);
输出:
1
因为真值与真值就是真值。
1、法则
a&&b:a和b都为真才真,a和b只要有一个为假就为假。
a||b:a和b只要有一个为真就为真,两个都为假才为假。
2、e.g.判断闰年
闰年如何判断?
能被4整除,且不能被100整除。
或者能被400整除。
#include<stdio.h>
void is_leap_year(int year)
{
if((year%4 == 0)&&(year%100 != 0)||(year%400 == 0))
{
printf("这是闰年.\n");
}
else{
printf("这不是闰年\n");
}
}
int main()
{
int n = 0;
printf("请输入年份:\n");
scanf("%d",&n);
is_leap_year(n);
return 0;
}
这段代码就应用到了逻辑与和逻辑或。
3、360的一道面试题:
#include <stdio.h>
int main()
{
int i = 0,a = 0,b = 2,c = 3,d = 4;
i = a++ && ++b && d++;
printf("%d %d %d %d %d",i,a,b,c,d);
return 0;
}
请问输出为多少?
0 1 2 3 4
为什么呢?看以下解释:
a++是先用a,再++,所以a刚开始等于0,给i的值也是0,0为假,所以0&&任何值都为假,所以
之后的全为假,都不会执行。所以b、c、d的值都不会变。所以输出0 1 2 3 4。
变式:
第一问:a = 1,结果是什么?
第二问:a = 0,i = a++ || ++b || d++,结果是什么?
解析:
第一问:
a = 1,则把1传给了i,并且左边为真,即要执行++b,b变成了3,为真值,即a++&++b为真
值,故需要执行d++,即d = 5;真值&真值&真值,所以i最终为1。所以输出为:1 2 3 3 5
第二问:
首先a把0给i,a为假,a再加一,a为假,则执行++b,++b为真,假||真 = 真,则不要执行d++,
故d = 4,总体来看,假||真||真 = 真,故i = 1.所以输出为:
1 1 3 3 4
4、条件操作符(三目操作符)
表达式1 ? 表达式2 :表达式3
真 ✔ ×
假 × ✔
只要表达式1为真,则执行表达式2,为假则执行表达式3。
e.g.使用条件表达式求两个数的最大值
int max = 0;
int a = 3;
int b= 4;
max = (a>b ? a : b);
八、逗号表达式
exp1,exp2,exp3....expn;
逗号表达式从左向右依次进行,整个表达式结果为最右边表达式的结果。
int a = 2;
int b = 3;
int c = 4;
int d = (a>b,a = b+10,b = a + 1);
请问d等于多少?
a>b没有改变a,b的值,b+10 = 12,给了a,b= a + 1 = 12 +1 = 14。最后一个表达式
的值为14,给了d,所以d = 14
九、下标引用,函数调用和结构成员操作符
1、下标引用操作符[]
就是访问数组下标的操作符。
int arr[10] = {0};
arr[7] = 8;//这里就是下标引用操作符。
[]的操作数,一个是arr,一个是7。为了证明[]是个操作符,我们有如下操作。
我们都知道加法有交换律,那么[]的两个操作数能交换吗?答案是可以。可以自行验证。
7[arr] = 9;//也能达到跟上面代码一样的效果
证明:arr为首元素地址。arr + 7即为第8个元素的地址。所以*(arr +7)就是第8个元素。
所以:
*(arr+7) = arr[7];
*(arr+7) = *(7+arr) = 7[arr];
2、函数调用操作符()
函数调用操作符不可以省略。放在函数后面,表示调用函数
#include <stdio.h>
void Add(int a,int b)
{
return a + b;
}
int main()
{
int a = 10;
int b = 20;
Add(a,b);
return 0;
}
利用函数调用操作符不可以省略的性质,我们也可以知道sizeof不是一个函数。
因为sizeof(a)和sizeof a都是合法的,即sizeof可以省略扩号。
函数调用操作符有1+n个操作数:函数名(1个),内部参数(n个),n = 0合法。
如这里的Add(a,b);有操作数:
Add a b
3、结构成员操作符
首先我们定义一个结构体,以学生类型为例:
#include<stdio.h>
#include<string.h>
struct Stu
{
char name;
int age;
double score;
};
包括姓名,年龄,学号。
声明一个Stu类型的变量s,初始化为{0}:
int main()
{
struct Stu s = {0};
}
我想要设置两个函数,一个放s的值,一个打印s,形参为struct Stu n
void set_value(struct Stu n)
{
strcpy(n.name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
n.age = 20
n.score = 2022105022.0;
}
void print_value(struct Stu n)
{
printf("%s %d %lf\n",n.name,n.age,n.score);
}
之后我需要调用这两个函数,写在main()里。这里我汇总一下:
#include<stdio.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
double score;
};
void set_value(struct Stu n)
{
strcpy(n.name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
n.age = 20;
n.score = 2022105022.0;
}
void print_value(struct Stu n)
{
printf("%s %d %lf\n",n.name,n.age,n.score);
}
int main()
{
struct Stu s = {0};
set_value(s);
print_value(s);
return 0;
}
输出:
0 0.000000
输出的跟我们想象的不一样,是传参的问题:注意:形参是实参的一次拷贝。我们
定义了一个叫s的Stu类型的变量,开辟了一个空间,创建了实参n,拷贝了形参s,
并在拷贝的区域储存了值,之后返回的值并不是n的而是,s的,此时s的内存里还是
原来的值,所以打印出的都是0。所以我们要取s的地址传进去而不是把s传进去:
set_value(&s);
取地址就需要用指针变量接收,所以我们要把n改成指针变狼,并对set_value内部更改:
void set_value(struct Stu* n)
{
strcpy((*n).name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
(*n).age = 20;
(*n).score = 2022105022.0;
}
这样才是正确的。但是这样会不会麻烦了一点,有没有更简单的方法呢?
有:用结构体成员操作符:->
void set_value(struct Stu* n)
{
strcpy(n -> name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
n -> age = 20;
n -> score = 2022105022.0;
}
这是个更简单的方法。总结在一起:
#include<stdio.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
double score;
};
void set_value(struct Stu* n)
{
strcpy(n -> name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
n -> age = 20;
n -> score = 2022105022.0;
}
void print_value(struct Stu n)
{
printf("%s %d %lf\n",n.name,n.age,n.score);
}
int main()
{
struct Stu s = {0};
set_value(&s);
print_value(s);
return 0;
}
输出:
张三 20 2022105022.000000
(*p).age 跟p->age是一样的
当然,我们这里打印的还是n内存里的数据,我们想要直接打印s的数据,可以对print_value做同样的操作:
#include<stdio.h>
#include<string.h>
struct Stu
{
char name[20];
int age;
double score;
};
void set_value(struct Stu* n)
{
strcpy(n -> name,"张三");//不能直接n.name = "张三" ,
//因为name是一个地址,不能把字符串直接放进地址
n -> age = 20;
n -> score = 2022105022.0;
}
void print_value(struct Stu* n)
{
printf("%s %d %lf\n",n->name,n->age,n->score);
}
int main()
{
struct Stu s = {0};
set_value(&s);
print_value(&s);
return 0;
}
标签:name,Stu,--,CH06,++,C语言,int,操作符,printf
From: https://blog.csdn.net/2401_83411974/article/details/139361047