11月29日 ——12月5日学习总结

六、指针

<1>指针的初等介绍(举个例子)

#include<stdio.h>
int main()
{
	int *p;//不表示定义了一个名字叫做*p的变量,p是变量名,p变量的数据类型是int*类型
	//所谓int*类型就是存放int变量地址的类型(p只能存放地址) 
	int i=3;
	int j; 
	p=&i;//指p保存了i的地址,因此p指向i 
	//p=i;因为类型不一致,p只能存放int类型变量的地址,不能存放int类型的值 
	//p=65;
	j=*p;
	printf("i=%d,j=%d,*p=%d\n",i,j,*p);
	return 0;
 } //输出:i=3,j=3

【注意:!!】

1、p=&i;指p保存了i的地址,因此p指向i

2、p不是i,i也不是p。更准确的说,修改p的值不影响i的值,修改i的值也不影响p的值。

3、如果一个指针变量指向了某个普通变量,则*指针变量就完全等同于普通变量

eg:如果p是个指针变量,并且p存放了普通变量i的地址,则p指向了普通变量i

*p就完全等同于i

或者说:在所有出现*p的地方都可以替换成i,在所有出现i的地方都可以替换成*p

<2>指针的重要性

1、表示一些复杂的数据结构

2、快速的传递数据

3、使函数返回一个以上的值

4、能直接访问硬件

5、能够方便的处理字符串

6、是理解面向对象语言中引用的基础

<3>指针与指针变量

指针就是地址,地址就是指针,地址就是内存单元的编号

指针变量是存放地址的变量

(*p就是以p的内容为地址的变量,*p和取地址为逆运算)

指针和指针变量是两个不同的概念,但是要注意,通常我们叙述时会将指针变量称为指针,但含义不同!!!

<4>什么是地址 

地址就是内存单元的编号,从零开始的非负整数

存储数据的方式:

内存条中的数据读入CPU,CPU进行处理,再将读入的数据写入内存条,当内存条中的数据达到一定时再传入硬盘

指针的本质就是一个操作受限的非负整数

<5>程序举例(常见错误)

#include<stdio.h>
1  int main()
{
	int* p;//*p表示以p的内容为地址的变量 
	int i=5;
	*p=i;//p无指向,*p代表的是一串垃圾值,将i=5赋给了未知的存储单元就会报错 
	printf("%d\n",*p);
	return 0;
}
2   int main()
{
	int i=5;
	int* p;
	int* q;
	p=&i;
	*q=p;//*q是int类型,p是int*类型,不能相互转换
	p=q//如果是这样,那么q是垃圾值,q赋给p,p也变成垃圾值
	   //q的空间是属于本程序的,所以本程序可以读写q的内容
	   //但是如果q的内部是垃圾值则本程序不能读写*q所代表的内容
	   //因为此时*q所代表的内存单元的控制权限并没有分配给本程序 
	printf("%d\n",*q);
	return 0;
 } 

<6>举个例子

互换两个数字

#include<stdio.h>
/*void huhuan_1(int ,int ); 
void huhuan_2(int *,int *);*/
void huhuan_3(int *,int *);
int main()
{
	int a=3;
	int b=5;
	huhuan_3(&a,&b);
	printf("a=%d,b=%d\n",a,b);
	return 0;
 } 
/* 1不能完成转换
void huhuan_1(int a,int b)
{
	int t;
	t=a;
	a=b;
	b=t;
	return;//形参中的a,b和主函数中的a,b不是同一个a,b
	//当子函数执行完毕后,给形参a,b,t分配的存储空间全部被释放
	//程序又回到主函数,此时a,b的值不变仍为3,5 
} 
// 2不能完成转换
void huhuan_2(int *p,int *q)
{
	int *t;//如果要互换p和q的值则t必须是int*不能是int 
	t=p;
	p=q;
	q=t;//改变的是p,q的值,不是*p*q的值,*p和a,*q和b仍是两个不同的值 
} */
// 3可以完成互换
void huhuan_3(int *p,int *q)
{
	int t;
	t=*p;
	*p=*q;
	*q=t;
	return;
} //执行完子函数p,q的值被释放,而*p,*q不会,所以a,b的值被成功改变 

形参和实参永远不是同一个变量!!! 

#include<stdio.h>
int f(int i)
{
	i=99;
	return i;
}
int main()
{
	int i;//=6;
	//printf("%d\n",i);
	f(i);
	printf("%d\n",i);
	return 0;
 } 

