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) 是指针存在的最大意义
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。