【C语言】指针


指针

一般用p来表示一个指针,来自pointer

一个指针类型的变量就是保存地址的变量。
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址

取地址运算符&
取值运算符*

int* p=&i;//表示p是一个指针,它指向的是一个int,现在把i的地址交给这个p指针
int* p,q;//无论*号靠近int还是靠近p,都只是表示p是个指针,q不是一个指针是一个普通变量
int *p,q;
int *p,*q;//p和q前面都有一个*号才表示p和q都是指针

作为参数的指针

void f(int *p);//f函数要一个int的指针
//在被调用的时候得到了某个变量的地址
int i=6;
f(&i);
//在函数里面可以通过这个指针访问外面的这个i

在这里插入图片描述
变量i里面存放了6,地址是70,把里面的地址取出交给f函数里面的变量p,p的值是70,于是可以说p是一个指针指向了i这个变量。

访问某个地址上的变量*

*是一个单目运算符,用来访问指针的值所表示的地址上的变量

例:*p表示变量i的值6
在这里插入图片描述

可以做左值也可以做右值。

int k=*p;
*p=k+1;

指针的应用

1、指针返回多个结果
① 函数返回值返回结果只能返回一个
② 函数返回多个值,某些值就只能通过指针返回,传入的参数实际上是需要保存带回的结果的变量

2、函数返回运算的状态,结果通过指针返回
① 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:-1或0 (在文件操作会看到大量的例子)。
② 但是当任何数值都是有效的可能结果时,就得分开返回了。
③ 后续的语言(C++,Java) 采用了异常机制来解决这个问题。

数组和指针

函数参数表中的数组实际上是指针,数组名代表数组首地址的值。

a为数组首元素的地址; &a为整个数组的地址。

sizeof(a) == sizeof(int*)

可以用数组的运算符[]进行运算

int sum(int *ar,int n);
等价于:
int sum(int ar[],int n);

int sum(int *,int);
等价于:
int sum(int [],int);

//不是表示类型等价,是表示在参数表中出现是等价的。

数组变量是特殊的指针

数组变量本身表达指针:

int a[10];
int *p=a;//数组变量本身表达指针,无需用&取地址

数组单元表达的是变量:

a==&a[0];//数组单元表达的是变量,需要用&取地址

[]运算符可以对数组做,也可以对指针做:

p[0] 等价于 a[0]

*运算符可以对指针做,也可以对数组做:

*a=25;

数组变量是const的指针,不能被赋值:

int a[];
等价于
int *const a;

const int a[] = {I,2,3,4,5,6};
数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int,所以必须通过初始化进行赋值。

保护数组值
因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值,为了保护数组不被函数破坏,可以设置参数为const

int sum(const int a[], int length);

指针常量和常量指针

指针常量——指针类型的常量(int *const p)
本质上是一个常量,指针用来说明常量的类型,表示该常量是一个指针类型的常量。在指针常量中,指针自身的值是一个常量,不可改变,始终指向同一个地址。

int a=10;
int *const p=&a;
p++;//报错,p指向的地址是一定的
*p=30;//p指向的地址是一定的,但其指向的内容可以修改

常量指针——指向“常量”的指针(const int *p,int const *p)
本质上是一个指针,常量表示指针指向的内容,说明该指针指向一个常量。在常量指针中,指针指向的内容是不可改变的,指针看起来好像指向了一个常量。

int a=10;b=20;
const int *p=&a;
p=&b;//指针可以指向其他地址,但是内容不可以改变
*p=30;//报错,指针的内容不可以改变

数组指针和指针数组

数组指针——指向数组的指针

指针数组——装着指针的数组

常见错误

地址变量没有被赋值之前,没有得到任何实际变量的地址之前,不能通过它用*访问任何的数据。

指针运算

这些算术运算可以对指针做:
给指针加、减一个整数(+, +=, +, -=)
递增递减(++/–)
两个指针相减……

  • 指针+1

给一个指针+1表示要让指针指向下一个变量
如果指针不是指向一片连续分配的空间,
如数组,则这种运算没有意义。

int a[]={1,2,3,4,5};
int *p=a;
printf("%p\n",p+1);//指针值+1实际上加的是sizeof

//*p等价于a[0], *(p+n)等价于a[n]
printf("%d\n",*(p+1));
等价于
printf("%d\n",a[1]);
  • 两个指针相减
//得到的是在该范围内数组范围内的元素的个数
int a[]={1,2,3,4,5};
int *p=a;//同int *p=&a[0];
int *p1=&a[5];
printf("%d\n",p1-p);

输出:5

//得到指针相减的字节大小
int b=(int)p1-(int)p;
printf("%d\n",b);

输出:20  //一个int型元素是4个字节,一个char型元素是1个字节
  • *p++

① 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
② *的优先级虽然高,但是没有++高
③ 常用于数组类的连续空间操作
④ 在某些CPU上,这可以直接被翻译成一条汇编指令

  • 指针比较

<, <=, ==, >, >=, != 都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性递增的

  • 0地址

① 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址,所以你的指针不应该具有0值,因此可以用0地址来表示特殊的事情:
返回的指针是无效的
指针没有被真正初始化(先初始化为0)
② NULL是一个预定定义的符号,表示0地址
③ 有的编译器不愿意你用0来表示0地址

  • 指针的类型

① 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
② 但是指向不同类型的指针是不能直接互相赋值的,这是为了避免用错指针

char a[]={1,2,3,4,5};
int a[]={1,2,3,4,5};
char *p=a;
int *q=b;
*q=0;
p=q;
//p指向char类型,一个char类型元素占1个字节,
//q指向int类型,一个int类型元素占4个字节,
//q给p赋值会导致p[0]-p[3]都被赋值

在这里插入图片描述

  • 指针的类型转换

① void表示不知道指向什么东西的指针,计算时与char相同(但不相通);
② 指针也可以转换类型
int p = &i; voidq = (void*)p;
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量:我不再当你是int了,我认为你就是个void!

  • 用指针来做什么

① 需要传入较大的数据时用作参数;
② 传入数组后对数组做操作;
③ 函数返回不止一个结果,如swap();
④ 需要用函数来修改不止一个变量;
⑤ 动态申请的内存……

动态内存分配

  • malloc

void* malloc(size_ t size);
向malloc申请的空间的大小是以字节为单位的,返回的结果是void*,需要类型转换为自己需要的类型
(int*)malloc(n*sizeof(int));

#include<stdlib.h>//需要加头文件<stdlib.h>

int *a=(int*)malloc(n*sizeof(int));
//(int*)强制装换为int类型,molloc需要的参数是这个数组占据的空间,以字节为单位的,所以用乘以sizeof()

free(a);//释放空间
  • free

① 把申请得来的空间还给系统,申请过的空间,最终都应该要还;
② 只能还申请来的空间的首地址,malloc申请来的地址原封不动的,怎么借来的怎么还!!!
③ free(NULL)不会报错,这不会做任何事情。

  • 常见问题

① 申请空间了没free,长时间运行内存逐渐下降;
② free过了再free;
③ 地址变过了,直接去free。


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值