C语言 ---- 第二章 类型和指针

第一节 地址运算符、间接运算符、指针初识

  • & 地址运算符 由地址运算符所组成的表达式称为地址表达式
  • * 间接运算符 由间接运算符所组成的表达式称为间接表达式
  • 共同特点:
    • 一元运算符,只需要一个右操作数
    • 优先级相同,从右向左结合
int main(){
    int a = 100;
    &a;  //地址  --> 指针
    *&a;  // *地址  -->  指针所指向的值
    int b = 100, c = 100;
    b + c; //表达式的值为200,是一个"int"类型的整数
    &b;    //表达式的值为(int*)0x22fe44,是一个"指向int"类型的指针
    /*
    long int b = 100, c = 100;
    &b;  //一个指向“long int”类型的指针
    */
    return 0;
}
  • &a;: 地址表达式,变量a的内存地址为地址表达式的值,地址又被称作“指针”
  • *&a; :间接表达式,地址所指向的值(100)为间接表达式的值,地址所指向的值又被称为“指针所指向的值”
    • 间接表达式的运算顺序为从右向左,在此表达式里,地址运算符的优先级高于间接运算符

第二节 声明存储指针的变量–整数

  • 如何声明一个指针类型的变量?

    • 在声明的变量前加一个*
  • int main(){
        int a;  //变量a,用来存储"int类型"的整数
        int * b;  //变量b,声明用来存储"指向int类型"的指针
        b = &a;   //将指向"int类型"的指针赋值给b
        *b = 100;
        /*
        *b为间接运算符,优先级高于赋值运算符
        先将变量b进行左值转换,*b的值则为变量a的值
        */
        return 0;
    }
  • int * b; //变量b,用来存储"指向int类型"的指针
    b = &a; //将指向"int类型"的指针赋值给b,由于上面一行代码声明了变量b为指针类型,故此行代码才能正确运行

  • * : 根据地址对变量进行还原,*b = = a *b的值等于变量a的值

    • *b = 100;< == > a = 100;
    • 通过*运算符和变量b,间接影响变量a的值,所以将*运算符称为间接运算符
    • *&a : 先执行&a取出地址(指针),再执行*&a对取出的指针进行还原*&a < == > a

第三节 函数名-指针转换、声明存储指针的变量–函数、使用变量调用函数

void swap(int * a, int * b){
    int temp = * b;
    * b = * a;
    * a = temp;
}
int main(void){
    int m = 10086, n = 10010;
    swap(& m, & n);
    
    void (* pf)(int *, int *) = swap;
    pf(& m, & n);  //处理器在进行处理的时候,会先将函数调用表达式`pf()中存储的是一个指向swap的指针(地址)`进行一个左值转换,通过变量pf调用swap函数
    //(*pf)(& m, & n); (&*pf)(& m, & n); (*&*pf)(& m, & n); (&*&*pf)(& m, & n);
    return 0;
}
  • 在main函数中,函数名(swap)会被隐式的转换为函数的地址(“指向函数的指针”),处理器会根据地址定位到swap函数所在的位置并执行,swap (& m, & n) < == > (&swap)(& m, & n);
  • 声明pf变量,用于存储“指向函数swap”的指针,只要符合声明格式的函数,pf都可以进行存储

第四节 类型匹配、数值的类型、整型常量的:前缀、后缀、类型判定方法

int main()
{
    //“变量”在声明的时候,我们规定了它的类型
    int a;  //变量a为int类型
    //在给变量进行赋值的时候,必须赋值给它相对应类型的数值
    a = 1.5;  //类型不匹配,存储就会出现问题
    
    //“常量”也是有类型的
    //整数类型的常量  -->  简称整型常量
    
    //1.整型常量3种进制的写法(前缀)
    a = 125;     //十进制写法:以非0的数字开头
    a = 0125;    //八进制写法:以0开头
    a = 0x125;   //十六进制写法:以0x/0X开头
    
     
    a = 0b00001; //二进制写法:以0b/0B开头
    //注意”C语言并没有规定二进制的写法,这是编译器自行添加的
    //所以,更换编译器的时候,程序有可能会报错
    
    //2.整型常量的3个后缀:u(unsigned)、l(long )、ll(long long)(不分大小写)
    a = 125u;
    a = 125L;
    a = 125LL;
    a = 125ul;  //u和l:谁前睡后都可以
    a = 125ull; //u和ll:谁前谁后都可以
    
    //怎样判定常量是什么类型?
    //前缀 + 后缀 + 具体的C实现 这三个因素决定了一个常量的类型
    //具体判定方法:C语言提供了整型常量类型对照表,详见下表
    return 0;
}
  • 整型常量对照表
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J0sqbcTI-1578483847139)(E:\workspace\TyporaProjects\C笔记\C语言\images\第二章 类型和指针\4.png)]

第五节 整数类型转换为_Bool类型、隐式类型转换

