本文主要介绍在指针使用过程中容易出现的错误
目录
理解名词:
一个十六进制地址占用的是一个字节
p[0] = *(p+0)
char *p (字符指针) -- int *p(整形指针) -- int p[10](整形数组)
int *p [10] (指针数组:数组里边有10个元素,每个元素类型是int *)
int (*p)[10] (数组指针:*p是一个指针,指向一个数组,数组里边有10个元素,每个元素类型是int)
int *p() (指针函数)
int (*p)(int,int) (函数指针)
int(*p[10])(int)(函数指针数组)
int (*p[10])[5] (一个数组,数组里边有10个元素,每个元素类型是数组指针,该数组指针指向5个元素,每个元素类型是int )对于这种类型数据,先找到主语---数------拿出来-----剩下的int (*p)[5]-----数组指针
void *:可以接受任意类型的地址,不能解引用操作,不能进行++--等操作,如果要解引用操作,需要进行强制转换(比如*(int *)p)
1.字符指针(常量字符串赋值给指针)
(1)先看下列代码
#include<stdio.h>
int main(int argv,char *argc[])
{
char *p = "abcdef";
printf("%s\n",p);
printf("%c\n",*p);
*p = 'A';
return 0;
}
输出结果
上述结果表明:指针存放的是字符串元素的首地址,而并非整个字符串(指针在32位/64系统中占4/8字节)
为什么会出现段错误呢,那是因为“abcdef”是一个常量字符串,是不可以被更改的
(2)再看下列代码
#include<stdio.h>
int main(int argv,char *argc[])
{
char arr1[] = "abcdef";
char arr2[] = "abcdef";
char *p = "abcdef";
char *n = "abcdef";
if(arr1 == arr2){
printf("arr1 == arr2\n");
}
else{
printf("arr1 != arr2\n");
}
if(p == n){
printf("p == n\n");
}
else{
printf("p != n\n");
}
return 0;
}
输出结果
上述结果表明:指针赋值的字符串是常量字符串,在我们操作系统中为了节约内存,常量字符串只会保存一次。即指针p和指针n都指向a(字符串元素首地址)
而字符串数组可以多个开辟。
2.指针数组的使用(存放指针的数组)
int * arr【10】(根据优先级先arr先结合【10】,arr[10]存放int*)
#include<stdio.h>
int main(int argv,char *argc[])
{
char arr1[] = {1,2,3,4};
char arr2[] = {5,6,7,8};
char arr3[] = {9,10,11,12};
char *p[3] = {arr1,arr2,arr3};
for(int i = 0;i < 3;i ++)
{
for(int j = 0;j < 4;j ++)
{
printf("%d",*(p[i]+j));
}
printf("\n");
}
// printf("%d\n",*(p[0]+1));
return 0;
}
3.数组指针(指向数组的指针)
int (* arr)【10】(根据优先级先arr先结合*,指针*arr指向int 【10】)
假设一位数组int arr[10]
那么int *p = arr; 则arr[i] == p[i] == *(p+i) == *(arr+i)
假设二维数组int arr[3][5]
那么int arr[3][5] = int (*p)[5] 则 ----> arr[i][j] == p[i][j] == *(*(p+i) + j) == *(p[i] + j)
4.函数指针
对比数组指针理解函数指针
int (*p)(int a,int b)
int add(int a,int b)
{
return a+b;
}
int main()
{
int a = 10;
int b =20;
//数组指针int (*p)[10]
int (*p)(int,int) = add; //函数指针 指针p指向add,*p相当于调用
printf("sum = %d\n",(*p)(2,3));
printf("sum = %d\n",p(2,3)); //*没有实际意义
}
1)看下列代码段(*(void (*)())0)();相当于一次函数调用
(*(xxx)xxx)()表示调用
2)
signal是一个函数声明
该函数的返回类型是函数指针,该函数指针指向参数int,返回类型是void
该函数的第一个参数是,第二个参数的类型是函数指针,该函数指针指向int,返回类型是void
注意:用typedef重新命名函数指针
void (*signal(int, void(*)(int)))(int);
typedef void (*function)(int);
function signal(int,function);
5.函数指针数组
指针数组是用来存放指针,函数指针数组是用来存放函数指针,两个是有区别的
函数指针数组表示:void(*p[4])(int,int) / void(*p[4])()........
初始化
//void(*p[4])(int,int);
void SUM(int,int)
{}
void SUB(int,int)
{}
void MUL(int,int)
{}
void DIV(int,int)
{}
int main(int,int)
{
void(*p[4])(int,int) = {SUM,SUB,MUL,DIV};
}
使用示例:
1.char *(*pf)(char *,const char *)
2.char *(*pf[4])(char *,const char *)
用于计算器加减乘除这种相同参数,不同函数的场景(转移表)
6.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。(并不是直接调用本函数--间接)
#include <stdio.h>
//回调函数
int ADD(int (*callback)(int,int), int a, int b){
return (*callback)(a,b);//此处回调add函数...
}
//普通函数
int add(int a, int b){
return a + b;
}
int main(void){
printf("%d\n",add(1,2));
printf("%d\n",ADD(add,1,2));
return 0;
}
7.传参
二维数组传参-----二维数组的数组名 如果表示首元素则是数组的第一行
void test(int arr[5][10])
{}
void test1(int arr[][10])
{}
void test3(int arr[5][]) //error
{}
void test4(int arr[][]) //error
{}
void test5(int **arr) //error 二级指针用来存放一级指针的变量的地址,arr对于二维数组来说就是第一行数组的地址,数组的地址不能存放在二级指针里边
{}
void test6(int (*arr)[10])//指向数组的指针,可以用来存贮一位数组
{}
int main()
{
int arr[5][10] = {0};
test(arr);
test1(arr);
test6(arr);
}
二级指针传参
void test(char **p)
{
printf("%c\n",**p);
}
int main()
{
char a = 'b';
char *p = &a;
char **pp = &p;
char *buff[32] = {p};
test(pp); //用二级指针传参
test(&p); //用一级指针的地址传参
test(buff);//用指针数组传参
}
函数指针传参(指向函数的指针,用来存放函数的地址)
int add(int a,int b)
{
return a+b;
}
int main()
{
int a = 10;
int b =20;
//数组指针int (*p)[10]
int (*p)(int,int) = add; //函数指针 指针p指向add,*p相当于调用
printf("sum = %d\n",(*p)(2,3));
printf("sum = %d\n",(p)(2,3)); //*其实没有实际意义
}