1野指针强化:

#include<stdio.h>#include<stdlib.h>#include<string.h>//野指针产生的原因//指针变量和它指向的内存空间变量是两个不同的概念//释放了指针所指向的内存空间,但是指针变量本省没有重置为NULL//造成释放的时候,通过if(p1!=NUll)//避免方法://1)定义指针的时候,初始化成NULL//2)释放指针所指向的内存空间,把指针重置成NULLintmain(){char*p1=NULL;p1=(char*)malloc(100);if(p1==NULL){printf("没有分配到堆空间\n");return0;}strcpy(p1,"1234567890");printf("%s\n",p1);if(p1!=NULL){free(p1);//p1=NULL;//如果没有这个步骤,下面的再次释放就导致程序出错(VS2013)}if(p1!=NULL){free(p1);}}





NULL 地址写入数据

不可预料的地址写入数据

不断改变指针的指向:



#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){charbuf[20]={'a','1','b','2','c','3','d','4','e','5'};char*p1=NULL;char*p2=NULL;p1=&buf[0];p1=&buf[1];p1=&buf[2];inti=0;for(i=0;i<10;i++){p1=&buf[i];printf("%c",*p1);}p2=(char*)malloc(20);strcpy(p2,"1234567890");for(i=0;i<10;i++)//不断的修改指针的值,相当于不断改变指针的指向{p1=p2+i;//注意p2的步长是charprintf("%c",*p1);}free(p2);system("pause");}编译运行:C:\Users\chunli>gccmain.c&aa1b2c3d4e51234567890请按任意键继续...



字面量:

#include<stdio.h>#include<stdlib.h>#include<string.h>intmain(){//结论:字面量不能取地址,没有放在栈区,也没有放在堆区,因为没法取其地址//我觉得放在代码区inti=0;//字面量00不能取地址for(i=0;i<10;i++)//字面量1010不能取地址{printf("HelloWorld!\n");}}便于运行:C:\Users\chunli>gccmain.c&aHelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!HelloWorld!


从0级指针到1级指针:

#include<stdio.h>#include<stdlib.h>#include<string.h>intfun1(){inta=20;returna;}intfun2(inta){a=30;}intfun3(int*a){*a=40;}intmain(){inta=10;int*p=NULL;p=&a;printf("%d\n",*p);fun1();printf("%d\n",*p);fun2(a);//只是把a的数值传过来,用来初始化函数的值,除此之外,函数内与a没有任何关系了printf("%d\n",*p);fun3(&a);//只有把a的地址传过来,才能对a进行修改printf("%d\n",*p);system("pause");}/*编译运行:10101040请按任意键继续...*/


从1级指针到2级指针:

#include<stdio.h>#include<stdlib.h>#include<string.h>voidfun1(char**p2)//只有这种方法才能修改一级指针的数值{*p2=0xff;//间接修改一级指针的值,而不是修改一级指针指向的内存块}voidfun2(char*p2)//这种方法永远也不可能修改传过来的一级指针的数值{//char*p2就相当于是在这里定义了一个p2指针变量,和外界的一级指针没有任何关系p2=0xf;}intmain(){char*p1=NULL;char*p2=NULL;//直接修改p1的值p1=0x22;p2=0x55;//间接是修改p1的值p2=&p1;//把p1的地址装入p2*p2=100;//把变量p1的值修改为100printf("%d\n",p1);//把p1的数值打印出来fun1(&p1);//间接修改一级指针的值,而不是修改一级指针指向的内存块printf("%d\n",p1);//把p1的数值打印出来fun2(p1);printf("%d\n",p1);//把p1的数值打印出来system("pause");}/*编译运行:100255255请按任意键继续...*/




【重要概念】


间接赋值(*p) 是指针存在的最大意义

