在中职学校当一名C语言教师。
在学习C语言指针的时候,为了给学生巩固指针知识,总结了几个C语言指针容易出错的地方。
程序示例 一、
#include <stdio.h>
char*getmemery()
{
char p[]="Hello,World!";
return p;
}
int main()
{
char*str=NULL;
str=getmemery();
printf("%s\n",str);
return 0;
}
getmemery()函数中,p[ ]是在函数中申请的数组,是临时变量,在函数返回时,指针p所指向的数组内存块会被释放掉,main函数能得到p的指针地址,但是这个指针所指向的内存块已经被释放掉了,所以printf打印指向这个地址,输出不了任何内容,或者是乱码。
程序示例 二、
#include <stdio.h>
char *getmemery()
{
char *p = "Hello,World!";
return p;
}
int main()
{
char *str = NULL;
str = getmemery();
printf("%s\n",str);
return 0;
}
该程序和示例一的差不多,就只是把数组p改成了,指针p,这里指针p指向的是一个字符串常量,而字符串常量,程序编译的寿,就已经确定在程序中的了,字符串常量的生存周期跟整个程序的生存周期一致,需程序结束,该字符串常量的内存块才会被释放,所以这个程序,str接收到返回的指针所指向的内存块没有没释放,所以该程序正常输出:Hello,World!
程序示例 三、
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemery(char *p)
{
p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
getmemery(str);
strcpy(str,"Hello,World!");
printf("%s\n",str);
if(str)free(str);
str = NULL;
return 0;
}
该程序使用malloc动态申请内存,很多同学想,这个函数的参数是*p,传的是地址,所以能改变原函数中的值。
但请仔细想想,我们在使用传指针的时候,是怎样改变原函数中的值的呢,请看示例
#include <stdio.h>
void fun(int *p){
*p = 8; //要使用指针改变mian中a的值,这里需要使用(*p)来整体操作。
}
int main(int argc, char const *argv[])
{
int a = 10;
int *p = &a;
fun(p);
printf("a = %d\n",a); //这里输出的是a = 8;
return 0;
}
所以,要想在函数中改变原调用函数中的值,必须使用*p整体操作才可以,若只使用指针p则会是指形参中的指针p指向别的地方而已。对函数外的值没有改变。str中的值依然还是NULL没有改变,程序执行报错。
根据这个,则示例三中的程序应该使用*p = malloc,那么参数就应该使用**p,二级指针。请看示例4。
程序示例 四、
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemery(char **p)
{
*p = (char *)malloc(100);
}
int main()
{
char *str = NULL;
getmemery(&str);
strcpy(str,"Hello,World!");
printf("%s\n",str);
if(str)free(str);
str = NULL;
return 0;
}
这样子就能正确的运行,正确输出结果。为什么呢。
举例子:
int a,*p = &a,则使用*p能改变a的值。对把,
那么对于*str呢,**p = &str,则通过*p就能改变 *str中的值。甚至可以类推至三级指针。
所以通过*p改变main函数中str指针所指向的值,让str指向在函数中申请的内存块首地址。函数结束,malloc中申请的内存不会释放,所以能正常输出结果。
程序示例 五、
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getmemery(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
int main()
{
char *str = NULL;
str = getmemery(100);
strcpy(str, "Hello,World!");
printf("%s\n",str);
if(str)free(str);
str = NULL;
return 0;
}
该程序采用指针函数的方式实现获取内存。直接将动态申请的内存块的首地址通过指针p返回给mian函数,所以str接收到的就是申请的内存块地址,能正常输出。
程序示例 六、
#include <stdio.h>
char *getmemery(void)
{
return "Hello,World";
}
int main()
{
char *str = NULL;
str = getmemery();
printf("%s\n",str);
}
改程序和例题2类似,直接返回的是常量字符串的指针。常量字符串,跟程序运行周期一致,所以能正常输出。
只是该程序在低版本的编译器中能正常运行,但在Visual Studio2019中,我测试,会有错误,常量字符串的指针是 const 类型。
所以会出现类型不匹配的情况。增加函数const类型,可以解决问题。
程序示例 七、
#include <stdio.h>
int main()
{
char *str = (char *) malloc(100);
strcpy(str, "Hello,World!");
free(str);
if(str != NULL)
{
strcpy(str, "Hello,World!");
printf(str);
}
}
该程序考察动态申请内存释放问题。
经过free(str) str所指向的内存块已经被释放,但是指针还在,我还是指向那个地方,只是那个地方已经被释放了,什么也没有,但str还是傻傻的指向那。好比,我手指着个房子,但那个房子如今已经被拆了,但我还是指着那个地方。
所以,这种现象就成为野指针。
在判断str的时候,str依然不是空,所以能通过程序中的if结构,只是不能再往里面复制东西了。就是房子已经被拆了,还能把东西放到房子的五楼吗?所以在释放内存后,就要把str指向NULL。消除野指针带来的危害。
程序实例 八、
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *str=(char *)malloc(100);
strcpy(str, "Hello,World!");
str+=6;
free(str);
if(str!=NULL)
{
strcpy(str, "Hello,World!");
printf(str);
}
}
这种情况,申请也是不行的,运行会报错而终止运行。str是申请内存的地址,而将str后移,想着只释放后面的内存块,这样的操作是不允许的,具体原因,我猜测是系统会记录申请内存的地址,如果释放的不是系统记录的地址,释放就会出错,也有可能只能通过内存块的首地址来释放。具体的若有知道的,可以留言告诉我。我只是测试了,是不行的。
好了,这就是我为我班上的同学总结的指针的问题。和我的讲解。若有错误的地方,请留言指正。谢谢!