六、指针
<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;
}