#include<stdio.h>#include<stdlib.h>#include<string.h>voidfun1(char**myp1,int*mylen1,char**myp2,int*mylen2){intret=0;char*tmp1=NULL;char*tmp2=NULL;//间接赋值tmp1=(char*)malloc(100);strcpy(tmp1,"1234567890");*mylen1=strlen(tmp1);//1级指针*myp1=tmp1;//2级指针tmp2=(char*)malloc(200);strcpy(tmp2,"abcdefg");*mylen2=strlen(tmp2);//1级指针*myp2=tmp2;//2级指针}intmain(){/*原来p1的值是NULL,通过函数调用,p1指向了一块内存区域*/char*p1=NULL;char*p2=NULL;intlen1=0;intlen2=0l;intret=0;fun1(&p1,&len1,&p2,&len2);if(ret!=0){printf("error:%d\n",ret);}printf("%s\n",p1);printf("%s\n",p2);if(p1!=NULL){free(p1);p1=NULL;}if(p2!=NULL){free(p2);p2=NULL;}system("pause");returnret;}/*编译运行:1234567890abcdefg请按任意键继续...*/




间接赋值的成立条件:

#include<stdio.h>#include<stdlib.h>#include<string.h>voidfun1(int*p){*p=50;}intmain(){//条件1,定义了2个变量inta=0;int*p=NULL;//条件2,建立关联p=&a;//条件3,*p间接修改*p=10;printf("%d\n",a);//一级指针与函数fun1(&a);//把实参的地址传给形参,建立关联printf("%d\n",a);system("pause");return0;}/*编译运行:10请按任意键继续...*/


//间接赋值的应用场景

#include<stdio.h>#include<stdlib.h>#include<string.h>/*间接赋值成立的三个条件:条件1:定义变量(实参),定义变量(形参)条件2:建立关联条件3:形参去间接的修改了实参的值*///间接赋值的应用场景//条件123写在一个函数中intmain(){charfrom[100]={0};charto[100]={0};char*p1=from;char*p2=to;strcpy(from,"112233445566");while(*p1!='\0'){*p2++=*p1++;}printf("%s\n",to);system("pause");return0;}/*编译运行:112233445566请按任意键继续...*/


字符串与一级指针 专题 --- //字符数组的初始化

#include<stdio.h>#include<stdlib.h>#include<string.h>//字符串与一级指针专题---//字符数组的初始化//1C语言中的字符串是以数字0结尾的//2在C语言中没有字符串类型,通过字符数组来模拟字符串//3字符串的内存分配可以在堆上栈上常量区intmain(){charbuf1[100]={'a','b','c','d'};//除前面4个外,其他全是0printf("%d\n",buf1[99]);//charbuf2[2]={'a','b','c','d'};//大于初始化的内存个数,编译器报错charbuf3[]={'a','b','c','d'};//这不是以0结尾的字符串charbuf4[]="abcd";//数组的大小是5,字符长度是4intsize=sizeof(buf4);intlen=strlen(buf4);printf("size=%d,len=%d\n",size,len);system("pause");return0;}/*编译运行:0size=5,len=4请按任意键继续...*/




字符串与一级指针 专题 -- 用数组和指针操作字符串

#include<stdio.h>#include<stdlib.h>#include<string.h>//字符串与一级指针专题--用数组和指针操作字符串//普通指针与数组变量名字的区别intmain(){inti=0;char*p1=NULL;charbuf1[128]="abcdefg";for(i=0;i<strlen(buf1);i++){printf("%c",buf1[i]);}//用指针操作字符串p1=buf1;//buf代表数组元素的地址,这句话是错误的。应该说,buf代表数组首元素的地址for(i=0;i<strlen(buf1);i++){printf("%c",*(p1+i));//效果一样printf("%c",*(buf1+i));//效果一样相当于buf1[i]//[]到*的推导过程//buf1[i]=>buf1[0+i]=>*(buf+i),buf代表数组首元素的地址//其实[]与*操作数组,没有多大的区别,只是中括号便于人们的阅读}//不允许的操作//buf1=buf1+1;左操作数必须为左值system("pause");return0;}//中括号[]的本质:和*是一样,只不过是符合程序员的阅读习惯//buf是一个指针,只读的常量,buf是一个常量指针。//为什么这么做?//buf是一个常量指针,析构内存的时候,保证buf空间完整释放/*编译运行:abcdefgaabbccddeeffgg请按任意键继续...*/



