【无标题】小白学习笔记(指针)

变量的访问方式:

1:直接访问:直接使用变量名进行访问

2:间接访问:通过指针来访问

内存地址与指针:

为了有效访问到内存内的每个单元,就给内存单元进行了编号,这就是内存单元的地址。因为每个内存单元都有地址,所以变量存储的数据也是有地址的。

通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象的称为指针,即:

变量:命名的内存空间,用于存放各种类型的数据

变量名:变量名是给内存空间取一个容易记忆的名字

变量值:在变量单元中存放的数据值

变量的地址:变量所用的内存空间的地址,即指针(如果要访问该变量,就访问首地址)

如图:

指针变量:一个变量专门用来存放另一变量在内存中数据的地址(即指针),则他被称为“指针变量”,我们可以通过访问指针变量达到访问内存中另一个数据的目的(有时为了方便直接将指针变量称为指针)

指针变量的定义:

一般格式:

数据类型 * 指针变量名 [ = 初始地址值];

数据类型是指,指针变量所指向变量数据的数据类型。可以是int,char,float等基本类型,也可以是数据等构造类型

字符*用于告知系统这里定义的是一个指针变量

举例1:

int *p;(这是一个指针变量,用于存储int型的整数在内存空间中数据的地址)

注意:

指针变量的名字是p,不是*p

指针变量中只能存放地址,不要将任何其他非地址类型的数据赋值给一个指针变量

举例2:同一行声明两个指针变量

int * a, * b;

举例3:一个指针指向的可能还是指针,这时就要用两个*号表示

int ** foo;

指针的应用场景:

场景1:使用指针访问变量或数组的元素

场景2:应用在数据结构中

指针的运算:

指针的运算都是针对内存中的地址来实现的。

取址运算符:&

作用:取出指定变量在内存中的地址。

格式:&变量

举例:

#include <stdio.h>
int main() {
    int num = 10;
    printf("%d\n",num);//输出变量的值
    printf("%p",&num);//输出变量的内存地址
    return 0;
}

说明:

在输出取址运算获得的地址时,需要使用%p作为输出格式符

这里的num有4个字节,每个字节都有地址,取出的是第一个字节的地址

指针变量的赋值:

1.指针变量中只能存放地址,不要将其他任何非地址类型的数据赋给指针变量

2.一个指针变量只能指向同一个类型的变量,不能抛开类型随意赋值,比如:

char*类型的指针是为了存放char类型变量的地址

int*类型的指针是为了存放int类型变量的地址等等

3.在没有指针变量赋值时,指针变量的值时不确定的。通常给指针变量赋初始值为0,并把值为0的指针变量称为空指针变量

举例:通过指针变量修改指向的内存中的数据

#include <stdio.h>
int main() {
    int num = 10,*ptr;
    ptr = &num;
    printf("%d\n",num);
    scanf("%d",ptr);//等价于scanf("%d",&num)
    printf("%d\n",num);
    return 0;
}

取值运算符:*

其作用与&相反,根据一个给定的内存地址取出该地址对应的变量,也称为解引用符号。

格式如下:

*指针表达式

举例1:

#include <stdio.h>
int main() {
    int a = 2024;
    int *p;//这里的*是告诉系统这是一个指针变量
    p = &a;
    printf("%p\n",&a);
    printf("%p\n",p);
    printf("%d",*p);//这里的*p与上面的*p不同,这里的*是运算符号
    return 0;
}

举例2:

#include <stdio.h>
int main() {
   int num = 10;
    printf("%d\n",num);//输出num的值
    printf("%p\n",&num);//输出num的地址
    printf("%d",*&num);//通过num的地址读取num中的数据,输出10
    return 0;
}

*和&互为逆运算,下面的表达式总是成立:

i ==*(&i)

举例3:通过指针变量修改指向内存地址上的值

#include <stdio.h>
int main() {
   int num = 10;
    int *p = &num;
    printf("%d\n",num);
    *p = 20;
    printf("%d",num);
    return 0;
}

其他类型的变量也适用

举例4:

//定义指针变量p1,p2
//默认各自指向整数a,b。a,b从键盘输入。
//设计程序,使得p1指向其中较大的值,p2指向其中较小的值
#include <stdio.h>
int main() {
    int *p1,*p2;
    int a,b;
    p1 = &a;
    p2 = &b;
    printf("输入a和b的值:");
    scanf("%d%d",&a,&b);
    if (*p1 < *p2) {
        int *temp = p1;
        p1 = p2;
        p2 = temp;

    }
    printf("%d\n",*p1);
    printf("%d",*p2);
    return 0;
}

指针的常用运算:

指针可以与整数加减,自增自减,同类指针相加减,但规则不是整数运算的规则

指针与整数值的加减运算

格式:指针+(-)整数

含义:指针与整数值的加减运算,表示指针所指向的内存地址的移动(加,向后移动;减,向前移动),指针移动的单位,与指针指向的数据类型有关。数据类型占据多少字节,每单位就移动多少字节。

说明:s +1表示指针向内存地址

的高位移动一个单位,而一个单位的short类型占据两个字符,所以相当于向高位移动两个字节。

注意:只有指向连续的同类型数据区域,指针加,减才有实际意义

举例:

