1,指针就是地址,每个字节都有对应的地址,指针变量本质就是个变量存放的是地址编号,任何地址编号都是四个字节,也就是sizeof(int * char* double ***)都是四个字节
2,如何定义一切指针
1、*修饰 指针变量名 // *p
2、保存啥类型变量的地址 就用该类型定义一个普通变量。 // int num
3、从上 往下 整体 替换 // int *p
3,*p什么意思?
*p表示取p保存的地址编号对应的空间内容。*p等价于num
4,指针变量的类型
1,指针变量自身类型(p是什么类型) 只将指针变量涂黑,剩下啥类型,指针变量自身就是啥类型。 比如 p自身的类型是int *
推广,int num,num的类型是?把num涂黑
2,指针变量所指向的类型? 将指针变量和最近的一个*一起涂黑剩下啥类型就指向啥类型 p指向的类型为int类型 == p保存int类型变量的地址
即指向XXXX类型 == 保存XXX类型变量的地址
5 指针变量的初始化
//如果 局部 指针变量 不初始化 保存的是随机的地址编号(千万别取值) int *p;
//不想让指针变量指向任何地方 应该初始化为NULL(千万别取值) //#define NULL ((void *)0)
int *p1 = NULL;
//将指针变量初始化为合法的地址(可以取值) int *p2=&a;
//千万别看成 *p2 = & num; 是先 int * p2; p2 = &a *修饰p2为指针变量
6,&&与**的区别
//如果对一个变量取地址 整个表达式的类型 就是变量的类型+*.
//p的类型是 int *类型
//*p的类型是 int 类型
//如果对指针变量取* 整个表达式的类型是 指针变量的类型‐*.
//高级总结:如果&和*同时存在 可以相互抵消(从右‐‐>左)。(重要)
7,数组与指针
1, []与*()关系 //在使用中 本质:[] 是*( ) 缩写
//缩写规则:+左边的值 放在[]左边边 +右边的值 放在[]里面
arr[i]=*(arr+i)
2,arr代表的是 第0个元素的地址(&arr[0])
//&arr[0] == &*(arr+0) == arr+0 == arr
//所以:数组名arr代表第0个元素的地址,&arr是数组的首地址
arr+1代表首元素地址+1,跳过了一个元素(4B)
&arr+1代表是数组的首地址+1,跳过整个数组(4*5B)
3,数组名arr是一个符号常量,不能被赋值(不能放在等号左边)
8,指针数组:本质就是数组,只是数组的每个元素都是指针
1,定义,就是数组的定义,int arr[5]
*arr
int *arr[5]
int *arr[3]={&num1,&num2,&num3} 这个数组有三个元素,每个元素是什么类型?
把arr[3]涂黑,剩下int *,每个元素是int *
2, 指针数组保存字符串
char * arr[3]={"hehe","haha","heihei"};
数组每个元素保存的是字符串首元素的地址,即arr[0]保存的是‘h’的地址
而字符串储存在文字常量区,是只读的
那如何打印字符串?
printf("%s",arr[1])
如何打印arr[1]中的字符a,printf("%c", *(arr[1]+1) )首元素的地址加一,再解引用
void test12()
{
int arr[5]={10,20,30,40,50};
int *p = arr;
printf("%d\n", *p++);//10
++运算级搞,但是因为是后自增,所以先算*p,再把p++,此时,*p=20
printf("%d\n", (*p)++);//20
先算*p就是20,然后再20++,(*p)++等价于(*p)=(*p)+1,此时*p等于21
printf("%d\n", *(p++));//21
}
9,数组指针 本质就是指针,保存的是数组的首地址,p=&arr
1,定义,就是定义一个指针,*修饰变量名*p 定义一个变量int arr [5] 替换int(*p)arr[5]
*修饰p为指针变量,指针变量保存什么类型的地址?把*p涂黑,保存的是数组的地址
是数组的地址(&arr)不是数组首元素的地址(&arr[0])
该数组有五个元素,每个元素是什么类型?
2,p=&arr,p+1跳过4*5个字节
如何利用p取出arr[3] 40?
如果p是=&arr[0]也就是p=arr,只要*(p+3),但是p=&arr故同时解引用,*p=arr
故arr[3] = *(*p+3)
睁大眼睛,*p看成*(p+0)=p[0], 见到*()想到变成p[]
*(p[0]+3)= p[0]p[3] 也就是说一维数组看成只有一行的二维数组
10,二维数组 int arr[3][4];
1,二维数组名代表行地址,加一跳过一行 arr+1就是第1行地址
2,对行地址取*就代表当前行第零列的列地址,*(arr+1)就是第一行第零列地址,再对他加一加二就可以取这一行其他元素的地址
3 ,组合技,如何取出第一行第二列的元素?
*(arr+1)第一行第零列元素地址,*(arr+1)+2,在对他解引用,*(*(arr+1)+2)
化简,== *(arr[1]+2)==arr[1][2]
4,案例, int arr[3][4]
*arr+2 第零行第二列的列地址
arr[1] 等于*(arr+1)第一行第零列的列地址
&arr[0]+2 化简==&*(arr+0)+2==arr+2第二行的行地址
**arr 化简==*(*(arr+0)+0)==arr[0][0]
11,数组指针与二维数组的关系
1,定义一个指针变量,保存二维数组的行地址 int arr[3][4]
数组指针 int (*p)[4] p=arr p完全等价与arr
2,二维数组的行数 int row = sizeof (arr)/sizeof(arr[0])
二维数组的列数 int col=sizeof(arr[0]/sizeof(arr[0][0]))
12,指针变量作为函数参数
1,如果想在函数内部修改外部变量的值,就要将外部变量的地址传递给函数(以指针变量作为函数的参数)
13,一维数组名做函数参数
1,如果函数内部想操作外部数组元素,请将外部数组的数组名传递给参数
2,一维数组作为函数的形参,会被优化为一级指针
int arr[5] int arr[]----->int *p
int arr[3][4]----->int(*p)[4]
int arr[3][4][5]------->>int(*p)[4][5]
14,函数指针
1,定义, *p int func (int a , int b)替换得到,int (*p)(int a,int b)
2,函数名代表函数入口地址,就是个地址 p=func
3,对函数指针取*****无意义
14,malloc和free函数
1 #include
2 void *malloc(unsigned int num_size);
3 形参:num_size需要申请空间大小的字节数。
4 返回值:
5 成功:返回空间的起始地址
6 失败:NULL
7 特点:
1、对于malloc的返回值 一般要强制类型转换
2、malloc申请的空间 内容不确定 一般使用memset进行清空
3、多次调用malloc 第1次malloc 和 第2次malloc的地址不一定连续
案例
void test02()
2 {
3 int *addr = NULL;
4
5 addr = (int *)malloc(sizeof(int));
6 if(addr == NULL)//申请失败了
7 {
8 printf("malloc err\n");
9 return;
10 }
11
12 printf("*addr=%d\n", *addr);//不确定的值
13
14 //对堆区空间 清0
15 memset(addr, 0, sizeof(int));
16 printf("*addr=%d\n", *addr);//0
17
18 //对addr的空间 就行写 或 读
19 *addr = 1000;//写
20
21 printf("*addr=%d\n", *addr);//1000 读
22
23
24 //释放堆区空间 空间使用权限的回收 是否对空间内容清0 这是不确定的
25 free(addr);
26 }
27 int main(int argc,char *argv[])
28 {
29 test02();
30 return 0;
31 }