您的位置:首页 > 其它

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

2016-06-14 18:26 671 查看
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) 是指针存在的最大意义
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  提高 赋值 间接