int main(void){
    _Bool a, b, c, d;
    a = 0;             //int类型的0  ---->  _Bool类型的0
    b = 1;			   //int类型的1  ---->  _Bool类型的1
    c = 100;  		   //int类型的100 ----> _Bool类型的1
    d = 100000000000;  //long long int类型的100000000000 --> _Bool类型的1
    
    return 0;
}
  • 由于a,b,c,d都是int或long long int型的数据,与_Bool类型不一致,故需要进行类型转换。
    • 类型转换不需要人为进行参与,自动进行,所以也称为隐式类型转换,转换的结果为上述代码段中的注释
  • 将“整数类型”转换为“_Bool类型”时:
    • 如果数值在_Bool类型存储范围以内,只有类型会发生改变,数值将不会发生改变
    • 如果数值不在_Bool类型存储范围以内,类型和数值将全部发生改变,数值将被转换为1

第六节 整数类型转换为非_Bool整数类型、隐式类型转换

int main(void){
    short int a = 100;  //将int类型的100  -->  隐式转换为short int类型的100
    int b = 3700u;      //将unsigned int类型的3700  ---->  隐式转换为int类型的3700
    //由于a和b的类型不一致,需要进行隐式类型转换
    
    //新类型为无符号整数
    unsigned char c = -1;  //int类型的-1隐式转换为unsigned char类型的255
    //由于-1太小,不在新类型的存储范围之内,所以需要加上一个数(256)得到一个新值
    //256由来:新类型(char)的最大存储值为1个字节(255),进行加1 ----> -1 + 256 = 255
    
    
    unsigned char d = 257; //将int类型的257隐式转换为unsigned char类型的1
    //由于257太大,不在新类型的存储范围之内,所以需要减去一个数(256)得到一个新值
   //256由来:新类型(char)的最大存储值为1个字节(255),进行加1后得到一个新值,做减法运算  ---->  257 - 256 = 1
    
    //有符号整数  -->  不同的C实现会有不同的处理结果
    
    return 0;
}
  • 由于类型的不一致,需要进行隐式类型转换
  • 将“整数类型”转换为“非_Bool整数类型”时:
    • 如果数值在新类型的存储范围以内,只有类型会发生改变,数值将不会发生改变
    • 如果数值不在新类型的存储范围之内,类型和数值都将全部发生改变
    • 数值的改变分为两种情况:
      • 新类型是无符号整数、新类型是有符号整数

第七节 表达式值的类型

int max(int a, int b)
{
    if(a >= b)
        //关系表达式的类型  -->  int(固定)
        return a;
    	return b;
}
int main(void)
{
    //每个表达式都有一个值,这个值也是有类型的
    //"表达式值的类型",简称"表达式的类型"
    int x = 1, y = 1;
    unsigned char z;
    x += z = 56;
    //整型常量表达式:56  -->  int类型(判断方法见本章第三节)
    //赋值表达式:z = 56: 值 -> 56、类型 -> unsigned char
    //复合表达式的值: x += z = 56:值 -> 57、类型 -> int
    //赋值表达式  --> 值 -> 左操作数的值、类型 -> 左操作数的类型
    
    z += max(x, y);
    //函数调用表达式:max(x, y):值 -> 函数的返回值、类型 -> 函数的返回类型
    //复合赋值表达式: z += max(x, y):类型 -> unsigned char 
    
    y = ++ x;
    //前缀递增表达式: ++ x:类型 -> int -> 操作数的原型
    //赋值表达式: y = ++ x: 类型 -> int
    
    return 0;
}

第八节 隐式类型转换和运算符的关系、整型转换阶、整型提升

int main(void)
{
    //类型的转换是由云端夫来发起的
    //赋值运算符:将右操作数转换为和左操作数一样的类型
    singned char a = 1, b = 2;
    //递增运算符:不改变操作数的类型
    ++ a;
    
    /*
    其他的大多数运算符都需要将操作数转换为相同的类型,然后再进行运算
    具体的转换规则:以加性运算符为例
    */
    
    a + 380L;
    //第一步:整型提升 --> 将转换阶低于int或者unsigned int的类型转换为int或者unsigned int类型
    //第二步:整型提升后,如果操作数类型不一致  --> 将转换阶低的操作数转换为和转换阶高的操作数一样的类型
    //详细转换见整型的转换阶
    /*
    注意:
    	1.整型的提升是对第一步的描述,和第二步无关
    	2.整型提升以int类型为首选
    	3.当进行整型提升时,如果数值不在int类型的存储范围之内,那么就将它转换为unsigned int --> 当short int 和 int的存储范围相同时
    */
    
    a + b;
    //整型提升,只要在int或unsigned int类型以下的,都需要进行整型提升
    
    int c = -1;
    unsigned int d = 100;
    c + d;
    //整型提升后,两个类型在同一阶
    //将“有符号类型”转换为“无符号类型”
    
    return 0;
}
  • 整型的转换阶
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRxqZk7L-1578483847141)(E:\workspace\TyporaProjects\C笔记\C语言\images\第二章 类型和指针\8.jpg)]

第九节 负号运算符、负号表达式