<7>*的三种含义

1、乘法

2、定义指针变量

int *p;//定义了1个名字叫做p的变量

int *表示p只能存放int变量的地址

3、指针运算符

该运算符放在已经定义好的变量的前面,如果p是一个已经定义好的指针变量,则*p表示以p的内容为地址的变量

<8>通过指针可以使变量返回一个以上的值

如果没有指针:

#include<stdio.h>
f(int i,int j)
{
    return 100;
}
int main(void)
{
    int a=3,b=5;
    a=f(a,b);
    b=f(a,b);
    rweturn 0;//只能返回100这一个值
}

使用指针后:

#include<stdio.h>
void g(int *p,int *q)
{
    *p=1;
    *q=2;
}
int main(void)
{
    int a=3,b=5;
    g(&a,&b);
    printf("%d %d \n",a,b);
    return 0;
} 

执行完子函数后pq的值会被释放,而*p,*q不会,所以a,b的值被成功改变

如何通过被调函数修改主调函数中普通变量的值

1、实参必须是该普通变量的地址

2、形参必须是指针变量

3、在被调函数中通过

*形参名=——

的方式就可以修改主调函数相关变量的值

 <9>类型不同的指针和变量相互赋值的问题
 

#include<stdio.h>
int main()
{
	int * p;
	int i=5;
	char ch='A';
	//p=&ch;[错误!!一个为整型一个为字符型不能实现取地址
	//相同类型的错误还有p=ch和p=5(一个是地址一个是整型变量) 
	p=&i;
	*p=99;
	printf("i=%d,*p=%d",i,*p);
	return 0;
}//i=99,*p=99

<10>指针和数组

1、一维数组名

一维数组名是一个指针常量,它存放的是一维数组第一个元素的地址

#include<stdio.h>
int main()
{
	int a[5];
//int a[3][4];3行4列a[0][0]为第一个元素,a[i][j]为第i+1行j+1列
	printf("%#x\n",&a[0]);
	printf("%#x\n",a);
	return 0;
}//0x62fe00
//0x62fe00 

2、f函数可以输出任何一个一维数组的内容 

#include<stdio.h>
void f(int *pArr,int len)
{
	pArr[3]=88;//pArr[3]=*(pArr+3)
}
int main()
{
	int a[6]={1,2,3,4,5,6};
	printf("%d\n",a[3]);
	f(a,6);
	printf("%d\n",a[3]);
	return 0;
}

*(pArr+i)=pArr[i]=a[i]=*(a+i)

pArr[3]与a[3]是同一个变量,pArr指向a[] 

确定1个一维数组2个参数缺一不可!!!

数组第一个元素地址

数组的长度

#include<stdio.h> 
void f(int *pArr,int len)
{
	int i;
	for(i=0;i<len;++i)
		printf("%d ",*(pArr+i));
	printf(" \n");
}
int main()
{
	int a[5]={1,2,3,4,5};
	int b[6]={-1,-2,-3,-6};
	int c[100]={1,99,22,33};
	f(a,5);
	f(b,6);
	f(c,100);
	return 0;
}

 3、用下标法访问数组=用指针法访问数组

#include<stdio.h>
int main()
{
	int a[5],*p,i;
	printf("InPutfive numbers:");
	p=a;
	for(i=0;i<5;i++)
	{
		scanf("%d",&p[i]);//&a[i]
	}
	p=a;//这里的p=a可有可无,因为p没有实现自加,未向下移动 
	for(i=0;i<5;i++)
	{
		printf("%4d",p[i]);
	}
	return 0; 
}

利用指针可在另一个函数中直接对main函数进行操作,改变一个数的值只能通过发送地址来实现

(即数组名)

#include<stdio.h>
void OutArr(int *pArr,int len)
{
	int i;
	for(i=0;i<len;++i)
	{
		printf("%d\n",pArr[i]); 
	}
}
int main(void)
{
	int a[5]={1,2,3,4,5}; 
	OutArr(a,5);
	return 0;
}

4、指针变量的运算

指针变量不能相加  不能相乘  也不能相除

如果两个指针变量所指向的是同一块连续空间中不同的储存单元,则这两个指针变量才可以相减!!!

#include<stdio.h>
int main()
{
	int i=5;
	int j=10;
	int *p=&i;
	int *q=&j;
	int a[5];
	p=&a[1];
	q=&a[4];
	printf("p和q所指向的单元相隔%d个单元\n",q-p);
	return 0;	
}

 七、动态内存分配

