一、指针数组和数组指针
二、多维数组和多维指针
三、函数与指针
一、指针数组和数组指针
- 饭前思考
我们先来看一段代码:
#include <stdio.h>
void test_pointer1()
{
int array[5];
int matrix[5][5];
int* pa = array;
int* pm = matrix;
}
int main()
{
test_pointer1();
return 0;
}
这里用两个int类型的指针指向两个数组,一个是一维的,一个是二维的,这里编译器没有报错,但是我们想这样是否存在问题?我们已经知道array代表首元素地址,那么matrix又代表什么呢?
- 数组类型定义
数组类型该如何定义呢?int类型数组是int类型吗?C语言中的数组有自己特定的类型:
(1)数组的类型由元素类型和数组大小共同决定
例:int array[5]的类型为int[5]
(2)C语言用typedef重定义数组类型:
typedef type(name)[size];
例如:
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
有了重定义,那么我们可以这样来定义数组了:
AINT5 array_int_5;
AFLOAT10 array_float_10;
void test_pointer2()
{
int int_array[5];
AINT5* p_int_5 = &int_array;
int(*p_int_5_2)[5] = &int_array;
}
-
数组指针
(1)数组指针用于指向一个数组
(2) 数组名是数组首元素的起始地址,但并不是数组的起始地址 但并不是数组的起始地址
(3) 通过将取地址符&作用于数组名可以得到数组的起始地址
(4)可通过数组类型定义数组指针: ArrayType* pointer; 也可以直接定义:type (*pointer)[n];
pointer为数组指针变量名
type为指向的数组的类型
n为指向的数组的大 -
指针数组
(1)指针数组是一个普通的数组
(2) 指针数组中每个元素为一个指针
(3)数组指针的定义:type* pArray[n];
type*为数组中每个元素的类型
pArray为数组名
n为数组大小
我们来看一个例子:
#define SIZEOF(A) ((sizeof(A)) / sizeof(*A))
const char* array[] = {
"china",
"japan",
"america",
"indian"
};
int test_pointer3(int size, const char* array[], const char* key)
{
int i = 0;
int ret = -1;
for(i=0; i<size; i++)
{
if(strcmp(key, array[i]) == 0)
{
ret = i;
break;
}
}
return ret;
}
int main()
{
int ret = -1;
if ((ret = test_pointer3(SIZEOF(array), array, "china")) != -1)
{
printf("find key %s index = %d in array\n", array[ret], ret);
}
ret = -1;
if ((ret = test_pointer3(SIZEOF(array), array, "japan")) != -1)
{
printf("find key %s index = %d in array\n", array[ret], ret);
}
return 0;
}
这里我们用一个char* []类型的指针数组指向一个字符串数组,在函数内部我们可以对数组指针进行操作,来看运行结果:
这里我们提一下main函数在运行时候携带的参数:
int main()
int main(int argc)
int main(int argc, char *argv[])
int main(int argc, char *argv[], char *env[])
argc – 命令行参数个数
argv – 命令行参数数组
env – 环境变量数组
我们直接看最复杂的一种情况:
int main(int argc, char *argv[], char *env[])
来看一个例子:
int main(int argc, char* argv[], char* env[])
{
int i = 0;
printf("============== Begin argv ==============\n");
for(i=0; i<argc; i++)
{
printf("%s\n", argv[i]);
}
printf("============== End argv ==============\n");
printf("\n");
printf("\n");
printf("\n");
printf("============== Begin env ==============\n");
for(i=0; env[i]!=NULL; i++)
{
printf("%s\n", env[i]);
}
printf("============== End env ==============\n");
}
这里会将所有的命令行参数和环境变量打印出来,来看看运行结果:
可以看到我们传入的1234 5678这两个命令行参数和所有环境变量都被打印出来。
二、多维数组和多维指针
- 指向指针的指针
指针实际上也是一种变量,既然是变量就具有内存,也就具有地址,那指针的地址该由谁来存放呢?
没错,就是指针的指针,来看一段代码:
void test_pointer5()
{
int a = 10;
int b = 20;
int* p = NULL;
int** pp = NULL;
p = &a;
pp = &p;
printf("p = 0x%x, pp = 0x%x, *pp = 0x%x\n", p, pp, *pp);
}
运行结果:
- 二维数组和二级指针
(1)二维数组在内存中以一维的方式排布
(2)二维数组中的第一维是一维数组
(3)二维数组中的第二维才是具体的值
(4)二维数组的数组名可看做常量指针
一维数组名代表数组首元素的地址
int a[5] a的类型为int*
二维数组名同样代表数组首元素的地址
int m[2][5] m的类型为int(*)[5]
结论:
- 二维数组名可以看做是指向数组的常量指针
- 二维数组可以看做是一维数组
3 二维数组中的每个元素都是同类型的一维数组
来看一个遍历二维数组的例子:
void test_pointer6()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<3; j++)
{
printf("%d\n", *(*(a+i) + j));
}
}
}
- 数组的退化
二维数组参数同样存在退化的问题
二维数组可以看做是一维数组
二维数组中的每个元素是一维数组
二维数组参数中第一维的参数可以省略
void f(int a[5]); <>void f(int a[]); <> void f(int* a);
void g(int a[3][3]); <>void g(int a[][3]); <> void g(int (*a)[3]);
等价关系:
三、函数与指针
8. 函数类型
(1)C语言中的函数有自己特定的类型
(2)函数的类型由返回值,参数类型和参数个数共同决定
例:int add(int i, int j)的类型为int(int, int)
(3)C语言中通过typedef为函数类型重命名
typedef type name(parameter list)
例:
typedef int f(int, int);
typedef void p(int);
- 函数指针
(1)函数指针用于指向一个函数
(2)函数名是执行函数体的入口地址
(3) 可通过函数类型定义函数指针: FuncType* pointer;
(4)也可以直接定义:type (*pointer)(parameter list);
pointer为函数指针变量名
type为指向函数的返回值类型
parameter list为指向函数的参数类型列表
来看一个例子:
typedef int(FUNC)(int);
int test_pointer7(int i)
{
return i * i;
}
void main()
{
FUNC* pt = test_pointer7;
printf("Function pointer call: %d\n", pt(2));
}
10.回调函数
(1) 回调函数是利用函数指针实现的一种调用机制
(2) 回调机制原理
调用者不知道具体事件发生的时候需要调用的具体函数
被调函数不知道何时被调用,只知道被调用后需要完成的任务
当具体事件发生时,调用者通过函数指针调用具体函数调用
(3) 回调机制的将调用者和被调函数分开,两者互不依赖
我们来看一个例子:
typedef int(*FUNCTION)(int);
int test_pointer8(int n, FUNCTION f)
{
int i = 0;
int ret = 0;
for(i=1; i<=n; i++)
{
ret += i*f(i);
}
return ret;
}
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return 2*x - 1;
}
int f3(int x)
{
return -x;
}
void main()
{
printf("x * f1(x): %d\n", test_pointer8(3, f1));
printf("x * f2(x): %d\n", test_pointer8(3, f2));
printf("x * f3(x): %d\n", test_pointer8(3, f3));
}
运行结果:
总结:
(1)数组指针本质上是一个指针
(2)数组指针指向的值是数组的地址
(3)指针数组本质上是一个数组
(4)指针数组中每个元素的类型是指针
(5)函数指针用于指向一个函数
(6)回调函数是利用函数指针实现的一种调用机制
代码分享:
#include <stdio.h>
typedef int(AINT5)[5];
#define SIZEOF(A) ((sizeof(A)) / sizeof(*A))
const char* array[] = {
"china",
"japan",
"america",
"indian"
};
void test_pointer1()
{
int array[5];
int matrix[5][5];
int* pa = array;
int* pm = matrix;
}
void test_pointer2()
{
int int_array[5];
AINT5* p_int_5 = &int_array;
int(*p_int_5_2)[5] = &int_array;
}
int test_pointer3(int size, const char* array[], const char* key)
{
int i = 0;
int ret = -1;
for(i=0; i<size; i++)
{
if(strcmp(key, array[i]) == 0)
{
ret = i;
break;
}
}
return ret;
}
void test_pointer4()
{
int int_array[5];
AINT5* p_int_5 = &int_array;
int(*p_int_5_2)[5] = &int_array;
}
void test_pointer5()
{
int a = 10;
int b = 20;
int* p = NULL;
int** pp = NULL;
p = &a;
pp = &p;
printf("p = 0x%x, pp = 0x%x, *pp = 0x%x\n", p, pp, *pp);
}
void test_pointer6()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int i = 0;
int j = 0;
for(i=0; i<3; i++)
{
for(j=0; j<3; j++)
{
printf("%d\n", *(*(a+i) + j));
}
}
}
typedef int(FUNC)(int);
int test_pointer7(int i)
{
return i * i;
}
typedef int(*FUNCTION)(int);
int test_pointer8(int n, FUNCTION f)
{
int i = 0;
int ret = 0;
for(i=1; i<=n; i++)
{
ret += i*f(i);
}
return ret;
}
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return 2*x - 1;
}
int f3(int x)
{
return -x;
}
int main()
{
int ret = -1;
test_pointer1();
test_pointer2();
test_pointer5();
if ((ret = test_pointer3(SIZEOF(array), array, "china")) != -1)
{
printf("find key %s index = %d in array\n", array[ret], ret);
}
ret = -1;
if ((ret = test_pointer3(SIZEOF(array), array, "japan")) != -1)
{
printf("find key %s index = %d in array\n", array[ret], ret);
}
test_pointer6();
FUNC* pt = test_pointer7;
printf("Function pointer call: %d\n", pt(2));
printf("x * f1(x): %d\n", test_pointer8(3, f1));
printf("x * f2(x): %d\n", test_pointer8(3, f2));
printf("x * f3(x): %d\n", test_pointer8(3, f3));
return 0;
}
/*int main(int argc, char* argv[], char* env[])
{
int i = 0;
printf("============== Begin argv ==============\n");
for(i=0; i<argc; i++)
{
printf("%s\n", argv[i]);
}
printf("============== End argv ==============\n");
printf("\n");
printf("\n");
printf("\n");
printf("============== Begin env ==============\n");
for(i=0; env[i]!=NULL; i++)
{
printf("%s\n", env[i]);
}
printf("============== End env ==============\n");
}*/