[C语言]指针和数组(二)

一、指针数组和数组指针
二、多维数组和多维指针
三、函数与指针

一、指针数组和数组指针

  1. 饭前思考
    我们先来看一段代码:
#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又代表什么呢?

  1. 数组类型定义
    数组类型该如何定义呢?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. 数组指针
    (1)数组指针用于指向一个数组
    (2) 数组名是数组首元素的起始地址,但并不是数组的起始地址 但并不是数组的起始地址
    (3) 通过将取地址符&作用于数组名可以得到数组的起始地址
    (4)可通过数组类型定义数组指针: ArrayType* pointer; 也可以直接定义:type (*pointer)[n];
    pointer为数组指针变量名
    type为指向的数组的类型
    n为指向的数组的大

  2. 指针数组
    (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这两个命令行参数和所有环境变量都被打印出来。

二、多维数组和多维指针

  1. 指向指针的指针
    指针实际上也是一种变量,既然是变量就具有内存,也就具有地址,那指针的地址该由谁来存放呢?
    没错,就是指针的指针,来看一段代码:
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. 二维数组和二级指针
    (1)二维数组在内存中以一维的方式排布
    (2)二维数组中的第一维是一维数组
    (3)二维数组中的第二维才是具体的值
    (4)二维数组的数组名可看做常量指针
    在这里插入图片描述
    一维数组名代表数组首元素的地址
    int a[5] a的类型为int*
    二维数组名同样代表数组首元素的地址
    int m[2][5] m的类型为int(*)[5]

结论:

  1. 二维数组名可以看做是指向数组的常量指针
  2. 二维数组可以看做是一维数组
    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));
        }
    }
}
  1. 数组的退化
    二维数组参数同样存在退化的问题
    二维数组可以看做是一维数组
    二维数组中的每个元素是一维数组
    二维数组参数中第一维的参数可以省略
    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. 函数指针
    (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");
}*/
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值