一次带你理清 ‘ || ’ 和 ‘ && ’ 和 ‘ ^ ’ 等常用操作符以及其余基本操作符(下)

简介: 一次带你理清 ‘ || ’ 和 ‘ && ’ 和 ‘ ^ ’ 等常用操作符以及其余基本操作符(下)


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这个值存储再内存中的地址
}


注意,每一次去取这个地址的时候都不一样,因为每次编译器给这个数开辟空间存储时都是随机分配的地址

c3e6eb40521f467c988081ed18745c46.png


5.sizeof 操作数的类型长度(以字节为单位)


对于sizeof,想必大家再使用其求解数组中的个数经常使用到,不会太陌生,那我们就来探讨一下它的其它特点。

1f9c365c075b42c7b0e08aea0868efba.png


我们都知道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位)下:

64335255fbf44be0a01f85aa3e7d8a46.png


X64(64位)下:

b8f66210b6b94e91b2c4cb08a46c69ab.png


在这,相必有一些分不清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;
}


6ab0d945f9614f0781af865300add120.png


又或者在数组传参当中:

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;
}

e41b680ad108411f828742ddabe0a7d4.png


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;
}


e157af68ce5f412a8e99294f9ede4e11.png


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;
}


641d55f10be84f7cb1684af8cbda2bd4.png


2.总结



以上便是我们基本的操作符,理解清楚操作符将会对我们写代码带来很多方便。


9632005b56dc447fbb2e252f4ae52b5c.png


相关文章
|
4月前
【C language】判断一个正整数是否是2^n
【C language】判断一个正整数是否是2^n
|
2月前
a+=b 和 a=a+b 真的完全等价吗?
a+=b 和 a=a+b 真的完全等价吗?
29 0
|
5月前
|
语音技术
语音识别,print(“Hello“,end=‘‘)不换行的写法,\t代表着tab,print(“H\tW“),for循环的基础语法,把字符串遍历的写法,数itheima is有几个a
语音识别,print(“Hello“,end=‘‘)不换行的写法,\t代表着tab,print(“H\tW“),for循环的基础语法,把字符串遍历的写法,数itheima is有几个a
|
7月前
|
C语言
C语言(9)----NULL、null(或者NUL)、\0、0、‘0’几者之间的区别
C语言(9)----NULL、null(或者NUL)、\0、0、‘0’几者之间的区别
122 0
|
Python
python str = ‘2023/11/9 0:00:00’ 转变成‘2023-11-09’
python str = ‘2023/11/9 0:00:00’ 转变成‘2023-11-09’
54 0
|
存储 编译器
一次带你理清 ‘ || ’ 和 ‘ && ’ 和 ‘ ^ ’ 等常用操作符以及其余基本操作符(上)
一次带你理清 ‘ || ’ 和 ‘ && ’ 和 ‘ ^ ’ 等常用操作符以及其余基本操作符(上)
311 0
|
C++
‘this’不能用于常量表达式错误(C++)【问题解决】
‘this’不能用于常量表达式错误(C++)【问题解决】
292 0
‘this’不能用于常量表达式错误(C++)【问题解决】
|
人工智能 测试技术
PAT (Basic Level) Practice (中文) B1008 数组元素循环右移问题 (20 分)
PAT (Basic Level) Practice (中文) B1008 数组元素循环右移问题 (20 分)
104 0
PAT (Basic Level) Practice (中文) B1008 数组元素循环右移问题 (20 分)
|
人工智能 BI
CF1398C. Good Subarrays(思维 前缀和)
CF1398C. Good Subarrays(思维 前缀和)
105 0
|
C++
201312-5 I’m stuck!
201312-5 I’m stuck!
82 0
201312-5 I’m stuck!