c语言-----指针
文章目录
运算符
运算符&
获取变量的地址,它的操作数必须是变量
获取一个变量的地址:
方法一:直接获取
int i;
printf("%x",&i); //十六进制输出
printf("%p",&i); //以十六进制整数方式输出指针的值
方法二:利用中间变量(注意:小心地址的字节大于int的字节,特别是在64位架构的编译器下)
int i;
int p;
p=(int)(&i); //将i的地址强制转换为int型
printf(""%x",p);
取数组的地址
int a[10];
printf("%p",&a); //取数组a的地址
printf("%p",a); //取数组a的地址
printf("%p",&a[0]); //取数组a第0个单元的地址
### 运算符*
*是一个单目运算符,用来访问指针的值所代表的地址上的变量
int k=*p; //吧p指针所代表的地址上的赋值值给变量k
*p=k+1; //把k+1的值赋值个p指针所代表的地址上的变量
### 运算符&和运算符*的关系
相互反作用
int *yptr;
就有:
*&yptr=*(&yptr); //都是取yptr得值
&*yptr=&(*yptr)=yptr //都是取yptr得地址值
指针
定义:保存地址的变量
int i;
int *p=&i; //指针P指向i(P里面放的是i的地址
int* p,q; //定义一个int型的指针P和int型变量q
int *p,q; //定义一个int型的指针P和int型变量q(和上面没有区别)
指针变量总结:
- 指针变量的值是内存的地址,普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
作为参数的指针
//定义一个带有指针的函数
void f(int *p) //传入的为一个int型的指针变量
{
}
//调用形式
int i;
f(&i); //调用时,必须传入一个地址(因为指针里面存放的是一个地址)
检验:
/*
检验通过子函数传进去的地址和变量的地址是否一样
*/
#include"stdio.h"
void f(int *p);
void main()
{
int i;
printf("i的地址:0x%p\n",&i);
f(&i);
}
void f(int *p)
{
printf("p的地址:0x%p\n",p);
}
结果:
i的地址:0x000000000062FE1C
p的地址:0x000000000062FE1C
所以两个地址是一样的。说明P用于访问i的地址的能力(p能够读写i的值)
指针的使用场景
使用场景一:交换两个变量的值
void swap(int *pa,int *pb)
{
int t;
t=*pa;
*pa=*pb;
*pb=t;
}
//调用
int a=1;
int b=2;
swap(&a,&b);
使用场景二a:函数返回多个值
返回一个数组的最大最小值
/*
返回一个数组的最大最小值
*/
#include"stdio.h"
void max_min(int a[],int length,int *max,int *min);
void main()
{
int b[10]={1,5,9,7,3,6,4,8,2,0};
int max,min;
max_min(b,sizeof(b)/sizeof(b[0]),&max,&min);
printf("b数组的最大值:%d\n",max);
printf("b数组的最小值:%d\n",min);
}
void max_min(int a[],int length,int *max,int *min)
{
int i;
*max=a[0];
*min=a[0];
for(i=1;i<length;i++)
{
if(a[i]>*max) //查找最大值
*max=a[i];
if(a[i]<*min) //查找最小值
*min=a[i];
}
}
结果:
使用场景二b :函数返回运算状态
常用套路:让函数返回特殊的,不属于有效范围内的值表示出错
常见的:返回-1或者0表示出错。但是如果-1和0也是有效的的?
解决方法:分开返回:状态用函数返回,结果用指针返回
#include"stdio.h"
/*
两个数做除法。
满足除法规则返还1和计算除法结果
不 满足除法规则返还0
*/
int divide(int a,int b,float *result);
void main()
{
int a,b;
float c;
printf("输入除数a:\n");
scanf("%d",&a);
printf("输入被除数b:\n");
scanf("%d",&b);
if(divide(a,b,&c))
{
printf("商:%f\n",c);
}
else {
printf("被除数不能为零!!!");
}
}
int divide(int a,int b,float *result)
{
int ret=1;
if(b==0)
{
ret=0;
}
else{
*result=(a*1.0)/b;
}
return ret;
}
指针最常见的错误
定义了指针变量,还没有指向任何变量,就开始使用指针
int i=8;
int *p;
*p=i; //错误
指针和数组
传入函数的数组实际上是指针,可以使用[]进行计算
/*
下面2种函数是等价的
*/
int sun(int a[]);
int sun(int *a);
所以数组变量就是特殊的指针。因为:数组变量本身表示地址
int a[10];int *p=a;//无需使用&
a==&a[0]
[]运算符可以对数组做,也可以对指针做:如果定义:int *p
则:
*p==p[0]
指针的运算
指针+1
指针+1相当于在当前指针的地址上+sizeof(类型)
比如
int a[10];
int *p;
char b[10];
int *q;
p=a;
q=b;
p=p+1; //p的地址加了4 因为 sizeof(a[0])=4
q=q+1; //q的地址加了1 因为 sizeof(b[0])=1
所以:*(p+n)
和a[n]
是等价的
如果指针没有指向一片连续分配的空间,这种计算没有意义。
两个指针相减
int *p,*q;
int a[10];
p=a;
q=&a[6]
printf("q-p=%d",q-p)
结果:q-p=6
两个指针相减的值为两个指针的地址值相减后再除以指针类型字节数
*p++的 计算
- 优先级:++的优先级大于*
- 计算方式:取出所指的地址里面的数据,完事后顺便把p移到下一个位置
- 常用于数组类的连续空间操作
eg:遍历一个数组
#include"stdio.h"
/*
遍历数组:-1结束标志
*/
void main()
{
int a[6]={0,1,2,3,4,-1};
int *p;
p=a;
while(*p!=-1)
{
printf("\t%d\n",*p++);
}
指针的比较
- 比较它们在内存中的地址
- 数组的单元的地址肯定是线性递增的
0地址
- 0地址常常是不能顺便碰的地址
- 我们所写的指针不能指向0地址
- 0地址表示特殊的事情
- 返回的指针无效
- 指针没有真正的初始化
- NULL是一个预定义的符号,表示0地址
指针的类型
- 无论指针指向什么类型,所有的指针大小都是一样的,因为都是地址
- 但是,指向不同类型的 指针是不能直接相互赋值的
指针的类型转化
int i;
int *p=&i;
void*q; //定义一个 不知道是什么类型的指针
q=(void*)p;
动态内存分配
输入一个数组,数组的大小由输入者决定
#include"stdio.h"
#include"stdlib.h"
/*
手动输入数组的个数,使用malloc进行动态内存分配
*/
void main()
{
int *q,i;
int number;
printf("请输入数组的单元数:\n");
scanf("%d",&number);
q=(int*)malloc(number*sizeof(int)); //分配空间
for(i=0;i<number;i++)
{
printf("输入第%d个数组元素:\n",i);
scanf("%d",&q[i]);
}
printf("数组为:\n");
for(i=0;i<number;i++)
{
printf("%d\t",q[i]);
}
free(q) ; //释放空间
}
结果:
malloc函数 :
函数原型void *malloc(long NumBytes)
该函数分配了NumBytes个字节,并返回了指向这块内存的指针。如果分配失败,则返回一个空指针(NULL)。
返回值为分配地址空间的首地址,但是,返回的类型是 void 类型。void 表示未确定类型的指针。C,C++规定,void* 类型可以通过类型转换强制转换为任何其它类型的指针。使用时需要手动指明指针类型**。
如果申请失败则返回0,或者NULL(0地址)
free函数
free()是C语言中释放内存空间的函数,通常与申请内存空间的函数malloc()结合使用,可以释放由 malloc()、calloc()、realloc() 等函数申请的内存空间。
注意:只能还申请的空间,不能对申请的指针做任何运算,也不能还定义的指针的地址
原型*void free(void *)
没有返回值
常见问题:
- 申请了空间,没有free,导致长时间运行内存逐渐下降
- free过了再free,会导致程序崩掉
查看系统能给多大的空间
#include"stdio.h"
#include"stdlib.h"
/*
查看系统能分配的空间
*/
void main()
{
void *p;
int cnt=0;
while(p=malloc(100*1024*1024))//每次分配100M的地址空间,
{ // 如果分配失败,返回0,退出循环
cnt++;
printf("目前分配的空间大小:%d100M\n",cnt);
}
}
结果:
目前我电脑能申请大约22G的空间
部分控制符说明
控制符 | 说明 |
---|---|
%d | 按十进制整型数据的实际长度输出。 |
%ld | 输出长整型数据。 |
%md | m 为指定的输出字段的宽度。如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。 |
%u | 输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。 |
%c | 用来输出一个字符。 |
%f | 用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。 |
%.mf | 输出实数时小数点后保留 m 位,注意 m 前面有个点。 |
%o | 以八进制整数形式输出,这个就用得很少了,了解一下就行了。 |
%s | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串, |
于 m,则按实际位数输出。 | |
%u | 输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。 |
%c | 用来输出一个字符。 |
%f | 用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。 |
%.mf | 输出实数时小数点后保留 m 位,注意 m 前面有个点。 |
%o | 以八进制整数形式输出,这个就用得很少了,了解一下就行了。 |
%s | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串, |
%x | 以十六进制形式输出整数, |
|