举例2:通过指针访问数组

#include<stdio.h>
int main() {
    int arr[5] = {10,20,30,40,50};
    int i = 0;
    int *p = &arr[0];
    for (i = 0; i < 5; i++) {
        printf("%d\n",*(p + i));
    }

    return 0;
}

指针的自增,自减运算:

指针型变量也可以进行自增,自减的运算

格式:p++,p--,++p,--p

对于数组来说:

当对指针进行++时,指针会按照它指向的数据类型字节数大小增加,比如:int*指针,每++一次,就增加4个字节

--,同理

拓展:

*(p++)先取*p值,再使p自增1
*(++p)先使p自增1个单位字节长度,再取*p

拓展:

//当p当前指向a数组中第i个元素a[i],则:
//*(p--),相当于a[i--],先对p进行*运算,再使p自减
//*(++p),相当于a[++i],先使p自加,在进行*运算

同类指针相减运算:

格式:指针 - 指针

同类型的指针允许进行减法运算,返回他们之间的距离,即 相隔多少个

数据单位(注意:非字节数)

高位地址减去低位地址,返回的是正值;低位减去高位返回的是负值

返回的值属于ptrdiff_t类型,这是一个带符号的整数类型别名,具体类型根据系统不同而不同。这个类型的原型定义在头文件stddef.h里面

举例:

#include <stdio.h>
int main() {
    int arr[] = {1,2,3,4,5};
    int *p1 = &arr[0];
    int *p2 = &arr[3];
    printf("p1的地址为:%d\n",p1);//当前的地址是1287649088
    printf("p2的地址为:%d\n",p2);//当前的地址是1287649100
    printf("p2 - p1 = %d",p2 - p1);//3,等同于俩地址互减之后/4(int类型所占用的字节数)


    return 0;
}

体会:两个指针相减,通常两个指针都是指向同一数组中的元素才有意义,结果是两个地址之差除以数组元素的长度。不相干的两个变量的地址,通常没有相减的必要

注意:同类指针进行加法是没有意义的,也是非法的

同类型指针间的比较运算:

指针间的比较运算,比如:==,!=,<,<=,>,>=等比较的是各自之间内存的大小,返回值通常是1(true)和0(false)

野指针:

定义:

指针指向的位置是不可知的(随机性,不正确,没有明确限制),实际开发中要避免野指针

野指针的成因:

1.指针使用前未初始化

指针变量在定义时,如果未初始化,其值就是随机的,此时操作指针就是去访问一个不确定的地址,所以结果是不可知的,此时指针就是野指针

举例:

#include <stdio.h>
int main() {
    int *p;
    printf("%d",*p);
    return 0;
}

在没有给指针变量赋初始值时,后续一系列的操作(包括修改指向内存的数据)也是错误的

2.指针越界访问

举例:

int main() {
    int arr[10] = {0};
    int *p = arr;
    for (int i = 0; i <= 10; p++) {
        *p = i;
    }
    return 0;
}

当i = 10时,此时*p访问的空间内存不在有效范围内,此时*p就属于非法形式的访问,为野指针

3.指针指向已经释放的空间

有关函数,还没学,先放着

野指针的避免:

1.指针初始化

定义指针的时候,如果没有确切的地址赋值,为指针变量赋值NULL值是一个良好的习惯。

举例:

#include <stdio.h>
int main() {
    int *p = NULL;
    return 0;
}

赋值NULL的指针被称为空指针,NULL指针是一个定义在标准库stdio.h中值为0的常量

后面如果用到指针的话,再让指针指向具有实际意义的地址,然后通过指针的取值符号(*)改变其指向的内容。

练习:

#include <stdio.h>
int main() {
    int *p =NULL;
    int b = 8;
    p = &b;//一定要有显示初始化这一步
    *p = 100;
    printf("%d\n",*p);//输出为100
    printf("%d",b);//输出为100
    return 0;
}

2.小心指针越界

3.避免返回局部变量的地址

4.指针指向空间释放,及时置NULL(先放着)

5.指针使用前检查有效性

二级指针(多重指针):一个指针p1记录一个变量的地址,由于指针p1也是变量,那么也会有地址,那p1的地址就可以用另一个指针变量p2表示,则p2就是二级指针。简单理解就是二级指针就是指向指针的指针格式:数据类型 **指针名;举例:

int main() {
    int a = 10;
    int *pa = &a;//pa是一级指针
    int **ppa = &pa;//ppa是二级指针,类型为int **
    
    return 0;
}

练习:使用malloc函数创建二维数组

#include <stdio.h>
#include <malloc.h>
int main() {
    //使用malloc()函数创建二维数组
    //1.从键盘获取row和column
    int row, column;
    printf("第一维长度:");
    scanf("%d", &row);
    printf("第二维长度:");
    scanf("%d", &column);
    //2.初始化外层数组
    int **arr = (int **)malloc(row*sizeof(int *));
    //通过循环赋值
    for (int i = 0; i < row; i++) {
        arr[i] = (int *)malloc(column*sizeof(int));
        for (int j = 0; j < column; j++) {
            arr[i][j] = 1;
            printf("%d",arr[i][j]);
        }printf("\n");
    }
//后续可以使用此数组
//使用完以后回收此数组
    free(arr);

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值