int main(void)
{
    unsigned char a;  
    a = -1;  //1为int型,4个字节"0000 0000 0000 0000 0000 0000 0000 0001",在此处,a的值为255    
    // - : 负号运算符  -->  负号表达式  -->  在C语言中,负数是通过运算所得到的
    // 一元运算符,只需要一个右操作数
    
    //运算规则:
    // 1.如果操作数是一个整数,负号运算符会发起类型转换  --> 对操作数进行整型提升
    // 2.对操作数进行“取补码”操作,得到 "1111 1111 1111 1111 1111 1111 1111 1111"
    
    signed char b = - a ++;   //b的值为1
    
    return 0;
}

第十节 显式类型转换、转型运算符、转型表达式、()的多种用法

int main(void)
{
    //显式/强制类型转换
    //语法规则:(类型名)表达式
    //() --> 转型运算符 --> 将表达式的类型转换为括号中所指定的类型
    
    long long a;
    a = 33;  //处理器在执行运算时会先进行隐式的运算,将int型的33转换成long long int型的33并赋值给a
    a = (long long int)33;  //int --> long long int
    
    a = (long long)33 + 100;
    a = (long long)(33 + 100);  //(33 + 100)  --> 基本表达式
    //()运算符的三种用法
    //1.函数调用运算符
    //2.转型运算符
    //3.构成基本表达式
    
    return 0;
}

第十一节 整数-指针的转换、间接运算符的性质、再识指针

int main(void)
{
    //间接运算符  -->  根据指针(地址)对变量进行还原  -->  它的操作数必须是指针,并且它不会发起类型转换
    
    int a = 0;
    int b = (int) &a;//&a的值为变量a的地址,类型为指向int类型的指针;此行代码的作用是将变量a的地址存储到变量b中
    /*
    错误:间接运算符的操作数类型错误
        * b = 100;
    */
    *(int *)b = 100; //(int *)b先将b进行左值转换,此时b中存储的是a的地址,类型为指向int类型的指针
    //此行代码执行完以后,a的值变为100
    //*(int *)b < == > a
    
    return 0;
}

第十二节 研究指针三剑客、类型的本质

int main(void)
{
    int a = 0;  //假设:变量a的内存地址为123456
    
    &a;
    int b = (int)&a;
    
    return 0;
}
  • &a : 值为123456,类型为指向int类型的指针
  • b : 值为123456,类型为int整型
  • &a和变量b : 数值相同,类型不同
  • 分为不同类型的原因:
    • 不同类型,对应着不同的使用规则
  • * b ----> 编译器会报错
  • * &a ----> 将&a的值解析为地址,然后访问这个地址所对应的内存单元
  • C语言为什么要引入地址运算符&间接运算符*指针这几种类型?
    • 让它们互相配合,使操作者可以通过地址来访问内存
    • & : 获取一个数值,并将这个数值的类型规定为指针
    • * : 通过指针类型的数值,来访问内存单元
  • 汇编语言可以通过地址来任意访问内存 ----> 强大
  • 高级语言把内存访问的细节屏蔽掉 ----> 简单
  • C语言 ----> 既强大又简单

第十三节 指针-指针转换、复杂表达式分析

int abc(int a, int b)
{
    return 0;
}
int main(void)
{
    int a, (* b)(int, int) = abc;
    // 变量  ---->  存储数据
    //变量a  ---->  int整型
    //变量b  ---->  指针  ---->  指向函数  ---->  两个int类型的参数,一个int类型的返回值
    /*
    	abc  ---->  函数名  ---->  函数名-指针转换  ---->  值:函数abc的地址  ---->  类型  ---->  指针  ---->  指向函数  ---->  两个int类型的参数,一个int类型的返回值
    */
    //变量b中的值为多少?  ---->  函数abc在内存中的地址
    //变量b中的值是什么类型?  ---->  int (*) (int , int)
    
    void (* c)(void) = (void (*) (void)) b;
    //变量  ---->  存储数据
    //变量c  ---->  指针  ---->  指向函数  ---->  没有返回值,没有参数
    //b  ---->  变量  ---->  左值转换  ---->  值:函数abc的地址、类型:int (*) (int, int)
    //变量c中的值是多少?  ---->  函数abc在内存中的地址
    //变量c中的值是什么类型?  ---->  void (*) (void)
    a = ((int (*)(int , int )) c)(1,2);//函数调用表达式,调用函数abc,并传入值1和2
    //((int (*)(int , int )) c)最外层()是为了让基本表达式作为一个整体(转型运算符)参与运算
    //基本表达式:(int (*)(int , int )) c
    //处理器会将c的值进行左值转换,将c的原类型void (*) (void)强制转换为int (*)(int , int )
    return 0;
}

第十四节 从内存的角度搞定 – 多级指针

int main(void)
{
    int i, * pi = &i, * * ppi = & pi;
    * * ppi = 1;
    * ppi = 1;
    //--------------------------------------------
    int j;
    pi = & j;
     ** ppi = 3;
    
    return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值