-
指针重要性:
表示一些复杂的数据结构
快速的传递数据
使函数返回一个以上的值
能直接访问硬件
能方便的处理字符串
是理解面向对象语言中引用的基础
c语言的灵魂
-
指针的定义
-
地址:
-
内存单元的编号
-
从零开始的非负整数
-
范围:
-
-
指针:
-
指针就是地址,地址就是指针
-
指针变量就是存放内存单元编号的地址,活着说指针变量就是存放地址的变量
-
指针和指针变量是两个不同的概念
-
但是要注意的是:通常我们叙述会把指针变量简称为指针
-
指针的本质就是操作受限的非负整数(为什么这么说,因为非负整数可以加减乘除,而指针不能加乘除,只能相减)
-
指针的分类:
1基本类型的指针
int * p;//p是变量名字, int* 表示存放的存放的是int类型的数据
int i =3;
p=&i;//p是指针变量,并且p存放了普通变量i的地址,所以说p指向了普通变量i ,所以*p=i;或者说所有出现*p的地方都可以替代成i,所有出现i的地方都可以替代成 *p,*p就是以p的内容为地址的变量
常见的错误写法
错误写法1:
int * p;
int i =5;
*p=i;//错误:因为*p就是以p的内容为地址的变量,但是p没有赋值,就是垃圾值,所以这里*p就是以这个垃圾值为地址的这个一个单元,这个地址我们并不知道,那么你要把i的值给这个我们不知道的单元,那么就会奔溃
错误写法2:
int i =5;
int*p;
int *q;
p=&i;
*q=p;//同上的一个原因是q是没有赋值,*q是垃圾值,原因二是*q和p的数据类型不一致
错误写法2:
int i =5;
int*p;
int *q;
p=&i;
q=q;//这个没错,q是垃圾值,q赋值给p,p也变成垃圾值
printf(@"%d",*q);//这个就是错的,q的空间数据本程序,所以本程序可以读写,但是如果q内部是垃圾值,则本程序不能读写*q的内容,因此此时 *q所代表的内存单元的控制权限并没有分配给本程序,所以本程序运行到此行就会出错
附注:* 的含义
1 乘法
2 定义指针变量: int * p;//定义了一个名字叫p的变量,int表示p只能存放数据类型为int类型的地址
3 指针运算符,该运算符放在已经定义好的指针变量前面,如果p是一个定义定义好的指针变量,则*p表示以p的内容为地址的变量
2.指针和数组
1.指针和一维数组
#####数组名:
一维数组名是个指针常量,存放的是数组首个元素的地址,因为它是常量,所以不能被改变
#####下标和指针的关系
如果p是个指针变量,则p[i] 永远等价于 *(p+i)
确定一个一维数组需要几个参数[如果一个函数要处理一个一维数组,则需要接受该数组的哪些信息],答案是需要两个参数:第一个是数组第一个元素的地址,第二个是数组的长度
#####指针变量的运算
指针变量不能相加不能相乘也不能相除,只能减
如果两个指针变量指向的是同一块连续空间中的不同储存单元,则这两个指针变量才可以相减,一般只能是数组
int i =5;
int j=10;
int a[5];
p=&a[1];
q=&a[4];
printf("相差几个单元:%d",q-p);//3
#####一个指针到底占几个字节
假设p指向char类型变量(char占一个字节)
假设q指向int类型变量(int占四个字节)
假设r指向doubel类型变量(doubel占八个字节)
那么p q r本身所占的字节数是否一样
答案是:q p r本身所占字节数都是一样的,四个字节
总结:
1.一个指针变量,无论他指向的变量占几个字节,该指针本身只占四个字节,
2.一个变量的地址使用该变量的首字节的地址表示
原理是这样,比如q指向int类型变量,其他p只保存了int所在四个字节内存的第一个字节,至于只保存了第一个字节但是却能储存四个字节的数据,这是由数据类型int决定,就是从p保存的第一个字节开始往后计算四个字节,对于doubel float也是一样的,但是为什么p本身占四个字节,这是因为,机器是32位的,也就说地址总线有32跟,那么最大地址的表示为2的32次方,也就是4个字节的表示方式,p表示其实都是内存单元,既然内存的最大单元为2的32次方法,那么它本身的最大值也应该为2的32次方
2.指针和二维数组
3.指针和函数
4.指针和结构体
-
结构体定义
为什么需要结构体:为了表示一些复杂的事物,而普通的基本类型无法满足实际需求
什么叫结构体:就是把一些基本类型组合在一起的形成的新的数据类型叫做结构体
```
//定义一个结构体的数据类型
struct Student{
int age;
float score;
char sex;
};
struct student stu={12,"edison"};
NSLog(@"age =%d name=%s",stu.age,stu.name);
定义结构体的方式
方式1:
struct Student{
int age;
float score;
char sex;
};
方式2:
struct Student{
int age;
float score;
char sex;
}stu;//在定义的同时直接定义变量名,这样不好,因为只能定义一次
方法3:
struct{
int age;
float score;
char sex;
}stu;//也不好
* ######结构体的初始化
初始化方法
方法1
struct student st ={12,"edison"};
方法2;
struct student st ;
st.age=12;
st.name="edison";
如何取出结构体的每个成员
/*
结构体变量名.成员名
指针变量名->成员名
/
struct student st={12,"edison"};
st.age=12;
st.name="name";
struct student * pst=&st;
pst->age=34;
pst->name="edison"//pst->age 在计算机内部会转化成(pst).age,
//所以 pst->age 等价于st.age,也等价于(*pst).age
* ######结构体运算
结构体变量可以相互赋值
struct student st={12,"edison"};
st.age=12;
st.name="name";
struct student st2=st;//是可以的
#####5.多级指针
* ####专题
#####1.动态内存分配
* ######传统数组的缺点:
1.数组长度必须事先指定,且只能是常整数,不能是变量
2.传统形势定义的数组,该数组的内存程序员无法手动释放,只能等本函数运行完毕由系统释放
3.数组的长度一旦定义,其长度就不能更改,数组的长度不能在函数运行的过程中动态的扩充或者缩小
4.a函数定好的数组,在a函数运行期间可以被其他函数调用,但是a函数运行完毕之后,a函数中数组将无法给其他函数使用
* ######为什么需要动态分配内存
就是因为动态数组很好的解决了传统数组的四个缺陷
int * p =(int )malloc(100);
/
malloc函数只有一个形参,就是需要分配的字节数
malloc函数其实只能返回分配好的内存的第一个字节的地址,所以在malloc前加入(int *)强制转化,就是为了表示把第一个字节的地址强制转化成整形变量的地址,那么就说明这100个字节得按照4个字节一一划分,
p本身所占的内存是静态分配的,p所指向的内存是动态分配的
free(p);//把p指向的内存给释放掉,p本身的内存是静态的,不能由程序员手动释放,把p指向的内存释放掉后就不能读写这块内存上的值了
*/
######1