【C语言】指针变量(1)

目录

地址----学前须知

指针变量

初始化和作用

模板

大小

类型

void* 特殊指针

const修饰变量

位置

放在*右边

放在*左边

*左右都放

指针的运算

1.指针  + -  整数

2.指针-指针

3.指针的关系运算

野指针

概念

成因

1.指针未初始化

2.指针越界访问

3.指针指向的空间被释放

规避方法

1.指针初始化

2.小心指针越界

3.指针不再使用时,指向空指针

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

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

传值调用和传址调用

传值调用

传址调用


地址----学前须知

首先我们需要理解一下地址

当计算机处理数据时,CPU需要从内存中读取和存放数据

内存被分为一个个内存单元,每个内存单元的大小为1字节

同时每个内存单元都有属于它自己的地址,例如:0x1100ff00这样子的16进制数字

所以可以这么理解:

内存单元的编号  =  地址  =  指针

指针变量

初始化和作用

指针通常情况下说的是指针变量,在进行初始化时需要用到取地址符号(&)

指针的作用就是访问内存


模板

指针变量中存放的是对应数据的地址

例如:

int a=10;
int * p = &a;

在上面的代码中,p作为一个int型的指针变量,指向的是a的地址

*的含义是:p是一个指针变量

int的含义是:p指向的是一个int类型的数据


大小

指针的大小取决于编译环境是32位或64位

倘若是32位,一个指针变量的大小就是4个字节,即32bit

若是64位,一个指针变量的大小就是8个字节,即64bit

指针变量还可以通过符号*进行解引用

例如

int a=10;
int * p =&a;
*p=5;//与a=5效果一致

最后一句*p=5就把p指针解引用了,相当于直接对a进行操作


类型

指针类型决定指针进行解引用操作符时访问字节的数目(即决定指针权限)

char*类型可访问1个字节

short*类型可以访问2个字节

int*类型可以访问4个字节的整型

float*类型可以访问4个字节的浮点型

下图中,一个格子代表一个字节

int*型+1时走过4个字节

char*型+1时走过1个字节

指针的类型决定了指针走一步经过的字节大小

当你想访问某一特殊内容时,记得使用适合的指针类型


void* 特殊指针

无具体类型的指针,也称泛型指针

可以接收任意地址的指针,但是无法直接进行指针的+-运算和解引用的运算

一般用于函数参数的部分,用来接收不同类型的指针,可使一个函数处理不同类型的数据


const修饰变量

在C语言中,被const修饰后的变量具有了常属性(不可被修改),为常变量

但在C++中,被const修饰后的变量就成为了常量

例如

const int a=10;
int arr[a];

倘若在C语言环境下就会报错,但是在C++环境下可以正常运行

但只用const修饰变量是可以通过指针进行修改的

就好比一栋宿舍楼,宿管阿姨把大门锁上了,但是有人翻墙进宿舍了,依旧对内部造成了变化

例如

#include<stdio.h>
int main()
{
    const int a=10;
    int * p =&a;
    *p=5;
    printf("%d",a);
    return 0;
}

可以看到a的值依旧被更改了,这种情况我们可以用const修饰指针变量加以限制


位置

在修饰指针变量时,const可以放在*的左边或右边

放在*右边

当放在*右边时,该指针变量的本身就被限制,不能指向其他变量

例如

int a=1;
int b=2;
int * const p=&a;
p=&b;//报错,因const限制了p

在上面的代码中,p=&b报错,因为p本身已经被const限制,无法指向除a以外的变量

假设a的地址为0x0011ff00,b的地址为0x0011ff04

那么p存放的内容就是0x0011ff00,不能改为0x0011ff04

但此时能通过解引用(*)来修改指针指向的内容

即p不能变,但是*p可以变!!!

例如

int a=1;
int * const p=&a;
*p=10;
printf("%d",a);

可以看到a的值被更改


放在*左边

当const放在*左边时,该指针可以指向其他变量,但不能通过解引用(*)修改其指向的值

int a=1;
int b=2;
int const * p=&a;
p=&b;

程序可以正常运行

但通过解引用(*)进行修改时则会报错

int a=1;
int const * p=&a;
*p=10;//报错

上面程序中*p=10报错

即p能变,但是*p不可以变!!!


同样可以看const右边的内容来区别

const * p时,*p不能变;const p时,p不能变

两种情况相反!!!大家要根据实际应用场景选择合适的修饰位置


*左右都放

也可以在*左右都加上const

此时不论是p还是*p都不能进行更改!!


