【C】Re05 指针

一、变量 & 指针

变量 = 内存地址 + 存储值

指针变量 = 内存地址 + 存储值【变量的内存地址】

作用: 间接访问内存地址

内存地址 = 地址编号

地址编号:内存中的每个字节唯一的编号,从0开始记录,使用十六进制显示

可以使用指针变量存储变量的地址

不同数据类型就有对应的指针的数据类型

二、使用

#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>


void pointer () {
    // 指针变量
    int * pointer;
    // 变量
    int varA = 100;

    printf("pointer -> x16 %x, x10 %d, x8 %o\n", pointer, pointer, pointer);
    printf("varA -> %d\n", varA);

    printf("- - - - - - - - - - - - - - -\n");

    // 把varA的地址赋值给指针变量pointer
    pointer = &varA;
    // 通过指针变量pointer取地址访问varA变量
    *pointer = 20;

    printf("pointer -> x16 %x, x10 %d, x8 %o\n", pointer, pointer, pointer);
    printf("varA -> %d\n", varA);
}

int main() {
    pointer();
    return EXIT_SUCCESS;
}

输出格式注意:

 // %p显示完整十六进制位数, %x只显示进制数

语法递进:

    int tf = (*pointer == *&varA); // 0 false, 1 true
    int tf2 = (pointer == &varA);
    int tf3 = (*pointer == varA);

    printf(" %d\n",tf);
    printf(" %d\n",tf2);
    printf(" %d\n",tf3);

三、空指针& 野指针

空指针定义

void nullPointerAndWildPointer () {
    // 定义一个空指针
    int * nullPointer = NULL;
}

指向的NULL常量来自于STDLIB标准库头文件的这一段

#else
#define NULL ((void *)0)
#endif
#endif

最后指向的还是一个0而已

void nullPointerAndWildPointer () {
    // 定义一个空指针
    int * nullPointer = 0;
}

实际上不建议直接写0,容易混淆指针变量与变量

空指针不能被访问到:

void nullPointerAndWildPointer () {
    // 定义一个空指针
    int * nullPointer = NULL;
    printf("nullPointer -> %p", *nullPointer);
}

int main() {
    nullPointerAndWildPointer();
    return EXIT_SUCCESS;
}

因为内存地址编号0 - 255已经被操作系统占用了

空指针的作用:

不知道应该对指针定义多少合适时使用

野指针定义:

void nullPointerAndWildPointer () {
    int * wildPointer = 0xffff;
    printf("wildPointer -> %p\n", *wildPointer);
}

指针变量存储了非法的、未知的一个内存地址,该地址存储的内容将无法访问

但是允许查看地址

void nullPointerAndWildPointer () {
    // 定义一个空指针
//    int * nullPointer = NULL;
//    printf("nullPointer -> %p\n", *nullPointer);

    // 定义一个野指针
//    int * wildPointer = 0xffff;
//    printf("wildPointer -> %p\n", *wildPointer);

    int * nullPointer = NULL;
    printf("nullPointer -> %p\n", nullPointer);

    int * wildPointer = 0xffff;
    printf("wildPointer -> %p\n", wildPointer);
}

int main() {
    nullPointerAndWildPointer();
    return EXIT_SUCCESS;
}

野指针的第二种情况:

也是一样,地址可以访问,但是内部存储的值无法访问

    // 野指针的第二种情况
    int * wildPointer2;
    printf("wildPointer2 -> %p\n", wildPointer2);
    // printf("wildPointer2 value -> %d\n", *wildPointer2);

四、无类型指针 和 万能指针

1、Void类型概述

void voidUsage() {
    // void 是一个数据类型,所以也具备对于的指针类型 void *
    // void 的用途是修饰函数返回类型和形参类型
}

// 形参修饰了void 表示 该函数不需要参数
void noNeedParam( void ) {
    
}

2、函数返回类型省略

当函数的返回值类型声明的是void时,我们可以省略,不需要return

不过不建议这样书写,C++并不支持这样的语法

