c c pointer - yaokun123/php-wiki GitHub Wiki

指针

一、定义指针三部曲

1、*与符号结合代表是一个指针变量

2、要保存谁的地址,将他的定义放在此处

3、用*p替换掉定义的变量

#include<stdio.h>

int main(){
    int a = 10;

    //1、*与符号结合代表指针变量(*p)
    //2、要保存谁的地址,将他的定义放在此处(int a)
    //3、用第一步的表达式替换掉定义的变量(int *p)
    int *p = &a;
    return 0;
}

二、指针变量的类型,以及存储的类型

指针变量:与*结合代表这是一个指针变量(p)

指针变量的类型:p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型(int *)

指针变量p用来保存什么类型数据的地址,将指针变量p和指针变量p最近的*一起拖黑,剩下什么类型,就保存什么类型数据的地址(int)

#include<stdio.h>

int main(){
    int a = 10;

    int *p = &a;

    //1、p是指针变量
    //2、指针变量p的类型是int *
    //3、指针变量p中存放的是什么类型的地址 int
    return 0;
}

三、指针变量的使用

在使用时,对一个表达式取*,就会对表达式减一级*,如果对表达式取&,就会加一级*

四、指针变量的大小

不管什么类型的指针,大小只和系统编译器有关

#include<stdio.h>

int main(){
    char *p1;
    short *p2;
    int *p3;
    int **p4;

    printf("%u\n",sizeof(p1));
    printf("%u\n",sizeof(p2));
    printf("%u\n",sizeof(p3));
    printf("%u\n",sizeof(p4));
    return 0;

    /*
    8
    8
    8
    8
    */
}

五、指针变量用来保存什么类型数据的地址的影响

#include<stdio.h>

int main(){

    int num = 0x01020304;
    char *p1 = (char *)&num;
    short *p2 = (short *)&num;
    int *p3 = &num;

    //通过*取指针变量所指向哪块空间内容时,取的内存的宽度和指针变量本身的类型有关
    printf("%x\n",*p1);//04
    printf("%x\n",*p2);//0304
    printf("%x\n",*p3);//01020304
    return 0;
}

六、不同类型的指针变量,取指针指向的内容的宽度

指针的宽度 = sizeof(将指针变量与指针变量最近的*拖黑,剩下的类型)

宽度也叫步长:指针+1跨过多少个字节

char *p1;//1

short *p2;//2

int *p3;//4

int **p4;//8

#include<stdio.h>

int main(){
    char a = 'A';
    char *p1 = &a;

    printf("char a address %u\n",p1);
    printf("char a address + 1 %u\n",p1+1);

    short b = 10;
    short *p2 = &b;
    printf("short b addredd %u\n",p2);
    printf("short b addredd + 1 %u\n",p2+1);

    int c = 10;
    int *p3 = &c;
    printf("int c address %u\n",p3);
    printf("int c address + 1 %u\n",p3+1);

    int **p4 = &p3;
    printf("**p4 address %u\n",p4);
    printf("**p4 address + 1 %u\n",p4+1);
    return 0;

    /*
    char a address 3799500779
    char a address + 1 3799500780
    short b addredd 3799500766
    short b addredd + 1 3799500768
    int c address 3799500748
    int c address + 1 3799500752
    **p4 address 3799500736
    **p4 address + 1 3799500744
    */
}

七、[]不是数组的专属

在使用时:[] = *()

p[n] = *(p+n)

#include<stdio.h>

int main(){
    int a = 10;
    int *p = &a;
    p[0] = 100;//p[0] = *(p+0) = *p

    printf("%d\n",a);
    return 0;
}

八、指针数组

指针数组是一个数组,数组里面的元素保存的都是地址

#include<stdio.h>


int main(){

    int a = 10;
    int b = 20;
    int c = 30;

    //定义指针数组
    int *p[10] = {&a,&b,&c};

    printf("sizeof int *p[10] = %d\n",sizeof(p));//80

    printf("%d \n",*p[0]);//访问数组第一个元素地址指向的值

    //定义一个指针用来保存数组p首元素的地址p = &p[0] = &(int *) = int**
    int **k = p;
    for(int i = 0;i<sizeof(p)/sizeof(p[0]);i++){
        printf("%d ",**(k+i));
    }
    return 0;
}

九、数组作为函数的参数

数组作为函数形参时,会默认转化为指针,数组首元素地址

#include<stdio.h>

void print_arr(int b[10]){
    //数组作为函数形参时,会默认转化为指针int *b
    printf("%u\n",b);
    printf("%u\n",sizeof(b));//8
    printf("%u\n",sizeof(b[0]));//4 = b[0] = *b = 1 = int;
}
int main(){
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    print_arr(a);
    return 0;
}


修改为:

#include<stdio.h>

void print_arr(int b[10],int len){
    //数组作为函数形参时,会默认转化为指针int *b
    printf("%u\n",b);
    printf("%u\n",sizeof(b));//8
    printf("%u\n",sizeof(b[0]));//4 = b[0] = *b = 1 = int;

    for(int i = 0;i<len;i++){
        printf("%d ",b[i]);//b[i] = *(b+i)
    }
    printf("\n");
}
int main(){
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    print_arr(a,sizeof(a)/sizeof(a[0]));
    return 0;
}

十、野指针

野指针不同于空指针,空指针是指一个指针的值为null,而野指针的值并不为null,野指针会指向一段实际的内存,只是它指向哪里我们并不知情,或者是它所指向的内存空间已经被释放,所以在实际使用的过程中,我们并不能通过指针判空去识别一个指针是否为野指针。避免野指针只能靠我们自己养成良好的编程习惯,下面说说哪些情况下会产生野指针,以及怎样避免。

1、指针变量的值未被初始化:

声明一个指针的时候,没有显示的对其进行初始化,那么该指针所指向的地址空间是乱指一气的。如果指针声明在全局数据区,那么未初始化的指针缺省为空,如果指针声明在栈区,那么该指针会随意指向一个地址空间。所以良好的编程习惯就是在声明指针的时候就对其进行初始化,如果暂时不知道该初始化成什么值,就先把指针置空。

#include <stdio.h>
 int main()
 {
    int *p;//只定义了指针,未初始化,会导致生成野指针
    *p = 100;
    printf("%d\n",*p);
    return 0;
 }

2、指针所指向的地址空间已经被free或delete:

在堆上malloc或者new出来的地址空间,如果已经free或delete,那么此时堆上的内存已经被释放,但是指向该内存的指针如果没有人为的修改过,那么指针还会继续指向这段堆上已经被释放的内存,这时还通过该指针去访问堆上的内存,就会造成不可预知的结果,给程序带来隐患,所以良好的编程习惯是:内存被free或delete后,指向该内存的指针马上置空。

void func()
{
    int *ptr = new int[5];
    delete []ptr;
    // 执行完delete后,ptr野指针
}

3、指针操作超越了作用域:

void func()
{
    int *ptr = nullptr;
    {
        int a = 10;
        ptr = &a;
    } // a的作用域到此结束

    int b = *ptr;    // ptr指向a,a已经被回收,ptr野指针
}

十一、空指针

指针指向了内存地址为0x00000000,该段地址不可操作

⚠️ **GitHub.com Fallback** ⚠️