C语言 day7 | 我的日常分享

C语言 day7

指针

指针变量的强制类型转换

案例:

1
2
3
4
5
#include<stdio.h>
int main(int argc,char *argv[]){
int num = 0x01020304;
return 0;
}

需求:取出如图 橙色框框内的 0x0203
图片

  1. 如果跨度与宽度不等时,需要用到类型转换
  2. 观察需求的跨度与宽度
  3. 选择min(跨度,宽度) 定义指针变量
  • 方法一:

    1
    2
    3
    4
    5
    6
    7
    8
    #include<stdio.h>
    int main(int argc,char *argv[]){
    int num = 0x01020304;
    char *p;
    p = &num;
    printf("%#x",*(short *)(p + 1));
    return 0;
    }
    运行结果:
    图片
    分析:定义的 char *p ,指向的变量类型为 char 跨度为1字节,宽度为1字节,p+1 跨1个字节,(short *)(p + 1) 跨一个字节后强制类型转换将指针变量 p 指向变量类型转换成了 short 宽度为2字节,所以取出2个字节。
  • 方法二:

    不取小的定义指针变量,我偏取大的定义指针变量,复杂些
1
2
3
4
5
6
7
8
#include<stdio.h>
int main(int argc,char *argv[]){
int num = 0x01020304;
short *p;
p = &num;
printf("%#x",*(short *)(((char *)(p) + 1)));
return 0;
}

运行结果:
图片
分析:定义的 short *p ,跨度、宽度都为2字节,先强制类型转换 (char *)(p) 将指针变量指向类型转换为 char 此时的跨度为1字节,然后((char *)(p) + 1)加一,跨一个字节,(short *)再强制类型转换将指针p指向short,此时的宽度为2字节,取空间内容即可得到0x0203

  • 方法三:

    定义两个指针变量,一个用来当跨度,一个用来当宽度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include<stdio.h>
    int main(int argc,char *argv[]){
    int num = 0x01020304;
    short *p1;
    char *p2;
    p2 = &num;
    p1 = p2 + 1;
    printf("%#x",*p1);
    return 0;
    }
    运行结果:
    图片
    分析:定义了两个指针变量,一个用来当跨度,一个用来当宽度,不好描述,自己领悟好了

指针变量的初始化

1. 如果局部指针变量,不初始化,保存的是随机的地址编号(千万不要操作哦)

1
2
3
4
5
6
7
8
#include<stdio.h>
int main(int argc,char *argv[]){
int *p;
printf("---------111---------");
printf("%d",*p);
printf("---------222---------");
return 0;
}

运行结果:
图片
printf("---------222---------");这个语句并没有执行(段错误),非法内存无法访问

2. 不想让指针变量指向任何地方(也千万不要操作哦)

1
2
3
4
5
6
7
8
9
#include<stdio.h>
int main(int argc,char *argv[]){
// #define NULL ((void *)0)
int *p=NULL;
printf("---------111---------");
printf("%d",*p);
printf("---------222---------");
return 0;
}

运行结果:
图片
一样的会出现段错误

3. 将指针变量初始化为合法地址(可以操作)

1
2
3
4
5
6
7
#include<stdio.h>
int main(int argc,char *argv[]){
int num = 10
int *p = &num;
printf("%d",*p);
return 0;
}

总结:

1. 指针变量初始化为 NULL

1
int *p = NULL;//不要对p进行*p操作,容易出段错误

2. 指针变量初始化为 合法地址空间

1
2
int num = 10;
int *p = &num;//可以操作

& 取地址符 和 * 指针解引用符 的区别(使用中)

引入:

1
2
3
4
#include<stdio.h>
int main(int argc,char *argv[]){
int num = 10;
}

上面代码中的 numint 类型,这个是肯定的,那么请问 &num 是什么类型?… 是 int * 类型。
总结:如果对一个变量取地址 整个表达式类型 就是变量的类型加 *

  • 问1:

    1
    2
    int *p;
    &p是什么类型? 是int类型
  • 问2:

    1
    2
    int ****p;
    &p是什么类型 是int ***类型
  • 问3:

    1
    2
    3
    4
    5
    int num = 10;
    int *p;
    p = &num;
    p是什么类型 是int *类型
    所以p = &num;这个赋值语句左右两边类型是相同的,是严谨的赋值语句
  • 问3:

    1
    2
    3
    4
    5
    int num = 10;
    int *p;
    p = &num;
    在使用中,*p是是什么类型?
    int,*p取得是对应地址的空间的内容

    总结:如果对指针变量取 * 整个表达式的类型是 指针变量的类型减 *

总结:如果 & 和 * 同时存在可以相互抵消(从右至左抵消)。(重要)

1
2
&*&*&num == &num;
&*&**&p == p;

指针的注意事项

1.void 不能定义变量

1
void num;//错误的 系统不知道num的内存大小,无法分配内存空间

2.void * 可以定义变量

1
2
void *p;//p的类型为void *,而指针类型在32为平台都是4字节,系统知道给p开辟4字节的空间
//能不能定义成功关键是看系统知不知道要给你分配多大的内存空间
  • p 叫万能指针 p 可以保存 任意一级指针 ,如果是 void **p 就可以保存 任意二级指针···

  • 对于 p 不能直接 *p 因为他指向的类型为void,就没办法确定指针的宽度和跨度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include<stdio.h>
    int main(int argc, char* argv[]) {
    void* p;
    int num = 10;
    p = &num;
    // printf("%d\n",*p);
    printf("%#x\n", p);
    //printf("%#x\n", p + 1);

    return 0;
    }

    所以第六行代码编译直接报错,而第八行代码的跨度按道理也是错误的。但是有的编译器编译成功并且跨度当做1字节计算(如DEV C++);在Visual Studio 2019下直接报错。

  • 如果要使用的话,就必须进行强制类型转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include<stdio.h>
    int main(int argc, char* argv[]) {
    void* p;
    int num = 10;
    p = &num;
    printf("%d\n",*(int *)(p));
    printf("%#x\n", p);
    printf("%#x\n", ((int *)(p) + 1));// p临时指向的类型为int
    //printf("%#x\n", p+1);//这里仍然会报错,因为上面的强制类型转换只是临时性的
    return 0;
    }

    运行结果:

图片

3. 不要操作 没有初始化的指针变量 即对其取 *

1
2
3
int *p;
printf("p=%d\n",*p);
//因为p没有初始化,内容随机,也就是p指向了一个位置的空间,系统不允许用户,进行取值*p的操作

4. 不要操作 初始化为NULL的指针变量 即对其取 *

1
2
3
int *p = NULL;
printf("p=%d\n",*p);
//NULL 就是(void *)0 地址,也就是内存的起始地址,受系统保护,也不能取值 *p

5. 不要给指针变量赋无意义的普通数值

1
2
3
int *p = 2000;
//此时的2000对于p来说 是地址编号 2000
// *p表示在地址编号为2000 的位置取值,而地址编号2000 不是合法的空间,所以不能*p 会出现段错误

内存必须申请后才能使用,通过人为调用函数申请、定义一个变量系统自动申请空间等等,这些都是合法空间;否则其他的都是非法空间,都不能使用,系统会保护的。

6. 指针变量不要操作越界的空间

1
2
3
char num = 10;
int *p = &num;
printf("num=%d",*p);

运行结果:
图片