5.单目操作符
! 逻辑反操作- - 负值 + 正 & 取地址 sizeof 操作数的类型长度(以字节为单位) ~ 对一个数的二进制按位取反 -- 前置、后置-- ++ 前置、后置++ * 间接访问操作符(解引用操作符) (类型) 强制类型转换
1.逻辑反操作符
int flag=1; if(!flag)// 等同于 if(0) printf("hehe"); 这里会输出 呵呵 嘛?答案是不会的 !为逻辑反操作符,意味真的变成假的,假的变成真的。
需要注意的是,这里的真假并非直接指得是对错问题,计算机中真用非0数值表示,假则用0表示
2.正、负值
对于正负值需要注意一个点
#include<stdio.h> int main() { int a = 5; printf("%p\n", &a);//取出a这个值存储再内存中的地址 }
3.取地址
#include<stdio.h> int main() { int a = 5; printf("%p\n", &a);//取出a这个值存储再内存中的地址 }
注意,每一次去取这个地址的时候都不一样,因为每次编译器给这个数开辟空间存储时都是随机分配的地址
5.sizeof 操作数的类型长度(以字节为单位)
对于sizeof,想必大家再使用其求解数组中的个数经常使用到,不会太陌生,那我们就来探讨一下它的其它特点。
我们都知道sizeof可以求一个数组、常变量、类型等再内存中所占空间大小
sizeof也有许多形式,例如上图。
注意的时,你是不是也发现最有一个sizeof求解编译器报错了?这种写法需要注意,是不支持的。
其次,我们发现第一种写法和第三种写法居然是结果一样的,对于sizeof来说,你是不是认为它是一个函数呢? 类似 strlen 这个库函数一样,但从上面的结果来看 不是的
由于函数例如 strlen() 这个括号便是函数的操作符,是不能省略的,而siziof 却可以省略,因此足以说明sizeof是一个操作符,并非一个函数
最后,我们再来说一说sizeof计算内存大小。
sizeof计算规则:返回所有的数所占内存包括 \0
#include<stdio.h> #include<string.h> int main() { int a[]={1,2,3,4,5}; printf("%d\n", sizeof(a));// 20 int 类型占四个字节,一共有5个元素,因此在内存中占20 byte char b[] = { 1,2,3,4, 5}; printf("%d\n", sizeof(b));// 5 char 类型占一个字节,一共五个元素,因此在内存中占 5 byte int c[]="abcd"; printf("%d\n",sizeof(c)); // 5 abcd \0,这个字符串后面有一个\0,用sizeof计算内存大小,\0也会被计算进去 }
特别需要注意的是:
对于sizeof计算类型来说 无论是X86(32位) 还是X86(65位)下
结果是一样的,每个类型所占的空间大小是规定好的
#include<stdio.h> int main() { printf("%zu\n", sizeof(int)); // 4 printf("%zu\n", sizeof(char)); // 1 printf("%zu\n", sizeof(double));// 8 printf("%zu\n", sizeof(float));// 4 return 0; }
但是 对于指针类型来说,由于指针变量的实质就是地址,在不同位的平台下不一样:
X86(32位)下:
X64(64位)下:
在这,相必有一些分不清sizeof和strlen的朋友,在此也说一说strlen
strlen的计算规则:读到 \0 则停止,返回读到\0 之前的字符长度
#include<stdio.h> #include<string.h> int main() { char a[] = "abcd"; 对于字符串,它的结尾有一个 \0 printf("%d\n", strlen(a)); // 因此结果为 4 char b[] = { 1,2,3,4,5 }; 对于这个数组而言,当读到 5 过后,编译器并不知道这个数组里 \0 再何处 只能一直往下读取,直到\0出现才返回之前的字符长度,因此是随机值 printf("%d\n", strlen(b)); // 随机值 char c[] = { 1,2,3,'\0',4,5 }; 原本这个数组的字符串长度是一个随机值,但是由于我们手动往里填加了\0, 因此,再读到第三位过后就遇到了\0,返回之前的三个数的字符长度,结果为3 printf("%d\n", strlen(c)); // 3 }
6. - - 的前置和后置 以及 ++ 的前置和后置
对于前置++或者前置 - - 而言:先对数进行自增(自减),然后对使用该数,也就是表达式的值是该数自增(自减)之后的值。
#include <stdio.h> int main() { int a = 10; int x = ++a; //先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。 int y = --a; //先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10; return 0; } 上述代码如果理解不清楚,可以按如下理解 int a=10; //int x=++a; 可以写成 a=a+1;此时 a=11; int x=a; //x=11 同理: //int =y=--a;可以写成 a=a-1;此时a使用的是上面++a后a的值 -- a=10; int y=a;// y=10
对于后置++或者 - - 而言:先对该数使用,在对该数进行自增(自减)
int a=10; int x=a++; //先使用a,然后再自增。先使用a时,x=10;之后自增,a=11; int y=a--; //先使用a,然后自增。先使用a时,由于上面++a后的影响,现在a=11; //先使用a,则y=11;在自减,a=10; 若对上述代码理解不清楚,可以按如下理解 int a=10; //int x=a++; 可以写成 int x=a; //x=10; a=a+1; //a=11; 同理: //int y=a--; int y=a;//由于上述a++的影响,此时a已经变成了11 //先使用a,则y=11; a=a-1; //a=10;
7. * 间接访问操作符(解引用操作符)
对于 * 这个操作符,我们常见于指针当众,因此我们可以大抵将它和指针变量认为是一对
#include<stdio.h> int main() { int a = 10; int* p = &a;//p当中存储的是a的地址 p = 0;//由于p中存的是a的地址,改变P的值,也就间接的改变了a地址里的值 printf("%d", a); return 0; }
又或者在数组传参当中:
void print(int* arr) { printf("%d", *(arr+1)); //arr相当于首元素地址,+1 向后跳过一个整形找到第二个元素 //等同于arr[1],所以打印的结果应该是2 } #include<stdio.h> int main() { int arr[5] = { 1,2,3,4,5 }; print(arr); //数组传参传的是首元素地址 return 0; }
8. (类型) 强制类型转换
对于强制类型转换,可以将一些你不需要的类型强制转换为你需要的类型,一般使用在一些表达式的返回值可能不是我们需要的时候,将其强制类型转换。
例如:
我们常使用的随机值
#include<stdio.h> #include<stdlib.h> #include<time.h> int main() { srand((unsigned int) time(NULL)); //随机数的起点设置我们用时间戳来表示--time函数,它的返回类型是time_t //因此我们需要将它强制类型转换成和X的类型一致。 int x = rand();//使用随机数时,需要设置一个随机数的生成起点 printf("%d", x); return 0; }
4.关系操作符
=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
对于上述关系操作符中,需要注意的是 判断是否相等的操作符 == ,在使用
时需要注意不要和赋值号写混
5.逻辑操作符
&& 逻辑与 || 逻辑或
对于逻辑与:两个条件都是真结果就是真,期中一个为假那么结果就是假
选自360笔试题:
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; (表达式)&&(表达式)这是&&的使用形式,因此我们在这不需要考虑 前置++和后置++的优先级比&&高的问题。他们都是表达式的情况下,优先级 相同,此时只需要按照&&的结合方式从左往右结合。 i = a++ && ++b && d++; 根据上面介绍的前置后置++的用法 a先使用在++,a=0 我们知道,对于表达式结果为0,则是一个假命题,&&只要 有一个假则结果为假,那么a++&&++b =0 同理 0&&d++=0 ,因此++b 和d++ 表达式都没有执行 此时 a=1 b=2 c=3 d=4 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
从上我们可以得到结论:对于 && 而言,如果表达式一边为假,另一边则不执行
对于逻辑或:一个条件为真,则结果为真,两个条件都是假则结果为假
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; 同样,||和&&一样,是表达式之间的使用形式,在这也不用考虑优先级 i = a++||++b||d++; a先使用,后++,a=0,对于||,两边为假结果才为假,还需要看表达式++b; b先++ 后使用,b=3;则a++||++b结果为真 那么,a++||++b||d++,前面为真,无论d++ 是否为真结果一定是真,因此d++不执行 此时,a=1 b=3,c=3,d=4 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
从上我们可以得到结论:对于 || 而言,如果表达式一边为真,则结果一定为真,另一边的表达式则不执行
6.条件操作符
表达式1 ? 表达式2 : 表达式3
可以理解为表达式1成立吗?成立执行表达式2,不成立执行表达式3.。
由于他有三个操作数,它也是一个三木操作符。
例如下面这个代码:
#include<stidio.h> int main() { int a=1; if(a>2) a=1; else a=-1; return 0; } 更改为条件表达式则为 #include<stdio.h> int main() { int a=1; (a>2)?(a=1):(a=-1); return 0; }
例如我们比较两个数的大小:
#include<stdio.h> int main() { int a=1; int b=2; int max=(a>b)?a:b; //通过一个条件操作符,我们很容易就能得出两个数的大小关系 printf("%d",max); return 0; }
7.逗号表达式
表达式1, 表达式2, 表达式3, …表达式N
运算规则:逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果
int a = 1; int b = 2; if (a =b + 1, c=a / 2, d > 0) 可以看到if()里面是一个逗号表达式,最终结果为最有一个表达式结果 等同于if(d>0)
需要注意的是,虽然逗号表达式最终结果为最后一个表达式的结果,但是前面的表达式也有可能影响最后表达式的结果
例如:
int a = 1; int b = 2; int c = (a>b, a=b+10, a, b=a+1);//逗号表达式 括号里面是逗号表达式,但是c并不是直接为b=a+1=2; 逗号表达式从左往右依次执行中,由于前面的表达式中的值影响了最后a的值, 因此最后结果并不直接为2,a=b+10=12, b=a+1=12+1=13; 最终结果为13
8.下标引用
[ ] 下标引用操作符
#include<stdio.h> int main() { int arr[5]={1,2,3,4,5}; printf("%d",arr[4]); 这里的arr[4]就是下标引用操作符 对于[]这个操作符,arr是它操作的数组名,4是它的索引值 return 0; }
9.函数调用操作符
() 函数调用符
可以有一个或多个操作数
int Add(int x, int y) { return x + y; } #include<stdio.h> int main() { int a = 1; int b = 2; int c = Add(a, b); 对于Add这个函数来说,函数调用操作符(),它的操作数首先是函数名Add 其次是里面的传给函数的参数,a和b 对于操作的参数可以是多个 printf("%d", c); }
10.访问一个结构的成员
. 结构体.成员名 -> 结构体指针->成员名
1. 结构体.成员名:
#include<stdio.h> #include<string.h> struct Book //定义一个书的结构体 { char name[20]; //这本书构成的内容包括书名 short price; // 价格 }; int main() { struct Book b1 = { "C语言程序设计",55 }; //结构体中 b1这本书规定它的书名和价格 b1.price = 55;//规定价格 strcpy(b1.name, "C语言程序设计");//规定书名 当我们b1 这本书已经构建好以后,我们该怎么获取这本书里的价格和书名信息呢? 通过 结构体.成员名的形式 b1 -- 结构体 prince和name 都是成员名 printf("价格:%d\n", b1.price); printf("书名:%s\n", b1.name); return 0; }
2.结构体指针->成员名
#include<stdio.h> #include<string.h> struct Book { char name[20]; short price; }; int main() { struct Book b1 = { "C语言程序设计",55 }; b1.price = 55; strcpy(b1.name, "C语言程序设计"); struct Book* pb = &b1;//获取结构体中 b1这本书的地址 printf("书名:%s\n", (*pb).name); // . 结构体变量.成员 printf("价格:%d\n", (*pb).price); 由于*(pb)存放了b1这个接头体的地址,因此*(pb)也可以认为是一个结构体变量,同理它也可以用上述方式访问里面的成员 printf("书名:%s\n", pb->name); // -> 结构体指针 ->成员 printf("价格:%d\n", pb->price); pb是一个指针,可以由指针-> 直接访问结构体成员 return 0; }
2.总结
以上便是我们基本的操作符,理解清楚操作符将会对我们写代码带来很多方便。