前言:
在前面基础部分其实有很多内容没有涉及到,是因为我觉得有些知识,如果不摊开细讲的话,和没说差不多.所以就把一部分内容索性安排到了进阶部分.接下来的内容还接续上次的风格.增加了"思考一下",希望大家看到问题,可以多思考,拒绝思维惰性.
指针专题
指针"9铁律"
铁律1:指针也是一种数据类型
1)指针也是一种变量,占有内存空间,用来保存内存地址
2)*p操作内存
在指针声明时,*号表示所指明的变量为指针
在指针使用时,*号表示 操作 指针所指向的内存空间中的值
*p相当于通过地址找到一块内存,然后操作内存.
*p放在等号的左边赋值(给内存赋值)
*p放在等号的右边赋值(从内存获取值)
3) 指针变量和它指向的内存块是两个不同的概念
4)指针是一种数据类型,是它所指向的内存空间的数据类型
指针步长(p++),根据所致空间的数据类型来确定
指针的步长,根据所指内存空间类型来定
指针指向谁,就把谁的地址赋值给指针
void main()
{
//测试指针变量占内存空间大小
int a = 10;
char *p1 = 100;
char ****p2 = 100;
printf("a:%d,p1:%d,p2:%d", sizeof(a), sizeof(p1), sizeof(p2));
int *p3 = NULL;
p3 = &a;
*p3 = 20;//间接修改a的值 *就像一把钥匙,通过一个地址,去修改a变量的内存空间
{
int c = 0;
c = *p3;
//左边读内存,右边取内存
printf("c:%d\n", c);
}//不断的给指针赋值,就是不断的改变指针的指向
{
char *p4 = NULL;
p4 = (char*)malloc(100);
p4 = (char*)malloc(200);
}
printf("a:%d,p1:%d,p2:%d", sizeof(a), sizeof(p1), sizeof(p2));
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
char *getStr81()
{
char *tmp = NULL;
tmp= "asdfdffg";
return tmp;
}
/*
int getABC1(char*p1);
int getABC2(char**p2);
int getABC3(char***p3);
int getABC4(char(*p4)[30]);
int getABC4(char p5[10][30]);
指针做函数参数 形参有多级指针的时候
站在编译器的角度,只需要分配4个字节的内存(32bit)
当我们使用内存的时候,我们才关心指针所指向的内存,是一维的还的二维的
*/
void main()
{
char *p = *getStr81();
printf("p:%s\n", p);
*(p + 2) = 'r';//保证所指的内存块,可以被修改
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
铁律1强化
/*
向NULL地址处copy数据
void main()
{
char *p1 = NULL;
p1 = 0x00077;
strcpy(p1, "asdfgg");//指针所指向的内存空间拷贝数据
会发生冲突,原因:那块内存是系统保护的
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
*/
void main()//不断改变指针指向
{
char buf[128];//c语言可以在栈上分配内存
int i;
int j=0;
char *p2 = NULL;//在做一个堆上的
char *p1 = NULL;
p1 = &buf[0];//数组首元素
p1 = &buf[1];
p1 = &buf[2];
for (i = 0; i < 10; i++)//这也是不断的在改变指针指向
{
p1 = buf[i];
}
p2 = (char*)malloc(100);
strcpy(p2, "abcdef213233213");
for (i = 0; i < 10; i++)
{
p1 = p2[i];
printf("%c", *p1);
}
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
思考一下
int j=0;//0放在哪个区?
for (i = 0; i < 10; i++)//10放在哪个区?
铁律2:间接赋值是指针存在的最大意义
1)指针变量和它所指向的内存块变量是两码事
2)间接赋值3条件
2个变量(通常一个实参,一个形参)
建立关系(实参取地址赋给形参指针)
形参去间接修改实参的值
间接赋值的应用场景
场景1: 三个条件写在一个函数里
场景2: 1,2写在一块,3单独写在一个函数里---->函数调用
场景3:1, 2,3写在一起----->c++会有
3)函数调用时,用n指针修改n-1指针的值
函数调用时,形参传给实参,用实参取地址,传给形参,在被调用函数里面用*p,来改变实参,把运算结果传出来
间接赋值从0级指针到1级指针的技术推演
int getFileLen(int *p)//改变值在函数内部也可以做,为什么要单独把它甩出来?
{//int getFileLen(int *p)//这个是指针做了函数参数,为什么括号里写int *p?
*p=40;
}
int getFileLen02()//当我们要求2个或者2个以上的文件,return只能返回一个结果,那怎么办?
{
int a = 100;
return a;
}
int getFileLen(int *p)//p的值是a的地址
{
*p = 41;
}
int getFileLen3(int b)
{
b = 100;
}
//1级指针的技术推演
void main()
{
int a = 10;
int *p = NULL;
a = 20;
p = &a;
*p = 30;
printf("a:%d\n", a);
{
*p = 40;
printf("a:%d\n", a);
}
getFileLen(&a);
printf("getFileLen a:%d\n", a);
getFileLen3(&a);
printf("getFileLen a:%d\n", a);
printf("hello...\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
2级指针的用法
1)在被调用函数直接修改1级指针的值
2)做输出
//间接赋值从1级指针到2级指针的技术推演
void getMem(char **p2)
{
*p2 = 400;//间接赋值p2是p1的地址
}
void getMem2(char *p2)//和写到这里是一样的
{//形参修改成千上万回,和
char *p2;//形参写到这里
p2 = 800;//间接赋值p2是p1的地址
}
void main()
{
char *p1 = NULL;//形参修改成千上万回,和这里没关系
char **p2 = NULL;
p1 = 0x11;
p2 = 0x22;
p1 = 0x111;
p2 = &p1;
*p2 = 100;
printf("p1:&d\n", p1);
{
*p2 = 200;
printf("p1:&d\n", p1);
}
getMem(&p1);
printf("p1:&d\n", p1);
printf("hello...\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
//指针做函数参数
铁律3:理解指针必须和内存四区相结合
1)主调函数 被调函数
主调函数可把堆区,栈区,全局数据内存地址传给被调用函数
被调函数只能返回堆区 全局数据区
2)内存分配方式
指针做函数参数,是有输入和输出特性
void getMem3(char **myp1/*out*/ , int *mylen1/*out*/, char **myp2, int *mylen2)
{
//1级指针改变实参的值
//修改0级用1级指针,1级用2级指针
int ret = 0;
char *tmp1, *tmp2;
tmp1 = (char*)malloc(100);//分配内存
strcpy(tmp1, "1112223");
//间接赋值
*mylen1 = strlen(tmp1);//1级指针
*myp1 = tmp1;//2级指针间接赋值
tmp2 = (char*)malloc(200);
strcpy(tmp2, "aaaaaaddddd");
*mylen2 = strlen(tmp2);
*myp2 = tmp2;//2级指针间接赋值
return ret;
}
char *getMem42(int num)
{
int ret = 0;
char *tmp1;
tmp1 = (char*)malloc(num);
strcpy(tmp1, "112233");
return tmp1;
}
int main()
{
int ret = 0;
char *p1 = NULL;//定义一个1级指针
int len1 = 0;
char *p2 = NULL;
int len2 = 0;
ret = getMem3(&p1, &len1, &p2, &len2);//取地址,扔给被调用函数
if (ret != 0)
{
printf("func getMem3() 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;
}
p1 = getMem42(100);
printf("p1:%s\n", p1);
if (p1 != NULL)
{
free(p1);
p1 = NULL;
}
printf("p1:%d\n", p1);
printf("hello...\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
铁律4:应用指针必须和函数调用相结合(指针做函数参数)
指针做函数参数,问题的实质不是指针,而是看内存块.
//1级指针的典型用法
//数组
//字符串(在c中没有字符串类型 通过字符数组 来模拟字符串)
//字符串的内存分配 堆上 栈上 全局区(!!!)
//字符数组 初始化
void main()
{ //1指定长度
char buf2[100] = { 'a','b','c','d' };
//后面的4-99全部置为0
//char buf2[2]= { 'a','b','c','d' }; 太大了报错
//2不指定长度 c编译器会自动求元素个数
char buf1[] = { 'a','b','c','d' };//是一个数组,不是字符串
printf("buf2:%s\n", buf2);
printf("hello...\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
//用字符串来初始化字符数组
void main()
{
int size = 0;
char buf3[] = "asdf";//作为字符数组是5个字节===作为字符串4个字节
int len = strlen(buf3);
printf("buf3长度:%d\n", len);
size = sizeof(buf3);
printf("buf3数组所占内存空间大小:%d\n", size);
printf("hello..\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
//用数组或指针操作字符串
void main()
{
int i = 0;
char *p = NULL;//辅助指针变量
char buf5[128] = "asdff";
for (i = 0; i < strlen(buf5); i++)
{
printf("%c", buf5[i]);
}
p = buf5;//代表数组首元素地址
for (i = 0; i < strlen(buf5); i++)
{
printf("%c", *(p + i));
}
printf("hello..\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
//[] 本质和 *p是一样的,只不过是符合程序员的阅读习惯
//buf5是一个指针 一个常量指针==? 编译器自定义为常量,不能轻易改变
//为了保证首地址的安全性,必须是常量指针.要不然没有办法完全析构掉内存空间,导致浪费
/*buf5[i]==>buf5[0+i]==>*(buf5+i);
{
buf5=buf5+1;
buf5=0x11;
}
*/
字符串内存模型
void main()
{
char buf[20] = "aaaa";
char buf2[] = "bbbb";
char *p1 = "111111";
char *p2 = malloc(100);
strcpy(p2, "3333");
printf("hello..\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
void main01()
{
char a[] = "i am a student";
char b [64];
int i = 0;
for (i = 0; *(a + i) != '\0'; i++)
{
*(b + i) = *(a + i);
}
//0没有复制到b的buf中
b[i] = '\0';
printf("a:%s\n", a);
printf("b:%s\n", b);
printf("hello..\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}
//字符串拷贝函数的技术推演
void copy_str21(char *from, char *to)
{
for (; *from = '\0'; from++, to++)
{
*to = *from;
}
*to = '\0';
return;
}
void copy_str22(char *from, char *to)
{
while ((*to= *from)!='\0')
{
from++;
to++;
}
}
void copy_str23(char *from, char *to)
{
while ((*to++ = *from++) != '\0')
{
;
}
}
//不要轻易改变形参的值,要引入一个辅助的指针变量 ,把形参接过来
int copy_str25(char *from, char *to)
{
char *tmpfrom = from;
char *tmpto = to;
if (from == NULL || to == NULL)
{
return -1;
}
while (*tmpto++ = *tmpfrom++);
printf("from:%s\n", from);
}
void main()
{
//指针指向谁就把谁的地址赋给指针
char *from = "asddff";
char buf2[100];// 主调函数分配内存
copy_str21(from, buf2);
printf("hello..\n");
system("pause");//按任意键暂停,阻塞功能
return 0;//正常退出值
}