指针进阶
一,字符指针
1.
常量字符串的内容不能修改
这段代码应写成:
#include
<stdio.h>
int
main()
{
const
char
* p =
"abcdef"
;
//"abcdef"是常量字符串,p中存的是a的地址;
//*p='a';常量字符串的内容不能修改。
printf(
"%c\n"
, *p);
printf(
"%s\n"
, p);
return
0;
}
2.
C/C++会将
常量字符串
存储到
单独的一块内存区域
,p1,p2指向的是同一常量字符串的时候,
指向的同一块内存
,所以地址相同。但是用相同的常量字符串来初始化多个数组
会开辟不同
的内存区域,所以arr1!=arr2.
3."abcdef"后还隐藏了一个'\0'
二,指针数组
三,数组指针
1.数组指针的格式
int
main()
{
char
* arr[5] = { 0 };
char
* (*p)[5] = &arr;
int
arr1[10] = { 0 };
int
(*p1)[10] = &arr1;
char
arr2[10] = { 0 };
char
(*p2)[10] = &arr2;
return
0;
}
--[]的优先级高于*,所以要加括号。
--char* [5],int [10],char [5]是被数组指针指向的数组的类型。
int
main()
{
int
arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int
(*p1)[10] = &arr1;
printf(
"%d\n"
, arr1[2]);
printf(
"%d\n"
, (*p1)[2]);
printf(
"%d\n"
, *(*p1 + 2));
return
0;
}输出:3,3,3
--p1存的是整个数组的地址,*p1相当于取出了
整个数组==数组首元素的地址
但是就一维数组而言,这样太复杂了
对于一维数组可以这样写:
int
main()
{
int
arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int
* p = arr1;
printf(
"%d\n"
, p[2]);
printf(
"%d\n"
, *(p + 2));
printf(
"%d\n"
, *(arr + 2));
return
0;
}输出:3,3
对于二维数组:
#include
<stdio.h>
//参数为数组;
void
print1(
int
arr
[3][4],
int
x
,
int
y
)
{
for
(
int
i = 0; i <
x
; i++)
{
for
(
int
j = 0; j <
y
; j++)
{
printf(
"%-3d "
,
arr
[i][j]);
}
printf(
"\n"
);
}
}
//参数为指针
void
print2(
int
(*
p
)[4],
int
x
,
int
y
)
{
for
(
int
i = 0; i <
x
; i++)
{
for
(
int
j = 0; j <
y
; j++)
{
printf(
"%-3d "
, *(*(
p
+ i) + j));
}
printf(
"\n"
);
}
}
void
print3(
int
(*
p
)[4],
int
x
,
int
y
)
{
for
(
int
i = 0; i <
x
; i++)
{
for
(
int
j = 0; j <
y
; j++)
{
printf(
"%-3d "
, (*(
p
+ i))[j]);
}
printf(
"\n"
);
}
}
void
print4(
int
(*
p
)[4],
int
x
,
int
y
)
{
for
(
int
i = 0; i <
x
; i++)
{
for
(
int
j = 0; j <
y
; j++)
{
printf(
"%-3d "
,
p
[i][j]);
}
printf(
"\n"
);
}
}
int
main()
{
int
arr[3][4] = { {1,2,3,4},{5,6,7,8},{10,11,12,13} };
print1(arr, 3, 4);
print2(arr, 3, 4);
print3(arr, 3, 4);
print4(arr, 3, 4);
return
0;
}
输出:
1 2 3 4
5 6 7 8
10 11 12 13
1 2 3 4
5 6 7 8
10 11 12 13
1 2 3 4
5 6 7 8
10 11 12 13
1 2 3 4
5 6 7 8
10 11 12 13
*(*(
p
+ i) + j)
--p+i表示跳几行--p是第一行整个数组的地址
--*(p+i)表示第i(从零开始)行数组首元素的地址
--*(p+i)+j表示第i行第j列元素的地址
(*(
p
+ i))[j]
--
p+i表示跳几行--p是第一行整个数组的地址
--
*(p+i)表示第i(从零开始)行数组首元素的地址
--
(*(
p
+ i))[j]
表示第i行第j列的元素
p[i][j]
--类比一维数组的传址
例题
int
(*p[10])[5]
--p[10]表示有十个元素的数组,
int
(* )[5]表示元素类型--数组指针,指向由五个整型元素构成的数组
--类比int p[10]--int表示元素类型--int [10]表示数组类型
四,数组传参,指针传参
1.一维数组传参
void
text(
int
arr
[])
{
}
void
text(
int
arr
[10])
{
}
void
text(
int
*
arr
)
{
}
void
text1(
int
*
arr1
[10])
{
}
void
text1(
int
*
arr1
[])
{
}
void
text1(
int
**
arr1
)//传递首元素的地址--地址的地址
{
}
int
main()
{
int
arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int
* arr1[10] = { 0 };
text(arr);
text1(arr1);
return
0;
}
2.二维数组传参
void
text(
int
arr
[2][5])
{}
void
text(
int
arr
[][5])//可以省略行,不能省略列
{}
void
text(
int
(*
arr
)[5])//传递首元素的地址--第一行数组的地址
{}
int
main()
{
int
arr[2][5] = {0,1,2,3,4,5,6,7,8,9};
text(arr);
return
0;
}
3.一级指针传参
void
text(
int
*
p
)
{}
int
main()
{
int
arr[10] = {0,1,2,3,4,5,6,7,8,9};
text(arr);
int
a = 0;
text(&a);
int
* p = &a;
text(p);
return
0;
}
4.二级指针传参
void
text(
int
**
p
)
{}
int
main()
{
int
* arr[10] = {0};
text(arr);
int
a = 0;
int
* p = &a;
int
** p1 = &p;
text(p1);
text(&p);
return
0;
}
五,函数指针
1.&函数名==函数名
2.函数的指针
int (*p)(int,int)=Add;
--*p--指针变量
--int (int,int)--函数的类型
--()的优先级高于*,int *p(int ,int)表示p为函数名,参数为(int,int),返回值为int*的函数声明
有没有*都一样
3.函数指针数组--转移表
#define
_CRT_SECURE_NO_WARNINGS
1
#include
<stdio.h>
double
Add(
double
x
,
double
y
)
{
return
x
+
y
;
}
double
Sub(
double
x
,
double
y
)
{
return
x
-
y
;
}
double
Mul(
double
x
,
double
y
)
{
return
x
*
y
;
}
double
Div(
double
x
,
double
y
)
{
return
x
/
y
;
}
void
menu()
{
printf(
"************************\n"
);
printf(
"***1.Add********2.Sub***\n"
);
printf(
"***3.Mul********4.Div***\n"
);
printf(
"**********0.exit********\n"
);
printf(
"请选择:>"
);
}
int
main()
{
double
x, y ;
int
input = 0;
menu();
scanf(
"%d"
, &input);
double
(*pfarr[])(
double
,
double
) = { 0,Add,Sub,Mul,Div };
do
{
if
(input >= 1 && input <= 4)
{
scanf(
"%lf%lf"
, &x, &y);
printf(
"%lf\n"
, pfarr[input](x, y));
}
else
if
(input < 0) printf(
"请重新输入\n"
);
else
if
(input == 0) printf(
"离开\n"
);
}
while
(input);
return
0;
}
4.回调函数
回调函数就是一个通过
函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
#define
_CRT_SECURE_NO_WARNINGS
1
#include
<stdio.h>
double
Add(
double
x
,
double
y
)
{
return
x
+
y
;
}
double
Sub(
double
x
,
double
y
)
{
return
x
-
y
;
}
double
Mul(
double
x
,
double
y
)
{
return
x
*
y
;
}
double
Div(
double
x
,
double
y
)
{
return
x
/
y
;
}
void
menu()
{
printf(
"************************\n"
);
printf(
"***1.Add********2.Sub***\n"
);
printf(
"***3.Mul********4.Div***\n"
);
printf(
"**********0.exit********\n"
);
printf(
"请选择:>"
);
}
void
calc(
double
(*
ph
)(
double
,
double
))
{
double
x, y;
scanf(
"%lf%lf"
, &x, &y);
printf(
"%lf\n"
,
ph
(x,y));
}
int
main()
{
int
input = 0;
do
{
menu();
scanf(
"%d"
, &input);
switch
(input)
{
case
1:
{
calc(Add);
break
;
}
case
2:
{
calc(Sub);
break
;
}
case
3:
{
calc(Mul);
break
;
}
case
4:
{
calc(Div);
break
;
}
case
0:
{
printf(
"离开\n"
);
break
;
}
default
:printf(
"请重新选择\n"
);
}
}
while
(input);
return
0;
}
Add,Sub,Mul,Div是回调函数
5.函数指针数组的指针
#include
<stdio.h>
int
Add(
int
x
,
int
y
)
{
return
x
+
y
;
}
int
main()
{
int
(*p)(
int
,
int
);
//函数指针
int
(*parr[4])(
int
,
int
);
//函数指针数组
int
(*(*pparr)[4])(
int
,
int
)=&parr;
//函数指针数组的指针
return
0;
}
六.void*
1.
void * 类型的指针变量仅保存指针的值(地址值),而忽略了指向数据的类型,因此 编译器不允许使用
“*”解引用运算,下标运算,加减指针运算.
• 作为任意类型数据的指针,即任意类型指针都可以
隐式转为 void *
• void * 类型的变量,可超越指针兼容性,
隐式转为任意类型指针(自己不这么写)
int m = 3, n = 4;
int* q = malloc(m * n *
sizeof(
int));
void* t = q;
int(*p)[4] = t;
不报错
2.void的泛用类型参数
见qsort