aaa () {
    printf("void返回类型省略的函数调用!!!");
}

int main() {
    aaa();
    return EXIT_SUCCESS;
}

如果函数不需要注入任何类型的参数,编写时是可以明确标注void 数据类型即可

3、无类型指针与万能指针:

 无类型指针可以强转任意类型接收对于的类型的变量地址

void noTypePointer() {
    void * p = NULL;
    printf("sizeof p = %d\n", sizeof(p)); // 64位 sizeof p = 8 32位 sizeof p = 4

    int num = 10;
    p = &num;
    // printf("p = %d\n", *p); int指针类型赋值给void指针类型, 类型不匹配错误
    // 使用强转来转换指针类型
    printf("p = %d\n", *(int *)p);
}

 另外可以作为万能指针使用:

void anyTypePointer() {

    void * pointer = NULL;

    int * varA = NULL;
    char * varB = NULL;

    // 一个是int指针类型一个是char指针类型,直接这样赋值不会有语法错误提示
    // 但是在编译执行时会有警告提示,另外,如果指针调用了就会报错。。。
    // varA = varB;
    // 所以需要强转处理
    varA = (int *)varB;

    // void*指针类型的指针可以赋值给任何指针类型,不需要强转【自动转换?】
    varA = pointer;
}

// 因为void*指针类型 第二用,作为参数类型
void function ( void * sss) {
    
}

五、Const修饰指针变量

void constWithPointer() {
    // 1、const修饰的是*, *p 是只读的, p可读可写的
    int varA = 100;
    const int * pToVarA = &varA; // 如何判断是修饰* 还是 p? 看*还是p靠前,和const近, 等同于 int const * pToVarA = &varA;
    // *pToVarA = 20; 不允许访问地址写入了

    printf("pointer -> %p\n", pToVarA);

    int varB = 200;
    pToVarA = &varB; // 更改赋值地址是允许的

    printf("pointer -> %p\n", pToVarA);

    // 总结下来就是:指针指向地址的存储值不可以改变,指向地址可以改变

    // --------------------------------------------------------------

    // 2、const修饰的是p, p 是只读的, *p可读可写的
    int c = 150;

    // 语法
    int * const p = &c;

    // 可以对指向地址的存储值进行写入更改
    *p = 20;
    // p = &varA; 但是不再允许指向其他地址了

    // 指针指向地址的存储值可以修改,但是指针的指向地址不可以修改

    // --------------------------------------------------------------

    // 3、const修饰的是*和p, *和p都是只读的
    int d = 120;
    int const * const p2 = &d;
    // p2 = &c; 更改指向 不允许
    // *p2 = 230; 更改存储值 不允许
}

int main() {
    constWithPointer();
    return 0;
}

六、不同指针类型的区别?

void differFromPointers() {
    char * p1 = NULL;
    int * p2 = NULL;
    double * p3 = NULL;

    printf("p1 -> %p\n", p1);
    printf("p2 -> %p\n", p2);
    printf("p3 -> %p\n", p3);

    // 区别1 不同指针类型 叠加字面值的步长不一样,这取决于他们的数据类型长度
    printf("p1 + 1 -> %p\n", p1 + 1); // char指针类型向前移动一个字节
    printf("p2 + 1 -> %p\n", p2 + 1); // int指针类型向前移动4个字节
    printf("p3 + 1 -> %p\n", p3 + 1);

    // 区别2 解引用的字节数量也不一样,取到的值也就不一样
    int num = 0x01020304; // int num = 0x01020304; int num = 0x21348903;
    int * p4 = &num;
    printf("*p4 -> %#x\n", *p4); // *p4 -> 0x1020304

    short * p5 = &num;
    printf("*p5 -> %#x\n", *p5); // *p5 -> 0x304

    char * p6 = &num;
    printf("*p6 -> %#x\n", *p6); // *p5 -> 0x304
}

int main() {
    differFromPointers();
    return 0;
}

 

posted @ 2020-11-09 13:39  emdzz  阅读(1990)  评论(0编辑  收藏  举报