#参考江科大C语言视频#
一、指针简介
指针(Pointer)
- 指针是C语言的一个重要知识点,其使用灵活、功能强大,是C语言的灵魂
- 指针与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问
1.1 计算机存储机制
- int a = 0x12345678
int 代表4个字节的数据,4*8 = 32个2进制来表示
a 可以表示为12 34 56 78
如上表所示:最低位78,被分配到了第一个地址;56,被分配到了第二个地址……
这种分配方式把小端放在了内存地址的第一位,被称为小端模式
- short b = 0x5A6B
short 是2字节数据
- char c[ ] = {0x33, 0x34, 0x35}
char 为数组,数组是按照顺序来存储的
1.2 定义指针
指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针。
定义指针变量:
数据类型 | 指向该数据类型的指针 | ||
(unsigned) char | 1字节 | (unsigned) char * | x字节 |
(unsigned) short | 2字节 | (unsigned) short * | x字节 |
(unsigned) int | 4字节 | (unsigned) int * | x字节 |
(unsigned) long | 4字节 | (unsigned) long * | x字节 |
float | 4字节 | float * | x字节 |
double | 8字节 | double * | x字节 |
16位系统:x=2,32位系统:x=4,64位系统:x=8
指针所占用的位宽 = 系统的位宽
eg1.1.
#include <stdio.h>
int main(void)
{
int a;
int *p;
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(p));
return 0;
}
执行结果:
4
8
sizeof() 函数的返回值是输入变量的字节
a是int型,所以为4字节;p是地址,且系统为64位,所以是8字节
1.3 指针操作
若定义:
- int a; //定义一个int型的数据
- int *p; //定义一个指向int型数据的指针
则对指针p有如下操作方式:
操作方式 | 举例 | 解释 |
取地址 | p=&a; | 将数据a的首地址赋值给p |
取内容 | *p; | 取出指针指向的数据单元 |
加 | p++; | 使指针向下移动1个数据宽度 |
p=p+5; | 使指针向下移动5个数据宽度 | |
减 | p--; | 使指针向上移动1个数据宽度 |
p=p-5; | 使指针向上移动5个数据宽度 |
eg1.2.
#include <stdio.h>
int main(void)
{
char a=0x66;
char *p;
p=&a;
printf("%x\n",a);
printf("%x\n",p);
printf("%x\n",*p);
return 0;
}
输出结果:
66
61fe17
66*p的意思是 把p的内容作为一个地址然后去找地址的内容
eg1.3.
#include <stdio.h>
int main(void)
{
int a=0x66;
int *p;
p=&a;
printf("%x\n",a);
printf("%x\n",p);
printf("%x\n",*p);
p++;
printf("%x\n",*p);
return 0;
}
输出结果:
66
61fe14
66
61fe18为什么执行完p++后地址多了4?
- 因为p++指的是使指针向下移动1个数据宽度,int型是4字节,所以应该是61fe18
若是定义short型,那就是+2;定义char型,就+1.
1.4 数组与指针
数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于申请内存、定义指针和初始化。
例如: char c[] = {0x33, 0x34, 0x35} ;
等效于: 申请内存
定义char *c = 0x4000;
初始化数组数据
利用下标引用数组数据也等效于指针取内容
例如: c[0]; 等效于: *c;
c[1]; 等效于: *(c+1);
c[2]; 等效于: *(c+2);
eg1.4.
#include <stdio.h>
int main(void)
{
char a[]={0x33,0x34,0x35};
printf("a[0]=%x\n",a[0]);
printf("a[1]=%x\n",a[1]);
printf("a[2]=%x\n",a[2]);
printf("*a=%x\n",*a);
printf("*(a+1)=%x\n",*(a+1));
printf("*(a+2)=%x\n",*(a+2));
return 0;
}
输出结果:
a[0]=33
a[1]=34
a[2]=35
*a=33
*(a+1)=34
*(a+2)=35数组名就是数组的首地址
#include <stdio.h>
#include <stdlib.h>
int a[]={0x33,0x34,0x35};
//等效于
int a;
a=malloc(3*4);//申请内存(3个变量*4个字节),其返回值为void*,可以表示任何类型的指针
*a=0x33;
*(a+1)=0x34;
*(a+2)=0x35;
1.5 注意事项
- 在对指针取内容之前,一定要确保指针指在了合法的位置,否则将会导致程序出现不可预知的错误。
- 同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告。
二、指针的应用
传递参数
- 使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用
- 使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计
传递返回值
- 将模块内的公有部分返回,让主函数持有模块的“句柄",便于程序对指定对象的操作
直接访问物理地址下的数据
- 访问硬件指定内存下的数据,如设备ID号等
- 将复杂格式的数据转换为字节,方便通信与存储
2.1 传递参数
eg2.1.
#include <stdio.h>
void fun(int param)
{
printf("%x\n",param);
}
int main(void)
{
int a=0x66;
fun(a);
return 0;
}
其中,a和param两个变量存储在两个不同的地址
值传递,数据复制,不会影响主函数变量;弊端:数据过大,占用内存
eg2.2.
#include <stdio.h>
int FindMax(int *array,int length)
{
int i;
int max=array[0];
for(i=1;i<length;i++)
{
if(array[i]>max)
{
max=array[i];
}
}
return max;
}
int main(void)
{
int a[]={2,1,6,9,7};
int Max;
Max=FindMax(a,5);
printf("Max=%d\n",Max);
return 0;
}
指针传递,在子函数中更改数组的值,会影响主函数原来的数组
可以在子函数调用时,在前面加上 const ,说明该调用为常量,为只读,不能修改
eg2.3.
#include <stdio.h>
void FindMaxAndCount(int *max, int *count, int *array, int length)
{
int i;
*max=array[0];
*count=1;
for(i=1;i<length;i++)
{
if(array[i]>*max)
{
*max=array[i];
*count=1;
}
else if(array[i]==*max)
{
(*count)++;
}
}
}
int main(void)
{
int a[]={2,1,6,9,9,2};
int Max;
int Count;
FindMaxAndCount(&Max,&Count,a,5);
printf("Max=%d\n",Max);
printf("Count=%d\n",Count);
return 0;
}
用指针实现多返回值
主函数中定义变量Max,函数调用&Max,取Max地址赋值给了int *max,为指针变量,*max取值,即为Max。
2.2 传递返回值
eg2.4.
#include <stdio.h>
/***************************/
int Time[]={16,51,20};
int *GetTime(void)
{
return Time;
}
/***************************/
int main(void)
{
int *pt;
pt=GetTime();
printf("pt[0]=%d\n",pt[0]);
printf("pt[1]=%d\n",pt[1]);
printf("pt[2]=%d\n",pt[2]);
return 0;
}
通过返回值式指针,获取句柄,从而实现数组的访问。
注意:不能返回局部变量,局部变量在函数执行完后会销毁。