第九章 指针

第九章 指针

9.1.1 取地址运算

sizeof

是一个运算符,给出某个类型或变量在内存中所占据的字节数

  • sizeof(int)
  • sizeof(i)
#include<stdio.h>
int main()
{
	int a;
	a=6;
	printf("sizeof(int)=%ld\n",sizeof(int));
	printf("sizeof(double)=%ld\n",sizeof(double));
	printf("sizeof(a)=%ld\n",sizeof(a));	
	return 0;
}

在这里插入图片描述

运算符&

scanf(“%d”,&i);里的&

获得变量的地址,它的操作数必须是变量

printf("%p\n",&i);

地址的大小是否与int相同取决于编译器

&不能对没有地址的东西取地址

  1. 变量的地址

  2. 相邻的变量的地址(紧挨着放,堆栈)

  3. &的结果的sizeof(32位和64位的sizeof是不同的)

  4. 数组的地址

  5. 数组单元的地址

  6. 相邻的数组单元的地址(差距永远是4)

    #include<stdio.h>
    int main()
    {
    	int a[10];
    	printf("%p\n",&a);
    	printf("%p\n",a);
    	printf("%p\n",&a[0]);
    	printf("%p\n",&a[1]);
    	return 0;
    }
    

    在这里插入图片描述

9.1.2 指针

指针变量就是保存地址的变量

int i;
int* p = &i;
int* p,q;
int *p,q;

变量的值是内存的地址

在这里插入图片描述

  • 普通变量的值是实际的值
  • 指针变量的值是具有实际值的变量的地址

作为参数的指针

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

#include<stdio.h>
void f(int *p);
int main()
{
	int i=6;
	printf("&i=%p\n",&i);
	f(&i);
	return 0;
}
void f(int *p)
{
	printf(" p=%p\n",p);
}

在这里插入图片描述

**访问地址上的变量 * **

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

  • int k= *p;
  • *p=k+1;
#include<stdio.h>
void f(int *p);
void g(int k);
int main()
{
	int i=6;
	printf("&i=%p\n",&i);
	f(&i);
	g(i);
	return 0;
}
void f(int *p)
{
	printf(" p=%p\n",p);
	printf("*p=%p\n",*p);
}
void g(int k)
{
    printf("k=%d\n",k);
}

在这里插入图片描述

int i;scanf(“%d”,i)

编译不一定会出错,运行一定会出错,scanf会把读取进来的数字当成一个地址

9.1.3 指针的使用

交换两个变量的值

#include<stdio.h>
void swap(int *pa,int *pb);
int main()
{
	int a=5;
	int b=6;
	swap(&a,&b);
	printf("a=%d,b=%d",a,b);
	return 0;
}
void swap(int *pa,int *pb)
{
	int t=*pa;
	*pa=*pb;
	*pb=t;
}

在这里插入图片描述

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

#include<stdio.h>
void minmax(int a[],int len,int *min,int *max);

