前言
在笔试的过程中,楼主发现自身的指针知识不牢固,所以重新复习C语言中的指针
一、指针是什么?
指针是C语言用来存储地址,自身的地址由系统分配,地址偏移与系统有关(64位为8字节,32位为4字节)。
1.指针与const
常指针:指向不能改变
int * const p;
常目标指针 指向的内容不能改变
const int *p;
int const *p
指向和指向的内容不能改变
const int const *p;
2.指针与数组
2.1指针与数组关系
数组名类似常指针 char * const q; 代表数组首元素 &str代表整个数组的首地址
char str[]="123456";
str++; //达咩,出错!
指针为动态分配的,数组为系统分配
char a[]="123456"; //存放在栈
char *p=&a;
a[0]='1'; //ok
对指针存储的字符串使用 sizeof 大小与存放的内容无关(数组作为参数传递时为指针)
char *p =“123456789”; //指向常量(静态存储区),自动补齐const(c99)
sizeof(p)结果为指针大小(4或8)
p[0]='0'; //达咩,出错! 常量不能修改
对数组的sizeof 大小与内容有关
char a[]="123456";
sizeof(a)结果为7; //包括字符串后的‘/0’
2.2 数组指针
int (*p)[];
2.3 指针数组
int *p[];
int a[3][4]={{1,2,3,4},{2,3,4,5},{3,4,5,6}};
int *p[3]={a[0],a[1],a[2]); //p[i][j] *(*(p+i)+j) *(p[i]+j) 三者等价
3.指针和函数
函数名为函数所占用内存的首地址。
3.1函数指针
int (*p)(int) //函数指针,指向参数为int,返回值为int的函数
使用如下
int add(int x,int y)
{
return x+y;
}
int main()
{
int (*q)(int,int);
q=add;
printf("%d",q(1,2));
return 0;
}
题外: f() 意味参数可以为任意个。没有参数为void
多用作回调函数(信号函数,内核中断服务......)
int select_down(int x,int y)
{
return x>y?1:0;
}
int select_up(int x,int y)
{
return x<y?1:0;
}
/*@function交换两数*/
void swap(int *a,int *b)
{
*a=*a+*b;
*b=*a-*b;
*a=*a-*b;
}
/*@function 冒泡函数
*@param - (*p)(int,int) : select_down :降序排 select_up: 升序排
*@param - a :待排序列
*@param - size :待排序列长度
*/
void bubble_sort(int *a,int size,int (*p)(int,int))
{
int i,j,tmp,key=0;
for(i=0;i<size-1;i++)
{
key=0;
for(j=0;j<size-1-i;j++)
//if(a[j+1]<a[j]){
if(p(a[j],a[j+1])){
swap(&a[j+1],&a[j]);
}
if(key=0)break;
}
}
3.2指针函数
函数返回类型为指针。 // tips!!不能返回函数局部变量的地址(栈内存)。
int * fun();
tips:函数对局部变量的回收需要一定的时间的,有时候返回结果看似成功,其实是是因为局部变量还没回收完毕,再sleep(1)等待一会,他的值就会被回收而改变。故指针函数的返回地址一定不能是该函数栈内存,可以为动态申请的堆空间或者静态存储区空间。
题外: (如果要修改实参的值,必须传递地址)函数参数为地址传递(即指针时),其参数指针指向真实变量的的地址(非该函数的栈空间),其经过地址变换或者未变换都可以用作返回。传递数组给函数时作为指针传递,一般需要再加数组长度作为形参。
二、指针作用
1.节省空间
当传入函数的参数的类型大小过大时,可以通过指针传地址的方式节省空间。
64位传入指针大小为8,32位指针大小为4;
(示例):
static struct T
{
int value;
char string;
char a[10];
};//占16字节,实际在应用中可能更大
void fun(struct *ptr)
{
printf("%d",sizeof(ptr));
//ptr->value=1; 使用结构体成员
}
int main()
{
struct T test;
printf("%d",sizeof(T));
fun(&test);
return 0;
}
2.使得函数修改值不止一个,同时腾出返回值用作运算的状态(出错或者其他情况)
函数的返回值只有一个或者没有,可以通过指针来使得函数可以修改多个值并回馈给主函数;
(示例):
int fun(int *s,int size,int *max,int *min)
{
ret=0;
for(int i=0;i<size-1;i++){
if(*max<s[i]) *max=s[i];
if(*min>s[i]) *min=s[i];
}
if(*min>*max) ret=-1;
return ret;
}
3.用作命令行参数
int main (int argc, const char **argv)
./a.out test go on
此时文件本身a.out为argv[0]; argv[1]为test argv[2]为go argv[3]为on
argc范围为1~4;argc最小为1,即本身
三、指针使用
空指针 指针指向NULL(0地址)
野指针 指针指向未知的,不能被访问的地址 (对指针的不正确使用会造成)
1.指针申请和释放内存
定义指针最好初始化内存,避免野指针而造成运行段错误(访问了不能用的内存)
(1)用户动态申请
/*申请内存时要注意4点
*1.有申请就要有释放,避免内存泄漏
*2.释放后指针让其指向NULL
*3.不要重复释放
*4.malloc申请的地址,释放时就释放对应的地址,即申请的指针地址不能改变
*/
int *p=NULL; //初始化指针给地址,或者空
p=(int *)malloc(8*sizeof(int));
free(p);
p=NULL;
(2)指向现有的内存
int a=1;
int *p =&a;
2.指针运算
(1)指针自加减
意为增加或者减去几个‘’单元‘’
char a[10];
char *p=a; p++; 偏移1个字节
int a[10];
int *p=a; p++; 偏移4个字节
(2)两个指针减法
int a[10]={0,1,2,3,4,5}
int *p=a; int*q=&a[4]
printf("%d", q-p); //打印结果为4 ,意味能放几个东西 地址实际相差16字节;
(3)指针与解引用*的运算
int x=3;y=0, *p =&x;
y=*p+5; //y=8
y=++*p; //y=4
y=*p++; //y=3,p++;
四、指针分析
对指针的分析最好通过画内存图来进行分析;
附录:
用变量a给出下面的定义
(1)一个整型数: int a。
(2)一个指向整型数的指针(一重指针): int *a。
(3)一个指向指针的的指针,它指向的指针是指向一个整型数的指针(二重指针): int **a。
(4)一个有10个整型数的数组 :int a[10]。
(5)一个有10个指针的数组,这10个指针是指向整型数的(指针数组): int *a[10]。
(6)一个指向有10个整型数数组的指针(数组指针):int (*a)[10]。
(7)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(函数指针):int (*a)(int)。
(8)一个有10个指针的数组,这10个指针均指向函数,该函数有一个整型参数并返回一个整型数(函数指针数组): int (*a[10])(int)。
总结
C语言的指针是C的精华,好好学习掌握对linux编程应用有很大的作用,同时也可以加强对内存的理解应用。