一级指针 内存模型的建立

#include<stdio.h>#include<stdlib.h>#include<string.h>//一级指针内存模型的建立intmain(){charbuf1[20]="aaa";charbuf2[]="bbbb";char*p1="11111111111111";char*p2=malloc(100);strcpy(p2,"333333");system("pause");return0;//见图}/*编译运行:*/



字符串做函数参数

#include<stdio.h>#include<stdlib.h>#include<string.h>//字符串做函数参数intmain(){chara[]="iamastudent";charb[64];inti=0;for(i=0;*(a+i)!='\0';i++){*(b+i)=*(a+i);//'\0'并不会存入}b[i]='\0';//'\0'并不会存入printf("%s\n",b);system("pause");return0;}/*编译运行:iamastudent请按任意键继续...*/



字符串拷贝,3种方法

#include<stdio.h>#include<stdlib.h>#include<string.h>//字符串拷贝,3种方法voidcopy_str1(char*from,char*to){while(*from!='\0')//当遇到0就跳出来了{//指针的指向不断变化,小心啊//++优先级高*to++=*from++;//'\0'并不会存入//相当于先*to=*from,在to++from++}*to='\0';return;}voidcopy_str2(char*from,char*to)//优化一下{while((*to=*from)!='\0')//先把值赋过去,再判断{*to++;*from++;}}//经典程序之一字符串拷贝voidcopy_str(char*from,char*to)//再优化一下{while(*to++=*from++);//先把值赋过去,再判断}intmain(){char*from="Hello";charbuf[100]={0};copy_str(from,buf);printf("%s\n",buf);system("pause");return0;}/*编译运行:iamastudent请按任意键继续...*/



经典程序之一 字符串拷贝

#include<stdio.h>#include<stdlib.h>#include<string.h>//字符串拷贝,3种方法//经典程序之一字符串拷贝//不要轻易改变形参的值intcopy_str(char*from,char*to)//再优化一下{char*tmp_to=to;//引进一个辅助if(from==NULL||to==NULL)return-1;while(*to++=*from++);//先把值赋过去,再判断//不能出现printf("%s\n",to)这样的语句,因为to已经改变了printf("%s\n",tmp_to);return0;}intmain(){intret=0;char*from="Hello";//char*buf=NULL;charbuf[20];ret=copy_str(from,buf);if(ret!=0){printf("funcopy_strerror%d\n",ret);}printf("%s\n",buf);system("pause");returnret;}/*编译运行:iamastudent请按任意键继续...*/


项目开发字符串模型 do while 模型

#include<stdio.h>#include<stdlib.h>#include<string.h>//项目开发字符串模型dowhile模型intmain(){intcount=0;char*p="11abcd123abcd234abcd23abcd2345abcd";do{p=strstr(p,"abcd");if(p!=NULL){count++;p=p+strlen("abcd");}else{break;}}while(*p!='\0');printf("%d\n",count);system("pause");return0;}/*编译运行:*/




项目开发字符串模型 while 模型

#include<stdio.h>#include<stdlib.h>#include<string.h>//项目开发字符串模型while模型intmain(){intcount=0;char*p="11abcd123abcd234abcd23abcd2345abcd";while(p=strstr(p,"abcd")){count++;p=p+strlen("abcd");if(*p=='\0'){break;}}printf("%d\n",count);system("pause");return0;}/*编译运行:*/



项目开发字符串模型 while 模型 -- API封装