<1>传统数组的缺点

1、数组长度必须事先制定,且只能是常整数,不能是变量

eg:int a[5];

        int len=5; int a[len];//错误

2、传统形式定义的数组,该数组的内存程序员无法手动释放

eg:

#include<stdio.h>
void f()
{
    int a[5]={1,2,3,4,5};
}//20个字节的储存空间程序员无法手动编程释放它,它只能在本函数运行完毕时由系统自动释放

 在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放。

3、数组的长度不能在函数运行的过程中动态的扩充或缩小,数组的长度一旦定义,其长度就不能再更改。

4、在A函数中定义的数组,在A函数运行期期间可以被其他函数使用,但A函数运行完毕后,AA函数中的数组将无法再被其他函数使用。【传统定义的数组不能跨函数使用!!!】 

#include<stdio.h>
void g(int *pArr,int len)
{
    pArr[2]=88;
}
void f()
{
    int a[5]={1,2,3,4,5};
    printf("%d\n",a[2]);
}
int main()
{
    f();
    return 0;
}

<2>动态内存的分配

malloc函数的介绍【memory(内存)allocate(分配)的缩写】

#include<stdio.h>
#include<malloc.h>
int main() 
{
	int i=5;
	int *p=(int*)malloc(4);//此行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
	*p=5;//*p代表的就是一个int型变量,只不过*p这个整型变量和i的分配方式不同
	free(p);//free(p)表示将p所指向的内存给释放掉,p本身的内存是静态的
	printf("haha!\n");
	return 0;
} 

1、使用malloc函数,必须添加malloc.h这个头文件

2、malloc函数只有一个形参,并且形参是整型

3、4表示请求分配4个字节

4、malloc函数只能返回第一个字节的地址

5、p本身所占的内存是静态分配的,p所指向的内存也占4个字节

#include<stdio.h>
#include<malloc.h>
void f(int*q)
{
	*q=200;
	//free(q);会把q所指向的内存给释放掉,会导致返回垃圾值 
}
int main()
{
	int*p=(int*)malloc(sizeof(int));
	*p=10;
	printf("%d\n",*p);
	f(p);//p是指针变量 
	printf("%d\n",*p);
	return 0; 
 } 

 在一个函数中分配了一块内存,在另一个函数中对这块内存进行处理,可以赋值可以释放

#include<stdio.h>
#include<malloc.h>
int main()
{
	int a[5];
	int len;
	int*pArr;
	int i;
	//动态的构造一维数组
	printf("请输入你要存放的元素的个数:"); 
	scanf("%d",&len);
	pArr=(int*)malloc(4*len);//构造动态一维数组成功=int pArr[len]; 
                            //该数组的数组名是pArr,长度是len,类型是整型 
    //对一位数组进行操作
	for(i=0;i<len;++i)
		scanf("%d",&pArr[i]);
	//对一维数组进行输出
	printf("一维数组的内容是:\n");
	for(i=0;i<len;++i)
		printf("%d\n",pArr[i]);
		free(pArr); 
	return 0;
}

 动态内存和静态内存的比较:

静态内存是由系统自动分配,系统自动释放

静态内存是在栈分配的

动态内存是由程序员手动分配,手动释放

动态内存是在堆分配的

 <3>多级指针

#include<stdio.h>
/*int main(void)
{
	int i=10;
	int*p=&i;
	int**q=&p;
	int***r=&q;//r=&p error,因为r是int***类型,r只能存放int**类型变量的地址,p是int*类型变量的地址 
    printf("%d\n",**r); 
	return 0; 
}*/
void f(int**q)
{
	//*q就是p 
}
void g()
{
	int i=10;
	int *p=&i;
	f(&p);
}//p是int*类型
int main(void)
{
	g();
	return 0;
}

  【跨函数使用内存:】

静态变量不能跨函数使用

动态变量可以跨函数使用

/*#include<stdio.h>
void f(int **q)
{
	int i=5;
	*q=&i;
}
int main()
{
	int *p;
	f(*p);
	printf("%d\n",*p);
	return 0;
}*/
//动态变量可以跨函数访问内存
#include<stdio.h>
#include<malloc.h>
void f(int **q) 
{
	*q=(int*)malloc(sizeof(int));
	**q=5;
}
int main(void)
{
	int *p;
	f(&p);//&p=**p
	printf("%d\n",*p);
	return 0; 
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值