2.1 指针强化
强化1:指针是一种数据类型
1)指针变量也是一种变量,占有内存空间,用来保存内存地址
测试指针变量占有内存空间大小。
2)*p操作内存
在指针声明时,* 号表示所声明的变量为指针
在指针使用时,* 号表示操作指针所指向的内存空间中的值
- *p相当于通过地址(p变量的值)找到一块内存,然后操作内存
- *p放在等号的左边赋值(给内存赋值,写内存)
- *p放在等号的右边取值(从内存获取值,读内存)
3)指针变量和它指向的内存块是两个不同的概念。
规则1: 给p赋值p=0x1111; 只会改变指针变量值,不会改变所指的内容; p = p +1; //p++
规则2 :给*p赋值 *p='a'; 不会改变指针变量的值,只会改变所指的内存块的值
规则3 :*p= 表示 给内存赋值, = *p 表示取值,含义不同切记!
规则4 :保证所指的内存块能修改
4)指针是一种数据类型,是指它指向的内存空间的数据类型 。
int a;
int *p = &a;
p++;
指针步长(p++),根据所致内存空间的数据类型来确定。
p++ 等价于 (unsigned char )p+sizeof(a);
5 ) 当我们不断的给指针变量赋值,就是不断的改变指针变量(和所指向内存空间没有任何关系)。指针指向谁,就把谁的地址赋值给指针。
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//不断给指针赋值就是不断改变指针的指向
int main(void)
{
char buf[128];
int i;
char *p2 = NULL;
char *p1 = NULL;
p1 = &buf[0]; //不断的修改p1的值 相当于 不断改变指针的指向
p1 = &buf[1];
p1 = &buf[2];
for (i=0; i<10; i++)
{
//不断改变p1本身变量
p1 = &buf[i];
}
p2 = (char *)malloc(100);
strcpy(p2, "abcdefg1212333333333311");
for (i=0; i<10; i++)
{
//不断的改变p1本身变量,跟p1指向的内存块无关
p1 = p2+i;
printf("%c ", *p1);
}
return 0;
}
6 ) 不允许向NULL和未知非法地址拷贝内存。
强化2:间接赋值(*p)是指针存在的最大意义
*p间接赋值成立条件: 三大条件
- 条件一: 2个变量(通常一个实参,一个形参)
- 条件二:建立关系,实参取地址赋给形参指针
- 条件三:*p形参去间接修改实参的值
int num = 0;
int *p = NULL; // 条件一:两个变量
p = # // 条件二:建立关系
Num = 1;
*p = 2 ; // 条件三:通过* 操作符, 间接的给变量内存赋值
-
间接操作:从0级指针到1级指针
include <stdlib.h>
#include <string.h>
#include <stdio.h>
//使用一级指针
void getFileLen(int *p)
{
*p = 41; // p的值是file_len的地址 *p的地址间接修改file_len的值
//在被调用函数里面 通过形参 去 间接的修改 实参的值...
}
int getFileLen2()
{
int len = 100;
return len;
}
//不使用指针, 0级指针
void getFileLen3(int file_len)
{
file_len = 100;
}
//1级指针的技术推演
int main(void)
{
int file_len = 10; //条件1 定义了两个变量(实参 另外一个变量是形参p)
int *p = NULL;
p = &file_len; //条件2 建立关联
file_len = 20; //直接修改
*p = 30; //条件3 p的值是file_len的地址
// *就像一把钥匙 通过地址
// 找到一块内存空间 间接的修改了file_len的值
{
*p = 40; // p的值是a的地址 *a的地址间接修改a的值 //条件3 *p
printf("file_len: %d\n", file_len);
}
getFileLen(&file_len); //建立关联: 把实参取地址 传递给 形参
printf("getFileLen后 file_len: %d \n", file_len);
getFileLen3(file_len);
printf("getFileLen3后 file_len: %d \n", file_len);
return 0;
}
-
间接操作:从0级指针到1级指针
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void getMem(char *p2)
{
p2 = 0x80088008;
}
void getMem2(char **p2)
{
*p2 = 0x40044004; //间接赋值 p2是p1的地址
}
int main(void)
{
char *p1 = NULL;
char **p2 = NULL;
//直接修改p1的值
p1 = 0x11001100;
//间接修改p1的值
p2 = &p1;
*p2 = 0x10101010; //间接赋值 p2是p1的地址
printf("p1:%p \n", p1);
{
*p2 = 0x20022002; //间接赋值 p2是p1的地址
printf("p1:%p \n", p1);
}
getMem(p1);
getMem2(&p1);
printf("p1:%p \n", p1);
return 0;
}
函数调用时,形参传给实参,用实参取地址,传给形参,在被调用函数里面用*p,来改变实参,把运算结果传出来。
- 间接赋值的推论
用1级指针形参,去间接修改了0级指针(实参)的值。
用2级指针形参,去间接修改了1级指针(实参)的值。
用3级指针形参,去间接修改了2级指针(实参)的值。
用n级指针形参,去间接修改了n-1级指针(实参)的值。
- 间接操作:应用场景
正常: 条件一,条件二,条件三都写在一个函数里。
间接赋值:条件一,条件二写在一个函数里, 条件三写在另一个函数里
#include <stdio.h>
#include <string.h>
/* 间接赋值成立的三个条件
条件1 定义1个变量(实参)
条件2 建立关联:把实参取地址传给形参
条件3: *形参去间接地的修改了实参的值。
*/
void copy_str(char *p1, char *p2)
{
while (*p1 != '\0')
{
*p2 = *p1;
p2++;
p1++;
}
}
//间接赋值的应用场景
int main(void)
{
//1写在一个函数中 2作为形参建立关联 3 单独写在另外一个函数里面
char from[128];
char to[128] = {0};
strcpy(from, "1122233133332fafdsafas");
copy_str(from, to);
printf("to:%s \n", to);
return 0;
}
强化3:理解指针必须和内存四区概念相结合
1) 主调函数 被调函数
- a) 主调函数可把堆区、栈区、全局数据内存地址传给被调用函数
- b) 被调用函数只能返回堆区、全局数据
2) 内存分配方式
- a) 指针做函数参数,是有输入和输出特性的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
{
int ret = 0;
char *tmp1, *tmp2;
tmp1 = (char *)malloc(100);
strcpy(tmp1, "1132233");
//间接赋值
*mylen1 = strlen(tmp1); //1级指针
*myp1 = tmp1; //2级指针的间接赋值
tmp2 = (char *)malloc(200);
strcpy(tmp2, "aaaaavbdddddddd");
*mylen2 = strlen(tmp2); //1级指针
*myp2 = tmp2; //2级指针的间接赋值
return ret;
}
int main(void)
{
int ret = 0;
char *p1 = NULL;
int len1 = 0;
char *p2 = NULL;
int len2 = 0;
ret = getMem(&p1, &len1, &p2, &len2);
if (ret != 0)
{
printf("func getMem() err:%d \n", ret);
return ret;
}
printf("p1:%s \n", p1);
printf("p2:%s \n", p2);
if (p1 != NULL)
{
free(p1);
p1 = NULL;
}
if (p2 != NULL)
{
free(p2);
p2 = NULL;
}
return 0;
}
强化4:应用指针必须和函数调用相结合(指针做函数参数)
指针作为函数参数是研究指针的重点。
如果指针是子弹,那么函数就是枪管,子弹只有在枪管中才能发挥出威力。
一级指针典型用法:
一级指针做输入
int showbuf(char *p);
int showArray(int *array, int iNum);
一级指针做输出
int getLen(char *pFileName, int *pfileLen);
理解
- 输入:主调函数分配内存
- 输出:被调用函数分配内存
被调用函数是在heap上分配内存而非stack上
二级指针典型用法:
二级指针做输入
int main(int arc ,char *arg[]); //字符串数组
int shouMatrix(int [3][4], int iLine);
二级指针做输出
int Demo64_GetTeacher(Teacher **ppTeacher);
int Demo65_GetTeacher_Free(Teacher **ppTeacher);
int getData(char **data, int *dataLen);
int getData_Free(void *data);
int getData_Free2(void **data); //避免野指针
2.2 经典语录
1)指针也是一种数据类型,指针的数据类型是指它所指向内存空间的数据类型
2)间接赋值*p是指针存在的最大意义
3)理解指针必须和内存四区概念相结合
4)应用指针必须和函数调用相结合(指针做函数参数)
指针是子弹,函数是枪管;子弹只有沿着枪管发射才能显示它的威力;指针的学习重点不言而喻了吧。接口的封装和设计、模块的划分、解决实际应用问题;它是你的工具。
5)指针指向谁就把谁的地址赋给指针
6)C/C++语言有它自己的学习特点;若java语言的学习特点是学习、应用、上项目;那么C/C++语言的学习特点是:学习、理解、应用、上项目。多了一个步骤。
7) 理解指针关键在内存,没有内存哪来的内存首地址,没有内存首地址,哪来的指针。