day5 -指针
指针和指针变量
- 指针就是地址,地址就是指针
- 地址就是存放单元的编号
- 指针变量是存放地址的变量
- 指针和指针变量是两个不同的概念,但是要注意,通常我们叙述时会把指针变量简称为指针,实际他们含义并不一样
指针热身-1
1 #include <stdio.h> 2 3 int main(void) 4 { 5 int * p; //p是变量的名字,int * 表示p变量存放的是int变量的地址 6 // int *p不表示一个名字叫做p的变量 7 //int*p应该理解为p是变量名,p变量的数据类型是int *类型 8 // 所谓 int *类型实际就是存放int变量地址的类型 9 int i=3; 10 int j; 11 p=&i; 12 /* 13 1.p保存了i的地址,因此p指向i; 14 2.p不是i,i也不是p,更准确的说,修改p的值不影响i的值,修改i的值也不影响p的值 15 3.如果一个指针变量指向了某个普通变量则 16 *的指针变量 就完全等同于 普通变量 17 例子: 18 如果p是个指针变量,并且p存放了普通变量i的地址 19 则p指向了普通变量i 20 *p 完全等同于 i 21 或者说 在所有出现*p的地方都可以替换成i 22 *p 就是以p的内容为地址的变量 23 */ 24 // p=i; //error,因为类型不一致,p只能存放int类型变量的地址,不能存放int类型变量的值 25 j = *p; //等价于j= i 26 printf("i=%d,j= %d",i,j); 27 return 0; 28 }
指针的重要性:
- 表示一些复杂的数据结构
- 快速的传递数据
- 使函数返回一个以上的值
- 能直接访问硬件
- 能够方便出来字符串
- 是理解面向对象语言中引用的基础
总结:指针是c语言的灵魂
指针的定义
地址:内存单元的编号
从零开始的非负整数
范围:4G【0-4G】
指针:指针就是地址,地址就是指针
指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量
指针的本质就是一个操作受限的非负整数
指针的分类
- 基本类型指针
- 指针和数组
- 指针和函数
- 指针和结构体
- 多级指针
经典指针程序—互换两个数字
1 // 互换两个数字的值 2 #include <stdio.h> 3 void huhuan_2(int * p,int * q) //*p=a *q=b 4 { 5 int t; //如果要互换*p和*q的值,则t必须定义成int,不能定义成int *,否则语法出错 6 t=* p ; //p是int *,*p是int 7 * p= * q; 8 * q= t; 9 return ; 10 } 11 /*void huhuan_1(int a,int b) //不能完成互换功能 12 { 13 int t; 14 t=a; 15 a=b; 16 b=t; 17 return ; 18 }*/ 19 int main(void) 20 { 21 int a=3; 22 int b=5; 23 huhuan_2(&a,&b); 24 printf("%d %d\n",a,b); 25 return 0; 26 }
附注:
*的含义
1.乘法
2.定义指针变量
int * p //定义了一个名字叫p的变量,int *表示p只能够存放int变量的地址
3.指针运算符
该运算符放在已经定义好的指针变量的前面
如果p是一个已经定义好的指针变量,则*p表示以p的内容为地址的变量
如何通过被掉函数修改主调函数普通变量的值?
1.实参必须为该普通变量的地址
2.形参必须为指针变量
3.在被掉函数中通过
*形参名
的方式就可以修改朱掉函数相关变量的值
2.指针和数组
指针和一维数组
一维数组名是个指针常量,它存放的是一维数组和第一个元素的地址
1 // 一维数组名是个指针常量,它存放的是一维数组和第一个元素的地址 2 #include <stdio.h> 3 int main(void) 4 { 5 int a[5]; 6 printf("%#x\n",&a[0] ); 7 printf("%#x\n",a); 8 return 0; 9 } 10 /* 在vc6.0中的结果为: 11 0x18ff34 12 0x18ff34
下标和指针的关系
如果p是个指针变量,则 p[i]永远等价于*(p+i)
确定一个一维数组需要几个参数
需要两个参数
1.数组第一个元素的地址
2.数组的长度
1 #include <stdio.h> 2 //f函数可以输出任何一个一堆数组的内容 3 void f(int * p,int q) 4 { 5 int i; 6 // for (i=0;i<q;++i) 7 for (i =0;i<q;++i) 8 printf("%d\n",*(p+i)); //*(p+i)等价于 p[i] 也等价于 a[i]也等价于*(b+i) 9 } 10 /* 11 p[i]的意思就是从地址p开始向后偏移i个单位后的地址空间内的值 12 *(p+i) 也是上面的意思 13 []实际上就是个地址偏移操作符 ,c语言编译器就是把p[i]转换成*(p+i)处理的,p[i]和*(p+i)无条件等价 14 比如有数组int p[4]={1,2,3,4}; 15 p就是这个数组的首地址 16 p[2]就是从p开始偏移2个int单位后的地址内的值 就是3 17 *(p+2) 这个更好理解,p+2就是在p的基础上+2个int单位的地址,那么*(p+2)就是这个地址内的值:3 18 */ 19 int main(void) 20 { 21 int a[5]={1,2,3,4,5}; 22 23 f(a ,5); //a是int * 24 return 0; 25 }
1 #include <stdio.h> 2 void f(int * p, int q) 3 { 4 p[3]=88; 5 } 6 int main(void) 7 { 8 int a[6] ={1,2,3,4,5,6}; 9 printf("%d\n",a[3]); 10 f(a ,6); 11 printf("%d\n",a[3]); 12 return 0; 13 } 14 /*在vc6.0中的结果为 15 4 16 88
指针变量的运算
指针变量不能想加 不能想乘 不能相除
如果两个指针变量指向的是同一块连续空间中的不同存储单元 则这两个指针变量才可以相减
1 #include <stdio.h> 2 int main(void ) 3 { 4 int a[5]; 5 int * p; 6 int * q; 7 p=&a[1]; 8 q=&a[4]; 9 printf("p和q所指向的单元相隔%d个单元\n",q-p); 10 return 0; 11 } 12 //在vc6.0中的结果为:p和q所指向的单元相隔3个单元
一个指针变量到底占几个字节
假设p指向char类型变量(1个字节)
假如q指向int类型变量(4个字节)
假如r指向double类型变量(8个字节)
p,q,r本身所占的字节数是一样的
指针和二维数组
动态内存分配
传统数组的缺点:
1.数组长度必须事先指定,且只能是常整数,不能是变量 例子: int a[5] // ok int len;int a [len] //error
2.传统形式定义的数组,该数组的内存程序员无法手动释放
在一个函数运行期间,系统为该函数中所非配的内存会一直存在,直到该函数运行完毕时,该数组才会被系统释放
3.数组的长度一旦定义,其长度就不能再更改
数组的长度不能再函数运行的过程中动态的扩充或缩小
4.A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕之后,A函数中的数组将无法再被其他函数使用
传统方式定义的数组不能跨函数使用
为什么需要动态分配内存
动态数组很好的解决了传统数组的这4个缺点
传统数组也叫静态数组
动态分配内存举例—动态数组的构造
molloc函数
1 #include <stdio.h> 2 #include <malloc.h> 3 int main(void ) 4 { 5 int i = 5; //分配了4个字节,静态分配 6 int * p=(int *)malloc(4); //12行 7 /* 8 1.要使用molloc函数,必须要加malloc.h这个头文件 9 2.malloc函数只有一个形参,并且形参是整型 10 3.4表示请求系统为本程序分配4个字节 11 4.malloc函数只能返回第一个字节的地址 12 5.12行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节 13 6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的 14 */ 15 * p =5; //*p代表就是一个int变量,只不过*p这个整型变量内存分配和11行的i变量分配方式不同 16 freee (p);//表示把p所指向的内存给释放掉 17 printf("同志们好!"); 18 return 0; 19 }
静态内存和动态内存的比较
跨函数使用内存的问题