C语言学习-翁凯(第九章笔记)

这篇博客详细介绍了C语言中指针的使用,包括取地址运算符&、指针变量的概念、指针的运算以及动态内存分配。通过示例解析了指针与数组的关系、const指针的用法,并探讨了指针运算中的常见问题和动态内存分配的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言学习-翁凯(第九章笔记)

第九章

9.1.1 取地址运算:&运算符取得变量的地址

运算符&
  • scanf("%d",&i);里面的&
  • 获得变量的地址,它的操作必须是变量
    • int i;printf("%x",&i);
  • 地址的大小是否与int相同取决于编译器
    • int i;printf("%p",&i);
&不能取的地址
  • &不能对没有地址的东西取地址
    • &(a+b)?
    • &(a++)?
    • &(++a)?
试试这些&
  • 变量的地址
  • 相邻变量的地址
  • &的结果的sizeof
  • 数组的地址
  • 数组单元的地址
  • 相邻数组单元的地址

(相邻地址之间的差距是4)

9.1.2 指针:指针变量就是记录地址的变量

scanf
  • 如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量?
    • scanf("%d",&i);
  • scanf()的原型应该是怎样的?我们需要一个参数能保存别的变量的地址,如何表达能够保存地址的变量?
指针
  • 就是保存地址的变量

    int i;

    int* p=&i;

    int* p,q;(p是指针,q是int变量)

    int *p,q;(p是指针,q是int变量)

    (*无论靠近p还是int,都是表示p是指针,如想表达两个指针则:int * p,*q )

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

在这里插入图片描述

访问那个地址上的变量*
  • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  • 可以做右值也可以做左值
    • int k=*p;
    • *p=k+1;
*左值之所以叫左值
  • 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果:
    • a[0]=2;
    • *p=3;
  • 是特殊的值,所以叫做左值
指针的运算符&*
  • 互相反作用
    • *&yptr -> *(&yptr)-> *(yptr的地址)->得到那个地址上的变量->yptr
    • &*yptr -> &( *yptr)->&(y)->得到y的地址,也就是yptr->yptr
传入地址
  • 为什么
    • int i;scanf("%d",i);
  • 编译没有报错?

9.1.3 指针的使用:指针有什么用呢?

指针应用场景一
  • 交换两个变量的值
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(void)
{
	int a[]={2,3,5,8,64,32,24,12,6,16,26};
	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];
		}
	}
}
指针应用场景二b
  • 函数返回运算的状态,结果通过指针返回
  • 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
    • -1或0(在文件操作会看到大量的例子)
  • 但是当任何数值都是有效的可能结果时,就得分开返回了
    • 后续的语言(C++,Java)采用了异常机制来解决这个问题
/*
@return 如果出发成功,返回1;否则返回0 
*/
int divide(int a,int b,int *result);

int main(void){
	int a=5;
	int b=1; 
	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;
	}
	return ret;
}
指针最常见的错误
  • 定义了指针变量,还没有指向任何变量,就开始使用指针

9.1.4 指针与数组:为什么数组传进函数后的sizeof不对了

传入函数的数组成了什么?
int isPrime(int x,int knownPrimes[],int number0fKnownPrimes)
{
    int ret=1;
    int i;
    for(i=0;i<number0fKnownPrimes;i++){
        if(x%knownPrimes[i]==0){
            ret=0;
            break;
        }
    }
    return ret;
}
  • 函数参数表中的数组实际上是指针
    • 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 a[]<==>int * const a=…

9.1.5 指针与CONST:指针本身和所指的变量都可能const

指针与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
这些是啥意思?
  1. int i;

  2. const int* p1=&i;

  3. int const* p2=&i;

  4. int *const p3=&i;

判断哪个被const了的标志是const在*的前面还是后面

(如果在*前面,即为所指的东西不能被修改,第2、3点是一样的。如果const在 *后面,表示指针不能被修改,第4点)

转换
  • 总算可以把一个非const的值转换成const的
void f(const int* x);
int a=15;
f(&a);//ok
xonst int b=a;

f(&a);//ok
b=a+1;//Error!
  • 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改
const数组
  • `const int a[]={1,2,3,4,5,6,};
  • 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
  • 所以必须通过初始化进行赋值
保护数组值
  • 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
  • 为了保护数组不被函数破坏,可以设置参数为const
    • int sum(const int a[],int length);

9.2.1 指针运算

1+1=2?
  • 给一个指针加1表示要让指针指向下一个变量

    int a[10];

    int *p=a;

    *(p+1)->a[1]

    *(p+n)->a[n]

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

指针计算
  • 这些算术运算可以对指针做:
    • 给指针加、减一个整数(+,+=,-,-=)
    • 递增递减(++/–)
    • 两个指针相减(给的不是地址的差,是地址之间有几个单元)
*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之前呢?
  • int *a=(int*)malloc(n*sizeof(int));
#include<stdio.h>
#include<stdlib.h>

int main(void)
{
	int number;
	int* a;
	int i;
	printf("输入数量:");
	scanf("%d",&number);
	//int a[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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾叶King

非常感谢您的支持和认同!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值