一、复杂指针定义? -- 二级指针。
1、什么是复杂指针?
无论是简单的指针,还是复杂指针。都是用于存放内存空间上的地址值,例如:0x100
简单指针:该地址指向一些基本数据类型 char*/int*/double* --> 字符指针/整型指针/浮点型指针
复杂指针:该地址指向一些非基本数据类型
指针/数组/函数/结构体
--> 二级指针/数组指针/函数指针/结构体指针
2、二级指针?
1)什么是二级指针?
二级指针就是指向指针变量的指针。
2)如何定义二级指针?
int a = 100;
int *pa = &a;
int **px = &pa
1>. 先写一个* *
2>. 在*后面写一个变量名 *px
3>. 确认一下你想指向的东西长什么样子的? int *pa
4>. 将第3步的结果的变量名去掉 int *
5>. 将第4步的结果写在第2步结果的前面 int **px
结果: int **px
数据类型: int **
变量名: px
3)解引用。
#include <stdio.h>
int main(int argc,char *argv[])
{
int a = 100;
int *pa = &a;
int **px = &pa;
//printf("a = %d\n",a); // a的值
//printf("pa = %p\n",pa); // a的地址
//printf("px = %p\n",px); // pa的地址
printf("*px = %p\n",*px); //*px = *(&pa) = pa = &a
printf("**px = %d\n",**px); // **px = **(&pa) = *pa = *(&a) = a = 100
return 0;
}
4)一般二级指针常见场景就是传参问题。
void func(int **px) //那么我就使用一个二级指针来接住这个变量的地址
{
}
int main()
{
int a;
int *pa = &a;
func(&pa); //你传递了一个int*变量的地址给我
}
二、复杂指针定义? -- 数组指针
1、什么是数组指针?
一个指针变量,这个指针变量是存放着整个数组的地址。
2、如何定义数组指针?
定义整型指针如下:
int a;
int *pa = &a;
分析:int *pa
数据类型: int * --> 这种类型的变量是专门用于存放整型变量的地址。
变量名:pa
定义数组指针如下:
int A[3];
int(*p)[3] = &A;
1>. 先写一个* *
2>. 在*后面写一个变量名,使用圆括号将结果括住。 (*p)
3>. 确认指针指向的内容是什么? int A[3]
4>. 将第3步的结果的变量名去掉 int [3]
5>. 将第2步的结果写在第4步结果的数据类型与元素个数之间 int(*p)[3]
结果:int(*p)[3]
数据类型: int(*)[3] --> 这种类型的变量是专门用于存放具有3个int类型数据的数组的地址
变量名: p
3、能不能这样写?
int A[3];
int (*p)[3] = &A; --> 正确 左边类型 int (*)[3] 右边类型 int (*)[3]
int (*p)[3] = A; --> 错误 左边类型 int (*)[3] 右边类型 int *
int *px = A; --> 正确 左边类型 int * 右边类型 int *
int *px = &A; --> 错误 左边类型 int * 右边类型 int (*)[3]
4、解引用。
int a = 100;
int *pa = &a;
请问*pa得到什么? -> 100
分析: *pa = *(&a) = a = 100
结论: 解引用一个整型指针,就可以得到这个指针指向的整型变量的值
int A[3] = {1,2,3};
int (*pA)[3] = &A;
请问*pA得到什么?
分析: *pA = *(&A) = A = &A[0]
结论: 解引用一个数组指针,就可以得到这个数组首元素的地址
5、已知以下代码,请问以下几个式子是什么意思?
int A[3] = {1,2,3};
int (*p)[3] = &A;
p[0] --> 数组首元素的地址
p[1] --> 段错误
(*p)[0] --> 数组首元素的值
(*p)[1] --> 数组第2个元素的值
练习: 如果有以下的说明:
int B[10];
int (*p)[10] = &B;
int *px = B;
则以下能够正确引用数组的元素的是: CEF
A. *p+1 B. *(p[1]) C. (px+3)[2] D. (*px)[3] E. (*p)[0] F. *(px+1)
三、复杂指针定义? -- 函数指针
1、什么是函数指针?
在程序运行时,函数也是在内存空间上申请空间的,所以函数也是有地址的,存放这些函数的地址的指针变量就称之为函数指针变量。
2、如何定义函数指针?
=======================已知函数,求函数指针==================
整型指针:
int a; //定义好目标
int *pa = &a; //定义指针变量
*pa求出a的空间: *pa = *(&a) = a
函数指针:
void func(int a,int b); //定义好目标
void (*pf)(int,int) = &func; //定义指针变量
等价于
void (*pf)(int,int) = func; //在C语言中,函数的名字就是函数的地址。
//所以以上的两种写法都是正确的。
*pf = *(&func) = func 等价于 &func
结论:解引用函数的指针也是得到函数的地址
分析结果:void (*pf)(int,int)
数据类型:void (*)(int,int) --> 这种类型的变量是专门用于存放返回值为void,有两个int类型的变量形式参数的函数的地址
变量名:pf
========================已知函数指针,求函数====================
已知:void (*p)(int,char)
结果:void func(int a,char b);
已知:void *(*start_routine) (void *)
结果:void *func(void *a)
例题: 求出两个数字的最大值,使用函数指针来完成。
#include <stdio.h>
//1. 定义好目标
int get_max(int x,int y)
{
return x > y ? x : y;
}
int main(int argc,char *argv[])
{
//2. 定义指针变量
int (*p)(int,int) = get_max;
//3. 只需要把函数指针变量名当作是函数名来使用即可。
//int ret = p(10,20);
int ret = (*p)(10,20);
printf("ret = %d\n",ret);
return 0;
}
四、数组作为函数参数/返回值时候,情况是怎么样的?
1、回顾学习过的传参情况。
例子1: 传递一个int类型的数据
void func(int x) //x = a
int a;
func(a); --> 传递了整个a过去
例子2: 传递普通指针地址
void fun(int *x) //x = &a
int a;
int *p = &a;
func(p); --> 传递了整个p过去
例子3: 传递函数
void func( void(*p)(int) ) // p = my_func
void my_func(int a);
func(my_func) --> 传递了函数的地址过去
2、如果传递了一个数组过去,会怎么样呢?
void func(int x[]) //x = A = &A[0]
void func(int x[3]) //x = A = &A[0]
void func(int *x) //x = A = &A[0]
{
}
int main()
{
int A[3];
func(A); //A是数组名
//这里没有sizeof()
//所以A在这里,是代表数组首元素的地址
其实以上的这行代码,等价于
func(&A[0]);
}
练习: 指针习题(C语言).doc 9 10
五、数组作为返回值的情况,会怎么样呢?
#include <stdio.h>
char* func()
{
char A[10] = {'a','b','c','d'}; //栈区
printf("A = %p\n",A); //打印首元素的地址 0x7ffe9cbee030
printf("A = %s\n",A); //以字符串形式打印数组的内容 abcd
return A;
//数组的空间全部都会释放
}
int main(int argc,char *argv[])
{
char *p = func();
printf("p = %p\n",p); //打印首元素的地址 null
printf("p = %s\n",p); //以字符串形式打印数组的内容 null
return 0;
}
结果:
gec@ubuntu:/mnt/hgfs/GZ2180/01 C语言/07/code$ ./return_array
A = 0x7ffe9cbee030
A = abcd
p = (nil)
p = (null)
如果真的有必须返回数组,那么怎么办?
1)将数组设置为全局变量。
#include <stdio.h>
char A[10] = {'a','b','c','d'}; //数据段
char* func()
{
//char A[10] = {'a','b','c','d'}; //栈区
printf("A = %p\n",A); //打印首元素的地址 0x7ffe9cbee030
printf("A = %s\n",A); //以字符串形式打印数组的内容 abcd
return A;
//数组的空间全部都会释放
}
int main(int argc,char *argv[])
{
char *p = func();
printf("p = %p\n",p); //打印首元素的地址 null
printf("p = %s\n",p); //以字符串形式打印数组的内容 null
return 0;
}
2)在数组前面添加一个static,让这个局部变量变成静态变量。
静态变量也是在数据段申请空间,不会在栈区申请。
#include <stdio.h>
char* func()
{
static char A[10] = {'a','b','c','d'}; //数据段
printf("A = %p\n",A); //打印首元素的地址 0x7ffe9cbee030
printf("A = %s\n",A); //以字符串形式打印数组的内容 abcd
return A;
//数组的空间全部都会释放
}
int main(int argc,char *argv[])
{
char *p = func();
printf("p = %p\n",p); //打印首元素的地址 null
printf("p = %s\n",p); //以字符串形式打印数组的内容 null
return 0;
}
六、二维数组。
1、什么是二维数组?
二维数组在内存中呈线性排列,不存在行与列的关系。
二维数组其实也是一个一维数组,只是这个一维数组每一个成员都是一个一维数组。
例子:
整型数组 int A[3] --> 每一个成员都是int类型的数据
二维数组 数组 A[3] --> 每一个成员都是数组来的
2、定义数组时,必须交代好两件事情。
1)数组元素的个数
2)数组中每一个元素的数据类型。 --> 基本数据类型/非基本数据类型
定义数组的步骤:
例子1: 定义具有5个int类型数据的数组。
1>. 确定数组名 A
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面 A[5]
3>. 确定数组中每一个成员是什么 int x
4>. 将第3步的结果的变量名去掉 int
5>. 将第4步的结果写在第2步结果的前面就可以 int A[5] --> 最终结果
例子2:定义一个二维数组,有2个成员,每一个成员都是具有3个int类型的数据的数组。
1>. 确实数组名 B
2>. 确定数组中元素的个数,使用[]括号括住他,然后跟在数组名后面 B[2]
3>. 确定数组中每一个成员是什么 int x[3]
4>. 将第3步的结果的变量名去掉 int [3]
5>. 将第2步的结果放在第4步结果的中间 int B[2][3] -> 最终结果
分析结果:
int B[2][3]
代表这个数组有2个成员,每一个成员都具有3个int类型数据的数组。
3、二维数组的赋值?
int B[2][3] = {{1,2,3},{4,5,6}}; --> 整体赋值
B[0][0] = 1; --> 单个赋值
B[0][1] = 2;
....
B[1][2] = 6;
int B[][3] = {{1,2,3},{4,5,6}}; --> 2可以省略,因为有两个初始化列表,决定了数组有2个成员。
//int B[2][] = {{1,2,3},{4,5,6}}; --> 3不可以省略,因为如果省略了,编译器无法知道数组每一个成员多大。
4、研究二维数组的名字
具体分析: 二维数组名字的理解.jpg
5、解引用。
int B[2][3] = {{1,2,3},{4,5,6}};
1)解引用二维数组的名字,可以得到什么?
*B = *(&B[0]) = B[0] = &B[0][0] --> 可以得到二维数组首元素的首元素的地址
2)解引用二维数组首元素的名字,可以得到什么?
*B[0] = *(&B[0][0]) = B[0][0] --> 可以得到二维数组首元素的首元素的值
练习: 指针习题(C语言).doc 11 12 13
七、指针数组。
1、什么是指针数组? 什么是数组指针?
数组指针是一个指针来的,这个指针是指向一个数组的。
指针数组是一个数组来的,这个数组中每一个成员都是一个指针。
2、如何定义指针数组?
例子1: 定义具有5个int类型数据的数组。
1>. 确定数组名 A
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面 A[5]
3>. 确定数组中每一个成员是什么 int x
4>. 将第3步的结果的变量名去掉 int
5>. 将第4步的结果写在第2步结果的前面就可以 int A[5] --> 最终结果
例子2: 定义具有5个int*类型数据的数组。
1>. 确定数组名 B
2>. 确定数组中元素的个数,使用[]括号括住它,然后跟在数组名后面 B[5]
3>. 确定数组中每一个成员是什么 int *x
4>. 将第3步的结果的变量名去掉 int *
5>. 将第4步的结果写在第2步结果的前面就可以 int *B[5]
3、以下几句话是什么意思?
int x[5]; -> 整型数组 在内存中连续申请20个字节,然后使用变量x间接访问这片空间
int *p[5]; -> 指针数组 在内存中连续申请40个字节,然后使用变量p间接访问这片空间
int (*p)[5]; -> 数组指针 在内存中连续申请8个字节,然后使用变量p间接访问这片空间