int* p;*p=8;//非法,p此时为野指针,不能直接赋值
while(*p++)//不是死循环
restrict关键字
概括的说,关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,
即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码.
注:两个由restrict关键字修饰的指针不会执行重叠内存空间
例子:void *restrict ptr;
void指针(无类型指针)
任何类型的指针都可以赋给void指针,但是void指针不能直接赋值给其他类型的指针,需要强制类型转换
void* p1;
int* p2;
p2=(int*)p1;
如果函数参数可以是任意类型指针,应该将参数声明为void指针
如:void memcpy (void s1, const void* s2, size_t n);
一、函数指针
声明函数指针:
1、(类型*)(类名::*指针名)(参数列表)=类名::函数名;
int add(int n1,int n2)
{return n1+n2;}
int (*p)(int ,int )=add;//执行add函数的指针,加不加&都一样
void main()
{
printf("add(1,2)=%d\n",(*p)(1,2));//*p一定要用括号包围,或者直接p(1,2)
}
2、typedef 类型 (*指针名)(参数列表);
//创建了一个函数指针类型ptr
typedef int (*ptr)(int ,int)
int sub(int n1,int n2)
{return n1-n2;}
void main()
{
//定义
ptr p1=sub;
printf("sub(2,1)=%d\n",(*p1)(2,1));//p(2,1)
}
函数指针应用:回调函数
例:
typedef int(*p)(int,int);
int callback_add(int x,int y,p func)//也可以不用typedef,直接在参数里写int(*p)(int,int)
{
return func(x,y);
}
int add(int x,int y)
{
return x+y;
}
void main()
{
int x=3,y=4;
int z=callback_add(x,y,add);//z=7;
}
一个用于排序的回调函数,通过调用不同的函数指针实现对不同类型数据的排序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//void*,因为排列函数也是void*,不然会出警报
int cmp_int(const void* n1,const void* n2)
{
return (*(int*)n1-*(int*)n2);
}
int cmp_double(const void* n1,const void* n2)
{
if(*(double*)n1-*(double*)n2>0)
return 1;
else
return 0;
}
//排序
void sort(void* data,const int n,const int size,int(*ptr)(const void*,const void*))
{
int i,j;
void* temp=malloc(size);
void* arr1,*arr2;
for(i=0;i<n-1;i++)
{
arr1=data+i*size;
for(j=i+1;j<n;j++)
{
arr2=data+j*size;
if(ptr(arr1,arr2)>0)
{
memcpy(temp,arr1,size);
memcpy(arr1,arr2,size);
memcpy(arr2,temp,size);
}
}
}
free(temp);//free-----
}
int main()
{
int i;
int group_ints[10] = {4,5,3,2,3,4,6,8,9,10};
double group_doubles[10] = {12.3,4,5,3.4,1,8.12,45,23,3.14,78};
printf("ints test-----------------\n");
sort(group_ints, 10, sizeof(int),cmp_int);
for(i=0; i<10; i++)
printf("%d ", group_ints[i]);
printf("\n");
printf("dobs test-----------------\n");
sort(group_doubles, 10, sizeof(double),cmp_double);
for(i=0; i<10; i++)
printf("%.2f ", group_doubles[i]);
printf("\n");
return 0;
}
二、malloc和free
注意:若指针为全局指针,无法在全局声明就直接动态分配空间(即无法在函数体外分配空间)
1、void* malloc(size_t size) ///参数是字节数
注意:malloc出来的空间,想改变其内的值不能简单使用=(应该使用memset、memcpy、memmove),=是浅拷贝,只是改变了指向,这样做的后果是内存泄漏,而且最后free这个指针的时候,由于指向改变,也会报错
例:创建一个4个int大小的数组空间
int* arr=(int *)malloc(4*sizeof(int));//(int *)为强制类型转换
2、void free(void* ptr)//与malloc配套使用:
free(arr);
3、free使用注意事项
free的指针必须是指向malloc函数开辟出的空间的首地址
(即指针在释放前应该变回首地址,或采用另一个指针存储首地址)
4、malloc创建二维数组
1)使用数组指针形式创建
//为数组指针,int*a[]为指针数组,[]的优先级大于*;
int (*a)[2]=(int(*)[2])malloc(sizeof(int)*3*2);
//创建一个数组指针,数组存放了2个int数据。
//若多出数据两个数据则按每行2个int排列(即变成多维数组)
free(a);//释放内存
2)使用二级指针创建
int** a;int rows=2;int cols=3;
//需要malloc两次
a=(int**)malloc(sizeof(int*)*rows);
for(int i=0;i<rows;i++)
a[i]=(int*)malloc(sizeof(int)*cols);
//free,需要free两次
for(int i=0;i<rows;i++)
free(a[i]);
free(a);
5、calloc
void* calloc(int num,int size);//num为个数,size为每个的字节大小
和malloc的区别:会初始化为0,比较耗时
6、realloc
void* realloc(void* address,int newsize);//重新分配空间
三、二级指针(慎用,能不用则不用)
使用原因:由于C语言没有引用类型,如果要在函数内改变指针指向,只能使用二级指针
以下函数改变不了指针:
void changePtr(int* p)
{
p = (int*)malloc(4 * sizeof(int));
if (p)
{
for (int i = 0; i < 4; i++)
{
p[i] = i;
}
}
}
void main()
{
int* a = NULL;
changePtr(&a);
if (a)
{
for (int i = 0; i < 4; i++)
{
cout << "a[" << i << "]=" << a[i] << endl;
}
free(a);
a = NULL;
}
}
结果:空
原因:函数参数传入指针,只能改变指针指向地址存储的值,无法改变指针指向
改变指向的两种方法
1、使用指针类型返回值
int* changePtr()
{
int *p = (int*)malloc(4 * sizeof(int));
if (p)
{
for (int i = 0; i < 4; i++)
{
p[i] = i;
}
}
return p;
}
2、使用二级指针
int changePtr(int** p)
{
*p = (int*)malloc(4 * sizeof(int));
if(*p)
{
for (int i = 0; i < 4; i++)
{
(*p)[i] = i;//*p要加括号
}
}
}
结论:函数内改变指针指向要使用指针类型返回或者使用二级指针。不过使用指针返回值的方法中,局部指针变量开辟的动态空间存放在堆区,即使函数结束也不会自动释放,又因为要返回指针(返回前不能释放) 故,使用指针返回值的方法会造成内存的泄露,还是使用二级指针妥当。