int main()
{
	int a[]={ 1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55};
	int min,max;
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min=%d,max=%d\n",min,max);
	return 0;
}
void minmax(int a[],int len,int *min,int *max)
{
	int i;
	*min=*max=a[0];
	for(i=1;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}

在这里插入图片描述

函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:

但是当任何数值都是有效的可能结果时,就得分开返回了

#include<stdio.h>
int divide(int a,int b,int *result);
int main()
{
	int a=5;
	int b=2;
	int c;
	if(divide(a,b,&c)){
		printf("%d/%d=%d\n",a,b,c);
	}
	return 0;
}
int divide(int a,int b,int *result)
{
	int ret=1;
	if(b==0){
		ret=0;
	}else{
		*result=a/b;
	}
}

在这里插入图片描述

常见错误

  • 定义了指针变量,还没有指向任何变量,就开始使用指针

9.1.4 指针与数组

#include<stdio.h>
void minmax(int a[],int len,int *min,int *max);

int main()
{
	int a[]={ 1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55};
	int min,max;
    printf("main size(a)=%lu\n",sizeof(a));
    printf("main a=%p\n",a);
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    printf("a[0]=%d\n",a[0]);
	printf("min=%d,max=%d\n",min,max);
	return 0;
}
void minmax(int a[],int len,int *min,int *max)
{
	int i;
    printf("minmax size(a)=%lu\n",sizeof(a));
    printf("minmax a=%p\n",a);
    a[0]=1000;
	*min=*max=a[0];
	for(i=1;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}

在这里插入图片描述

函数参数表中的数组实际上是指针
sizeof(a) == sizeof(int*)
但是可以用数组的运算符[]进行运算

数组参数

  • 以下四种函数原型是等价的:
    • int sum(int *ar, int n);
    • int sum(int *, int);
    • int sum(int ar[], int n);
    • int sum(int [], int);

数组变量是特殊的指针

  • 数组变量本身表达地址,所以

    • int a[10]; int*p=a; //无需用&取地址
    • 但是数组的单元表达的是变量,需要用&取地址
    • a== &a[0]
  • []运算符可以对数组做,也可以对指针做:

    • p[0]<==> a[0]
  • *运算符可以对指针做,也可以对数组做

    • *a=25;
  • 数组变量是const的指针,所以不能被赋值

    • int b[]-->int * const b;
      

9.1.5 指针与const

只适用于C99

在这里插入图片描述

指针是const

表示一旦得到了某个变量的地址,不能再指向其他变量

int *const q= &i;  // q是const
*q= 26;// OK
q++;   // ERROR

所指是const

在这里插入图片描述

表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)

const int *p = &i;
*p= 26;// ERROR! (*p)是const
i= 26;//OK
p = &j;//OK

转换

总是可以把一个非const的值转换成const的

void f(const int* x); //保证在函数内部不会驱动这个指针的值
int a=15;
f(&a); // ok 
const int b = a;
f(&b); // ok
b = a+1; // Error!

当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改

const数组

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

保护数组值

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

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

9.2.1 指针运算

#include<stdio.h>
int main(void)
{
	char ac[]={0,1,2,3,4,5,6,7,8,9};
	char *p=ac;
	printf("p  =%p\n",p);
	printf("p+1=%p\n",p+1);
	int ai[]={0,1,2,3,4,5,6,7,8,9};
	int *q=ai;
	printf("q  =%p\n",q);
	printf("q+1=%p\n",q+1);
	return 0;
}

在这里插入图片描述

加一个sizeof而不是加1

在这里插入图片描述

#include<stdio.h>
int main(void)
{
	char ac[]={0,1,2,3,4,5,6,7,8,9};
	char *p=ac;
	printf("p  =%p\n",p);
	printf("p+1=%p\n",p+1);
	printf("*(p+1)=%d\n",*(p+1));  //得到a[1]的值
	int ai[]={0,1,2,3,4,5,6,7,8,9};
	int *q=ai;
	printf("q  =%p\n",q);
	printf("q+1=%p\n",q+1);
	printf("*(q+1)=%d\n",*(q+1));
	return 0;
}

在这里插入图片描述

给一个指针加I表示要让指针指向下一个变量

int a[10];
int*p=a;
*(p+1)-> a[1]

如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义

指针计算

  • 给指针加、减一个整数(+,+=,-,-=) 在现在的位置往前/后挪一个位置

  • 递增递减(++/–) 挪到下个位置上去

  • 两个指针相减

    #include<stdio.h>
    int main(void)
    {
    	char ac[]={0,1,2,3,4,5,6,7,8,9};
    	char *p=ac;
    	char *p1=&ac[5];
    	printf("p  =%p\n",p);
    	printf("p+1=%p\n",p+1);
    	printf("p1-p=%d\n",p1-p);
    	int ai[]={0,1,2,3,4,5,6,7,8,9};
    	int *q=ai;
    	int *q1=&ai[6];
    	printf("q  =%p\n",q);
        printf("q1 =%p\n",q1);
    	printf("q1-q=%d\n",q1-q);
    	return 0;
    }
    

    在这里插入图片描述

    q1-q=(62FDD8-62FDD0)/sizeof(int)=6

*p++

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

指针比较

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

0地址

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

指针类型

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

指针的类型转换

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

用指针来做这什么

  • 需要传入较大的数据时用作参数
  • 传入数组后对数组做操作
  • 函数返回不止一个结果
  • 需要用函数来修改不止一个变量
  • 动态申请的内存…

9.2.2 动态内存分配

输入数据

如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
C99可以用变量做数组定义的大小,C99之前呢?

#include<stdlib.h>
int *a = (int* )malloc(n*sizef(int));
#include<stdio.h>
#include<stdlib.h>
int main()
{
	int number;
	int *a;
	int i;
	printf("输入数量:");
	scanf("%d",&number);
	a=(int*)malloc(number*sizeof(int));
	for(i=0;i<number;i++){
		scanf("%d",&a[i]);
	}
	for(i=number-1;i>=0;i--){
		printf("%d",a[i]);
	}
	free(a);
	return 0;
}

malloc

#include <stdlib.h>
void* malloc(size_ t size);

向malloc申请的空间的大小是以字节为单位的
返回的结果是void * ,需要类型转换为自己需要的类型

(int* )malloc(n*sizeof(int))

如果申请失败则返回0,或者叫做NULL
你的系统能给你多大的空间?

#include<stdio.h>
#include<stdlib.h>
int main(void)
{
	void *p;
	int cnt=0;
	while((p=malloc(100*1024*1024))){
		cnt++;
	}
	printf("分配了%d00MB空间\n",cnt);
	return 0;
}

在这里插入图片描述

free()

把申请得来的空间还给“系统’
申请过的空间,最终都应该要还
只能还申请来的空间的首地址

常见问题

  • 申请了没free-- >长时间运行内存逐渐下降
    新手:忘了
    老手:找不到合适的free的时机
  • free过了再free
  • 地址变过了,直接去free
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值