指针的运算

1.指针  + -  整数

打印数组时可以通过指针加减整数来实现

由于数组的地址在内存中是连续的,我们便可以通过指针加1的方法来依次找到数据对应的地址

PS:数组名就是数组首元素的地址,如:数组arr,则arr = &arr【0】

#include<stdio.h>
int main()
{
    int a[5] = { 1,2,3,4,5 };
    int* p = &a[0];
    int sz = sizeof(a) / sizeof(a[5]);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", *(p + i));
    }
    return 0;
}

在上面代码中我们将p指向数组第一个元素的地址,随后依次加上i来指向后面的元素

注意:在打印时若写成*p+i,打印的值就会变成*p的值再加上i


2.指针-指针

指针-指针=两个指针之间的元素个数

指针也可以相加,但通常情况下没有意义

#include<stdio.h>
int main()
{
    int a[5] = { 1,2,3,4,5 };
    int* p1 = &a[0];
    int* p2 = &a[4];
    printf("%d", p2 - p1);
    return 0;
}

在上面代码中,p1是a【0】的地址,p2是a【4】的地址,两者相减得到之间的元素个数

注意:该计算方式的前提是两个指针指向了同一块空间!


3.指针的关系运算

指针的关系运算就是指针和指针比较大小,也就是地址和地址比较大小

我们可以通过指针的关系运算来打印数组内容

#include<stdio.h>
int main()
{
    int a[5] = { 1,2,3,4,5 };
    int* p = a;
    int sz = sizeof(a) / sizeof(a[0]);
    while (p < sz + a)
    {
        printf("%d ", *p);
        p++;
    }
    return 0;
}

在上面的代码中,我们就是通过“p<sz+a”这个关系式进行循环来打印数组内容


野指针

概念

野指针是有随机指向位置的指针

成因

1.指针未初始化
#include<stdio.h>
int main()
{
    int* p;
    *p = 1;
    return 0;
}

在上面的代码中,由于指针p没有初始化,成为野指针,在此时打印p的地址时会出现随机值(部分编译器会检测到p没有初始化而编译出错)

2.指针越界访问
#include<stdio.h>
int main()
{
    int a[5] = { 1,2,3,4,5 };
    int* p1=a;
    int* p2 = p1 + 5;
    return 0;
}

上面的代码中,由于a数组只有5个成员,但p2指向了p1后面第5个不在数组内的地址,p2就成为了野指针

3.指针指向的空间被释放
#include<stdio.h>
int* f()
{
    int a = 1;
    return &a;
}
int main()
{
    int* p = f();
    printf("%d", p);
    return 0;
}

在上面的代码中f函数向p传递了a的地址,但是这块地址在f函数调用过后被释放,p就成了野指针,此时再通过p访问该地址,就是非法访问

就好比出门旅游向酒店订了一间房,房号为401,一天后退房,此时再想对401访问就是非法的

规避方法

1.指针初始化

若明确知道指向地址,则直接初始化指向地址

若不知道指向地址,就指向NULL空指针(此时无法使用该指针)

2.小心指针越界

使用是注意指针申请内存大小,不要超出界限

3.指针不再使用时,指向空指针

在一段程序中,若一个指针在后面没有用处时,记得把它指向NULL

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

使用前查看指针是否无指向地址或指向NULL空指针

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

在程序中不要main函数中的指针不要返回其他局部函数的地址


传值调用和传址调用

传值调用

假设我想要写一个函数交换数字

#include<stdio.h>
void  f(int b,int c)
{
    int temp = b;
    b = c;
    c = temp;
}
int main()
{
    int a = 1, b = 2;
    f(a, b);
    printf("%d %d", a,b);
    return 0;
}

可以看到a和b的值并没有被交换

原因是因为在运行f函数时,传递的是数值

f函数在运行时,另外创建了两个int型数据b和c来接收a和b的值,即使在函数里交换了两者的值,也不会影响到主函数中a和b的值

在只需要传值来计算时可以采用传值调用


传址调用

同样还是以交换数字的函数举例

#include<stdio.h>
void  f(int* x,int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}
int main()
{
    int a = 1, b = 2;
    f(&a, &b);
    printf("%d %d", a,b);
    return 0;
}

可以看到同样的函数在使用传址调用后就成功让a和b交换

这是因为在运行函数时,调用的是a和b的地址

在函数中,对传来的指针进行解引用(*)就可以做到直接修改a和b的值

从而做到交换a和b

在需要更改传递的值时,要采用传址调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值