#include<stdio.h>#include<stdlib.h>#include<string.h>//项目开发字符串模型while模型--API封装intgetCount(char*String/*in*/,char*sub/*i*/,int*count/*out*/){inttmp_count=0;intret=0;char*p=String;if(String==NULL||sub==NULL){ret=-1;printf("fungetCounterrorString==NULL||sub==NULL%d\n",ret);returnret;}while(p=strstr(p,sub)){tmp_count++;p=p+strlen(sub);if(*p=='\0'){break;}}*count=tmp_count;return0;}intmain(){intret=0;intcount=0;char*p1="11abcd123abcd234abcd23abcd2345abcd";char*p2="abcd";ret=getCount(p1,p2,&count);if(ret!=0){printf("funfetCounterror%d\n",ret);}printf("%d\n",count);system("pause");returnret;}/*编译运行:*/


练习题:


第一题,我的答案:

#include<stdio.h>#include<stdlib.h>#include<string.h>intstr_del(constchar*str1/*in*/,char*str2/*out*/){intret=0;if(str1==NULL||str2==NULL){ret=-1;printf("funstr_delstr1==NULL||str2==NULLerror:%d\n",ret);returnret;}char*p=str1;while(*p==''){p++;//跳出空格}strcpy(str2,p);//从第一个非空格开始,写入原字符串intlen=strlen(str2);if(str2[len-1]=='')//如果最后一位有空格{p=str2+len-1;//找到最后一个空格的位置while(*p==''){*p=0;p--;}}returnret;}intmain(){intret=0;char*str1="abc123";charstr2[128];ret=str_del(str1,str2);if(ret!=0){printf("errorinfunstr_delcode:%d\n",ret);returnret;}printf("%s",str2);system("pause");returnret;}/*编译运行:abc123请按任意键继续...*/



第二题,我的答案:

#include<stdio.h>#include<stdlib.h>#include<string.h>intgetstr(constchar*str1/*in*/,char*str2/*out*/,char*str3/*out*/){intret=0;if(str1==NULL||str2==NULL||str3==NULL){ret=-1;printf("funstr_delstr1==NULL||str2==NULL||str3==NULLerror:%d\n",ret);returnret;}char*p1=str1;char*p2=str2;char*p3=str3;inti=0;while(*p1!='\0'){if(i%2)*p2++=*p1;else*p3++=*p1;i++;p1++;}returnret;}intmain(){intret=0;char*string="1a2b3c4da1b2c3d4";charstr1[128]={0};charstr2[128]={0};ret=getstr(string,str1,str2);if(ret!=0){printf("errorinfunstr_delcode:%d\n",ret);returnret;}printf("str1=%s\n",str1);printf("str2=%s\n",str2);system("pause");returnret;}/*编译运行:str1=abcd1234str2=1234abcd请按任意键继续...*/



指针经典话语:

1,指针指向谁,就把谁的地址赋给指针;

2,指针变量 和 它指向的内存空间变量是两个不同的概念

3,理解指针的关键是内存,没有内存哪里来的指针


变量的本质是一个固定大小的数据块,变量名就是数据块的编号


内存的使用范围:

main函数可以在栈分配内存/堆分配内存/全局分配内存,可以给子函数使用

子函数在栈分配的内存不能给主函数使用,但是堆内存与全局变量是可以给main使用


编译器会为每个程序分配一个内存4区,主函数与子函数公用这个内存4区


建立正确程序运行内存布局图是学好C的关键!



指针铁律1:指针是一种数据类型

1)指针也是一种变量,占有内存空间,用来保存内存地址

2)*p 操作内存;

3)*就像一把钥匙,通过一个地址(&a),去修改a变量的标示的内存空间

4)不断的给指针赋值,相当于不停的改变指针的指向。

5) 指针是一种数据类型,是指它指向内存空间的数据类型

指针铁律1:间接赋值(*p) 是指针存在的最大意义