基础
- 内存:是通过连续的内存编号来管理的(内存的地址)
- 指针:指针变量里边存储的就是内存地址
值和类型
不能简单通过检查一个值的位来判断它的类型,值得类型取决于其使用方式
未初始化和非法指针
int *a;
*a = 12;
访问未初始化的指针是非法的
指向指针的指针
int a = 12;
int *b = &a;
int **c = &b;
*操作符具有从右向左的结核性,所以这个表达式相当于*(*c)
定义
*a 间接访问
通过一个指针访问它所指向的地址的过程称为间接访问(indirection)或解引用指针(dereferencing the pointer),这个用于执行间接访问的操作符是单目操作符*
int a = 10;
int *p = &a;
-
int *p
表示:表达式*p产生的结果类型是int类型 -
int *
并不是一个类型,原因如下
int* b,c,d;
这并不是将三个变量声明为指向整型的指针,b是一个指针,其余两个变量为普通整型
&b 取地址符
int *pb = &b;// & scanf函数中有一个取地址符指针变量可以通过地址赋值
%p 指针占位符
printf("%p\n",p);
NULL指针
对一个NULL指针进行解引用操作是非法的
NULL类似于给普通变量赋初始值的0,其实就是地址为0
int *p =NULL;//定义了一个指针变量,初始值为NULL
float*p1 = NULL;//浮点型指针
double *p2 =NULL;
char *p3 =NULL;
指针的算术运算
int *pp =NULL;
int num1 = 10;
pp = &num1;
printf("%p\n",pp);
printf("%p\n",(pp+1));
指针的算术运算需要根据指针类型去计算,指针+ 1是向高位移动一个类型所占的字节数,指针- 1是向低位移动一个类型所占的字节数
printf("%p\n",pp++);
printf("%p\n",++pp);
printf("%p\n",pp);
++ --还是遵循之前++ --运算符法则
指针的重指向
咱们可以让指针重新指向另一个变量(内存的另一块区域),这就叫指针的重指向
我们可以通过指针修改内存地址上的值
int number1 = 10;
int *pNum1 = &number1;
int number2 = 8;
pNum1 = &number2;
*pNum1 = 9;
printf("%d\n",*pNum1);
printf("%d\n",number2);
用指针交换两个数的值
- 函数在传参数的时候,形式参数只是拷贝了实际参数的内容或者值,实际参数并未发生改变
- 有了指针我们可以直接操作指针指向的内存区域,然后修改内存中的值
int num1 = 12;
int num2 = 16;
swapTwoNumbers(&num1, &num2);
printf("%d %d\n",num1,num2);
指针与数组的关系
数组名就是数组的地址,它恒等于数组元素的首地址
实际上*(arr1 + i) = arr[i];
int arr1[4] = {1,2,3,4};
for(inti = 0; i < 4; i++) {
printf("%p\n",arr1+0);
}
printf("%lu\n",sizeof(arr1));//数组名虽然是地址,但是我们利用它计算内存空间的话,还是根据里边存储的数据类型而定
int *pp = &num1;
printf("%lu\n",sizeof(pp));//打印一个单纯的变量地址的话,并不是根据类型而定
printf("%lu\n",sizeof(&num1));
char aa = 'w';
char *pp1 = &aa;
printf("%lu\n",sizeof(pp1));//指针类型所占的字节数与计算机本身的计算位数有关,如果是32位的计算机,指针类型占4个字节,如果是64位的计算机,指针类型占8个字节
short array[4] = {1,2,3,4};
int *p = array;//不匹配的指针类型
数组名确实能够打印出数组元素的首地址,但是如果把数组名认为就是地址是不准确的,实际上数组名是一个常量
指针与字符串的关系
char name[] ="guozhenyan";
char *p1 = name;
printf("%p\n",p1);
for(int i = 0; i <strlen(name); i++) {
printf("%c\n",*(p1+i));
}
指针数组
char *a[10] = {"ddd","sdq","das","qwe","cva","btg","aaa","ccc","eee","fff"};
printf("%p\n",a);//数组指针的地址
printf("%p\n",a+1);//对数组指针地址进行操作
printf("%p\n",a[0]);//对存的数据地址进行操作
printf("%p\n",a[1]);
printf("%p\n",*a);//存的是地址
printf("%p\n",*(a+1));
printf("%c\n",**a);
printf("%c\n",**(a+1));
printf("%c\n",*a[0]);
printf("%c\n",*a[1]);
结构体指针
typedef struct person {
char name[20];
int age;
int score;
}Person;
Person p1 = {"guangguang",16,90};
Person p2 = {"jintao",18,99};
Person p3 = {"wangce",17,88};
void printArr(Person*p,intcount){
for(int i = 0; i < count; i++) {
printf("%s,%d,%d\n",(*(p+i)).name,(p+i)->age,(*(p+i)).score);
}
//使用指针去访问结构体数组的成员变量的时候,可以使用->
Person arr[3] = {p1,p2,p3};
函数指针
函数名跟数组名类似,实际上它也是一个地址
printf("%p\n",helloWorld);
我们也可以找一个指针,指向函数名所在的地址.这个指针就是所谓的函数指针
函数的指针类型根据函数的返回值和参数而定
返回值类型+ (*指针名) +参数的类型=函数指针
void (*p1)() =NULL;
p1 =helloWorld;//声明
p1();//调用方式
void (*p2)(int) =NULL;
p2 =printNumber;
p2(31415);
BOOL (*p3)() =backYes;
printf("%d\n",p3());
int (*p4)(int,int) =sum;
printf("%d\n",p4(8,4));
函数指针可以重新指向别的函数,只要函数类型一样就可以重指向
char a[10];
printf("请输入函数名:\n");
scanf("%s",a);
int(*p5)(int,int) =NULL;
printf("请输入a,b的值:\n");
int b,c;
scanf("%d%d",&b,&c);
if(strcmp(a,"maxValue") == 0){
p5 =max;
}else if(strcmp(a,"sum") == 0){
p5 =sum;
}else{
printf("输入有误\n");
return 0;
}
//现在p5这个指针并没有指向认可地址,也可以导致程序崩溃,这种情况叫作野指针
printf("%d\n",p5(b,c));
替换指针类型
typedef int (*pGet)(int,int);//重新定义函数指针的类型:pGet
//我们利用typedef这个关键字还可以重新定义函数指针的类型变成一个新的类型*后边跟上我们新类型的名字
函数指针的作用
- ️函数指针是OC中block的底层实现,OC中block得形式跟函数指针一模一样.
- ️函数指针因为可以重指向不同函数,可以起到简化代码和解耦合的作用
函数回调(用函数指针作为函数参数)
int(*p)(int,int) =NULL;
p = max;//函数指针指向的函数可能实现的功能比较复杂,或者是公司核心代码,我们一般程序员不需要知道里面的详细内容,只需要会调用即可.(面向对象语言,封装特性的底层(雏形))
函数转移表
//常规写法
switch ( oper ){
case ADD:
result = add( oper1, oper2 );
break;
case SUB:
result = sub( oper1, oper2 );
break;
case MUL:
result = mul( oper1, oper2 );
break;
case DIV:
result = div( oper1, oper2 );
break;
...
}
//使用转移表之后的写法
double add( double, double );
double sub( double, double );
double mul( double, double );
double div( double, double );
...
double (*oper_func[])( double, double ) = {
add, sub, mul, div, ...
};
//下面的语句替换前面整条switch语句
result = oper_func[ oper ]( op1, op2 );
范指针
void *p;
memcpy;
类型转换
fptr = (float *) iptr;