一、指针易错点
1.越界问题
当我们写出下列代码时,编译器便自动给我们报错。原因是buf只有3个字符,但"abc"其中包含’\0’为4个字符,赋值时出现越界问题编译器自动报错。
void test()
{
char buf[3] = "abc";
printf("%s\n",buf);
}
2.指针叠加会不断改变指针指向
当我们执行下面代码时,看似没有问题,结果也正常输出。
void test()
{
char* p = (char*)malloc(sizeof(char)*64);
for (int i=0; i<10; i++)
{
*p = i+97;
printf("%c",*p);
p++;
}
}
输出为:
但是我们好像忘了一件事情:那就是我们手动开辟的内存,要手动释放!
于是我们将开辟的内存释放。
if (p != NULL)
{
free(p);
p = NULL;
}
程序出现错误,费了老大力气才关掉它!为什么会出现这种情况?主要原因在这一行:当我们申请的首地址指针偏移后,就不可以利用这个指针释放堆内存了。
p++;//申请的首地址指针偏移后,就不可以利用这个指针释放堆内存了。
那么针对这种问题如何改进呢?我们利用临时指针来对其进行操作,释放的时候还是释放原来的地址,程序便不会出现问题了。
void test()
{
char* p = (char*)malloc(sizeof(char)*64);
char* pp = p;//利用临时指针进行操作
for (int i=0; i<10; i++)
{
*pp = i+97;
printf("%c",*pp);
pp++;
}
if (p != NULL)
{
free(p);
p = NULL;
}
}
输出正确:
3.返回局部变量地址
编译器会报错,因为str是在栈上开辟的,函数的生命周期为该函数结束时候,后面别和函数接收后,它已经消亡,再返回即为随机值。
void test()
{
char str[] = "abcdefg";
printf("str = %s\n",str);
return str;
}
4.同一块内存释放多次(不可以释放野指针) :内存被释放,不能够再释放这块内存,此时已无权限对其操作了;
void test()
{
int* p2 = (int*)malloc(100);
free(p2);//释放p2,不能再使用了
free(p2);//再次操作,程序崩溃
}
二、二级指针简单理解
二级指针:二级指针也是一个变量,它指向的一定是“指针的地址”。
我们简单理解一下:一级指针指向的是普通变量的地址,那么二级指针指向的是“一级指针的地址”,以此类推,三级指针对应的是“二级指针的地址”。
如下代码:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
//定义普通变量、一级指针、二级指针,不初始化
int a;
int *p1;
int **p2;
//对前面的普通指针、一级指针、二级指针进行初始化赋值
a = 10;
p1 = &a;
p2 = &p1;
printf("a = %d\n", a);
printf("&a = %d\n", &a);
printf("p1 = %d\n", p1);
printf("*p1 = %d\n", *p1);
printf("p2 = %d\n", p2);
printf("*p2 = %d\n", *p2);
printf("**p2 = %d\n", **p2);
printf("&p2 = %d\n", &p2);
return 0;
}
输出结果为:p1为a的地址,*p2为p1的地址,即a的地址。验证了一级指针指向的是普通变量的地址,那么二级指针指向的是一级指针的地址。
三、二级指针做函数参数的输入特性
输入特性:主调函数分配内存,被调函数使用内存。
实例1:主调函数在堆区开辟内存,被调函数在栈区使用。
代码实现:
void printArray(int** pArray,int len)//被调函数
{
for (int i=0; i<len; i++)
{
printf("%d\n",*pArray[i]);
}
}
void test()//主调函数
{
int** p = (int**)malloc(sizeof(int*)*5);//在堆上分配内存
//在栈上创建数据
int a1 = 10;
int a2 = 20;
int a3 = 30;
int a4 = 40;
int a5 = 50;
p[0] = &a1;
p[1] = &a2;
p[2] = &a3;
p[3] = &a4;
p[4] = &a5;
printArray(p,5);
if (p != NULL)
{
free(p);
p = NULL;
}
}
输出结果:主调函数分配内存,被调函数使用内存成功。
实例2:主调函数在栈上开辟内存,被调函数堆区使用。
代码实现:
void printArray(int** pArray,int len)
{
for (int i=0; i<len; i++)
{
printf("%d\n",*pArray[i]);
}
}
void test2()
{
//在栈上创建
int* pArray[5];
for (int i=0; i<5; i++)
{
pArray[i] = (int*)malloc(4);//堆上分配
*(pArray[i]) = 100+i;
}
int len = sizeof(pArray)/sizeof(int*);
printArray(pArray,len);
for (int i=0; i<5; i++)
{
if (pArray[i] != NULL)//要注意释放
{
free(pArray[i]);
pArray[i] = NULL;
}
}
}
输出结果:主调函数分配内存,被调函数使用内存成功。
四、二级指针做函数参数的输出特性
输入特性:被调函数分配内存,主调函数使用内存。
实例1:被调函数在堆区开辟内存,主调函数栈区使用。
void allocateSpace(int** p)//被调函数分配内存
{
int* arr = (int*)malloc(sizeof(int)*10);
for (int i=0; i<10; i++)
{
arr[i] = i+10;
}
*p = arr;
}
void printArray(int** pArray,int len)
{
for (int i=0; i<10; i++)
{
printf("%d\n",(*pArray)[i]);
}
}
void test()//主调函数使用
{
int* p = NULL;
allocateSpace(&p);
printArray(&p,10);
if (p != NULL)
{
free(p);
p = NULL;
}
}
输出结果:被调函数分配内存,主调函数使用内存成功。
五、二级指针进行文件读写
我们利用二级指针进行文件的读写操作:从文件中读取数据,并且将数据存放在堆区数组中。
1.首先创建一个源文件,打开其所在目录,添加一个test.txt文件并写入一些数据。
2.从文件中读取数据,并且将数据存放在堆区数组中。
void ShowFileData(char** pArray,int len)//显示数组
{
for (int i=0; i<len; i++)
{
printf("第%d行的数据为:%s\n",i+1,pArray[i]);
}
}
int GetFileLines(FILE* file)//获取文件有效行数
{
if (file == NULL)
{
return -1;
}
char buf[1024];//读取到的数据存储到缓冲区buf中
int num = 0;
while (fgets(buf,1024,file) != NULL)
{
num++;
}
//将文件光标置为文件首,若不置到文件首,文件光标获取后将在最后
fseek(file,0,SEEK_SET);
return num;
}
//参数1:文件指针 参数2:有效长度 参数3:堆区数组
void ReadFileData(FILE* file,int len,char** pArray)
{
if ((file == NULL) || (len <= 0) || (pArray == NULL))
{
return;
}
char buf[1024];
int index = 0;
while (fgets(buf,1024,file) != NULL)//buf中就是存放的每行数据
{ //buf就是存放的每行数据
int currentLen = strlen(buf)+1;
char* currentP = (char*)malloc(sizeof(char)*currentLen);
//将数据拷贝到堆区内存中
strcpy(currentP,buf);
pArray[index++] = currentP;
//清空缓冲区
memset(buf,0,1024);
}
}
void FreeSpace(char** pArray,int len)//释放
{
for (int i=0; i<len; i++)
{
if (pArray[i] != NULL)
{
free(pArray[i]);
pArray[i] = NULL;
}
}
free(pArray);
pArray = NULL;
}
void test()
{
FILE* file = fopen("./test.txt","r");//以只读方式打开文件
if (file == NULL)
{
printf("文件打开失败\n");
return;
}
int len = GetFileLines(file);
printf("文件的有效行数为:%d\n",len);
char** pArray = (char**)malloc(sizeof(char*)*len);
//将文件中的有效数据读取后放入到pArray中
ReadFileData(file,len,pArray);
//打印数据
ShowFileData(pArray,len);
//释放数据
FreeSpace(pArray,len);
pArray = NULL;
}
输出结果:成功获取。
参考博客:
指针(一级指针,二级指针)
如何理解C指针及二级指针(1)