1野指针强化:
#include <stdio.h> #include <stdlib.h> #include <string.h> // 野指针产生的原因 //指针变量和它指向的内存空间变量是两个不同的概念 //释放了指针所指向的内存空间,但是指针变量本省没有重置为NULL //造成释放的时候,通过if(p1 != NUll) //避免方法: // 1)定义指针的时候,初始化成NULL // 2)释放指针所指向的内存空间,把指针重置成NULL int main() { char *p1 = NULL; p1 = (char *)malloc(100); if (p1 == NULL) { printf("没有分配到堆空间 \n"); return 0; } 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> int main() { char buf[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]; int i = 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的步长是char printf("%c ",*p1); } free(p2); system("pause"); } 编译运行: C:\Users\chunli>gcc main.c & a a 1 b 2 c 3 d 4 e 5 1 2 3 4 5 6 7 8 9 0 请按任意键继续. . .
字面量:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { //结论:字面量不能取地址,没有放在栈区,也没有放在堆区,因为没法取其地址 //我觉得放在代码区 int i = 0; //字面量 0 0不能取地址 for (i = 0; i < 10; i++) //字面量 10 10不能取地址 { printf("Hello World! \n"); } } 便于运行: C:\Users\chunli>gcc main.c & a Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!
从0级指针到1级指针:
#include <stdio.h> #include <stdlib.h> #include <string.h> int fun1() { int a = 20; return a; } int fun2(int a) { a = 30; } int fun3(int *a) { *a = 40; } int main() { int a = 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"); } /* 编译运行: 10 10 10 40 请按任意键继续. . . */
从1级指针到2级指针:
#include <stdio.h> #include <stdlib.h> #include <string.h> void fun1(char **p2) //只有这种方法才能修改一级指针的数值 { *p2 = 0xff; //间接修改一级指针的值,而不是修改一级指针指向的内存块 } void fun2(char *p2) //这种方法永远也不可能修改传过来的一级指针的数值 { //char *p2 就相当于是在这里定义了一个p2指针变量,和外界的一级指针没有任何关系 p2 = 0xf; } int main() { char *p1 = NULL; char *p2 = NULL; //直接修改p1的值 p1 = 0x22; p2 = 0x55; //间接是修改p1的值 p2 = &p1; //把p1的地址装入p2 *p2 = 100; //把变量p1的值修改为100 printf("%d \n",p1); //把p1的数值打印出来 fun1(&p1); //间接修改一级指针的值,而不是修改一级指针指向的内存块 printf("%d \n", p1); //把p1的数值打印出来 fun2(p1); printf("%d \n", p1); //把p1的数值打印出来 system("pause"); } /* 编译运行: 100 255 255 请按任意键继续. . . */
【重要概念】
间接赋值(*p) 是指针存在的最大意义
#include <stdio.h> #include <stdlib.h> #include <string.h> void fun1(char **myp1, int *mylen1, char **myp2, int *mylen2) { int ret = 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级指针 } int main() { /* 原来p1的值是NULL,通过函数调用,p1指向了一块内存区域 */ char *p1 = NULL; char *p2 = NULL; int len1 = 0; int len2 = 0l; int ret = 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"); return ret; } /* 编译运行: 1234567890 abcdefg 请按任意键继续. . . */
间接赋值的成立条件:
#include <stdio.h> #include <stdlib.h> #include <string.h> void fun1(int *p) { *p = 50; } int main() { //条件1,定义了2个变量 int a = 0; int *p = NULL; //条件2,建立关联 p = &a; //条件3,*p间接修改 *p = 10; printf("%d \n",a); //一级指针与函数 fun1(&a); //把实参的地址传给形参,建立关联 printf("%d \n", a); system("pause"); return 0; } /* 编译运行: 10 请按任意键继续. . . */
//间接赋值的应用场景
#include <stdio.h> #include <stdlib.h> #include <string.h> /* 间接赋值成立的三个条件: 条件1:定义变量(实参),定义变量(形参) 条件2:建立关联 条件3:形参去间接的修改了实参的值 */ //间接赋值的应用场景 // 条件1 2 3 写在一个函数中 int main() { char from[100] = { 0 }; char to[100] = { 0 }; char *p1 = from; char *p2 = to; strcpy(from,"112233445566"); while (*p1 != '\0') { *p2++ = *p1++; } printf("%s \n",to); system("pause"); return 0; } /* 编译运行: 112233445566 请按任意键继续. . . */
字符串与一级指针 专题 --- //字符数组的初始化
#include <stdio.h> #include <stdlib.h> #include <string.h> //字符串与一级指针 专题 --- //字符数组的初始化 // 1 C语言中的字符串是以数字0结尾的 // 2 在C语言中没有字符串类型,通过字符数组来模拟字符串 // 3 字符串的内存分配可以在堆上 栈上 常量区 int main() { char buf1[100] = { 'a', 'b', 'c', 'd' };//除前面4个外,其他全是0 printf("%d \n",buf1[99]); //char buf2[2] = { 'a', 'b', 'c', 'd' };//大于初始化的内存个数,编译器报错 char buf3[] = { 'a', 'b', 'c', 'd' };//这不是以0结尾的字符串 char buf4[] = "abcd"; //数组的大小是5,字符长度是4 int size = sizeof(buf4); int len = strlen(buf4); printf("size=%d, len=%d \n",size,len); system("pause"); return 0; } /* 编译运行: 0 size=5, len=4 请按任意键继续. . . */
字符串与一级指针 专题 -- 用数组和指针操作字符串
#include <stdio.h> #include <stdlib.h> #include <string.h> //字符串与一级指针 专题 -- 用数组和指针操作字符串 //普通指针与数组变量名字的区别 int main() { int i = 0; char *p1 = NULL; char buf1[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"); return 0; } // 中括号[]的本质:和*是一样,只不过是符合程序员的阅读习惯 // buf是一个指针,只读的常量,buf是一个常量指针。 //为什么这么做? //buf是一个常量指针,析构内存的时候,保证buf空间完整释放 /* 编译运行: a b c d e f g a a b b c c d d e e f f g g 请按任意键继续. . . */
一级指针 内存模型的建立
#include <stdio.h> #include <stdlib.h> #include <string.h> //一级指针 内存模型的建立 int main() { char buf1[20] = "aaa"; char buf2[] = "bbbb"; char *p1 = "11111111111111"; char *p2 = malloc(100); strcpy(p2,"333333"); system("pause"); return 0; //见图 } /* 编译运行: */
字符串做函数参数
#include <stdio.h> #include <stdlib.h> #include <string.h> //字符串做函数参数 int main() { 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[i] = '\0'; //'\0' 并不会存入 printf("%s \n",b); system("pause"); return 0; } /* 编译运行: i am a student 请按任意键继续. . . */
字符串拷贝,3种方法
#include <stdio.h> #include <stdlib.h> #include <string.h> //字符串拷贝,3种方法 void copy_str1(char *from, char *to) { while (*from != '\0') //当遇到0就跳出来了 { //指针的指向不断变化,小心啊 // ++ 优先级高 *to++ = *from++; //'\0' 并不会存入 //相当于先 *to = *from, 在to++ from++ } *to = '\0'; return; } void copy_str2(char *from, char *to) //优化一下 { while ((*to = *from) != '\0') //先把值赋过去,再判断 { *to++; *from++; } } //经典程序之一 字符串拷贝 void copy_str(char *from, char *to) //再优化一下 { while (*to++ = *from++); //先把值赋过去,再判断 } int main() { char *from = "Hello"; char buf[100] = {0}; copy_str(from,buf); printf("%s \n",buf); system("pause"); return 0; } /* 编译运行: i am a student 请按任意键继续. . . */
经典程序之一 字符串拷贝
#include <stdio.h> #include <stdlib.h> #include <string.h> //字符串拷贝,3种方法 //经典程序之一 字符串拷贝 //不要轻易改变形参的值 int copy_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); return 0; } int main() { int ret = 0; char *from = "Hello"; //char *buf = NULL; char buf[20] ; ret = copy_str(from,buf); if (ret != 0) { printf("fun copy_str error %d \n", ret); } printf("%s \n",buf); system("pause"); return ret; } /* 编译运行: i am a student 请按任意键继续. . . */
项目开发字符串模型 do while 模型
#include <stdio.h> #include <stdlib.h> #include <string.h> //项目开发字符串模型 do while 模型 int main() { int count = 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"); return 0; } /* 编译运行: */
项目开发字符串模型 while 模型
#include <stdio.h> #include <stdlib.h> #include <string.h> //项目开发字符串模型 while 模型 int main() { int count = 0; char *p = "11abcd123abcd234abcd23abcd2345abcd"; while (p=strstr(p,"abcd")) { count++; p = p + strlen("abcd"); if (*p == '\0') { break; } } printf("%d \n" ,count); system("pause"); return 0; } /* 编译运行: */
项目开发字符串模型 while 模型 -- API封装
#include <stdio.h> #include <stdlib.h> #include <string.h> //项目开发字符串模型 while 模型 -- API封装 int getCount(char *String/*in*/, char *sub/*i*/, int *count/* out*/) { int tmp_count = 0; int ret = 0; char *p = String; if (String == NULL || sub == NULL) { ret = -1; printf("fun getCount error String == NULL || sub == NULL %d \n ",ret); return ret; } while (p = strstr(p, sub)) { tmp_count++; p = p + strlen(sub); if (*p == '\0') { break; } } *count = tmp_count; return 0; } int main() { int ret = 0; int count = 0; char *p1 = "11abcd123abcd234abcd23abcd2345abcd"; char *p2 = "abcd"; ret = getCount(p1,p2,&count); if (ret != 0) { printf("fun fetCount error %d \n",ret); } printf("%d \n" ,count); system("pause"); return ret; } /* 编译运行: */
练习题:
第一题,我的答案:
#include <stdio.h> #include <stdlib.h> #include <string.h> int str_del(const char *str1/*in*/, char * str2/*out*/) { int ret = 0; if (str1 == NULL || str2 == NULL) { ret = -1; printf("fun str_del str1 == NULL || str2 == NULL error: %d \n",ret); return ret; } char *p = str1; while (*p == ' ') { p++;//跳出空格 } strcpy(str2, p);//从第一个非空格开始,写入原字符串 int len = strlen(str2); if (str2[len-1] == ' ')//如果最后一位有空格 { p = str2+len-1;//找到最后一个空格的位置 while (*p == ' ') { *p = 0; p--; } } return ret; } int main() { int ret = 0; char *str1 =" abc 123 "; char str2[128]; ret = str_del(str1,str2); if (ret != 0) { printf("error in fun str_del code:%d\n",ret); return ret; } printf("%s", str2); system("pause"); return ret; } /* 编译运行: abc 123请按任意键继续. . . */
第二题,我的答案:
#include <stdio.h> #include <stdlib.h> #include <string.h> int getstr(const char *str1/*in*/, char * str2/*out*/, char * str3/*out*/) { int ret = 0; if (str1 == NULL || str2 == NULL || str3 == NULL) { ret = -1; printf("fun str_del str1 == NULL || str2 == NULL || str3 == NULL error: %d \n",ret); return ret; } char *p1 = str1; char *p2 = str2; char *p3 = str3; int i = 0; while (*p1 != '\0') { if (i % 2) *p2++ = *p1; else *p3++ = *p1; i++; p1++; } return ret; } int main() { int ret = 0; char *string ="1a2b3c4da1b2c3d4"; char str1[128] = { 0 }; char str2[128] = { 0 }; ret = getstr(string,str1,str2); if (ret != 0) { printf("error in fun str_del code:%d\n",ret); return ret; } printf("str1=%s\n", str1); printf("str2=%s\n", str2); system("pause"); return ret; } /* 编译运行: str1=abcd1234 str2=1234abcd 请按任意键继续. . . */
指针经典话语:
1,指针指向谁,就把谁的地址赋给指针;
2,指针变量 和 它指向的内存空间变量是两个不同的概念
3,理解指针的关键是内存,没有内存哪里来的指针
变量的本质是一个固定大小的数据块,变量名就是数据块的编号
内存的使用范围:
main函数可以在栈分配内存/堆分配内存/全局分配内存,可以给子函数使用
子函数在栈分配的内存不能给主函数使用,但是堆内存与全局变量是可以给main使用
编译器会为每个程序分配一个内存4区,主函数与子函数公用这个内存4区
建立正确程序运行内存布局图是学好C的关键!
指针铁律1:指针是一种数据类型
1)指针也是一种变量,占有内存空间,用来保存内存地址
2)*p 操作内存;
3)*就像一把钥匙,通过一个地址(&a),去修改a变量的标示的内存空间
4)不断的给指针赋值,相当于不停的改变指针的指向。
5) 指针是一种数据类型,是指它指向内存空间的数据类型
指针铁律1:间接赋值(*p) 是指针存在的最大意义