C语言数组与指针关系详解
因为以前是学习的java方向,并没有什么指针的概念,但是现在正在从事C、C++方面的编程,所以重新整理一遍关于指针的内容,我们从以下几个方面来讲解:一 数组,二 指针,三 指针和数组的关系,四 指针作为函数形式参数的三种常见形式。
好了one by one
数组
数组我们就不用多说了吧,从一维数组到多维数组,只要是稍微有点编程经验的程序员已经玩的不能再烂了,基础内容我们就不在讲解了,我只讲解两个方面的内容。1 数组名 a: 数组名其实就是数组首元素的地址值。
2 数组名取地址 &a : 故名思议,其就是数组的地址。
很有意思的是数组首元素地址和数组地址的取值确实相同的,其值得大小都是数组首元素的地址值。但是数组首元素的地址和数组的地址是两个不同的概念,我们这里要先有一个认知。
其实数组也只不过是一种数据类型,只要是数据类型必然得经历声明,初始化,我们先来看看数组的声明方式,可能颠覆你以前的概念。
typedef int(MYINT)[5];
数组定义:
MYINT Array; 或 int array[5];
好了,让我们在来认识认识数组指针的概念吧!
指针
我们先抛开数组指针不说,我们先来看看指针是一个什么东西,我们其实不用想的那么麻烦,我们只需要把指针看做是一种数据类型,只不过还得根据指针所指向的不同数据类型细分,比如指向int型的指针,指向char型的指针等等,数组指针也是如此,即指向数组的指针。
数组与指针的关系
只要是指针就是一种数据类型,他的大小根据不同的操作系统分为不同的固定值,里面存储的时指向数据类型的地址值。那么问题来了,指向int型的指针我们可以声明为int * p,那么指向数组的指针我们如何声明呢?1)typedef int(ArrayType)[5];
ArrayType* pointer; //这一点我们比较容易理解但是却不常用。
2)直接定义:int (*pointer)[n]; //这种不容易理解,但是却比较常用
//pointer 为数组指针变量名
//type 为指向的数组的类型
//n 为指向的数组的大小
好了,我们已经会声明数组指针了,但是我们应该为其赋什么样的值呢?
不用说,既然是数组指针,必然是赋给一个数组的地址了,那就是&a了。
即 int (*p) [5] = &a;
既然都是指向内存地址的变量我们问什么还要声明其指向的不同的数据类型呢?原因很简单,C、c++编译器会根据我们声明的指向不同数据类型的指针来判断步长值,比如
int a[] = {1,2,3,4,5,6,7,8,9,0}
int (*p)[10] = &a;
int *p2 = a;
p + 1和p2 + 1就是完全不同的两个值了,原因是什么呢?就是步长值不同,第一个是一个数组的步长值,第二个是一个数据元素的步长值。说到这里你应该明白点了吧。
那么对于多维数组呢?
比如说二维数组int a[3][3] 中的a又是什么呢?经过测试我可以很负责任的告诉你a就是指向int a[3]的数组指针。这就是多维数组的本质。
证明代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*
该案例研究二维数组的数组名是什么
a = 1638004
a + 1 = 1638024
说明步长值为20,所以我们有理由相信a是一个指向char [20]数组的指针
*/
void main () {
char a[9][20] = {"weijinhao","zhaijiayi",
"zhoufeifei","kongqinghua","lixuerao",
"guodengxiao","liuyawei","liuyunfei","liuziyang"};
printf("%d\n" ,a);
printf("%d\n" , a + 1);
system("pause");
}
好了我们来推演一下数组和指针的关系吧!
#include <stdio.h>
#include <stdlib.h>
//指针数组做函数参数的推演过程
void printfArray1(char *p[10]);
void printfArray2(char *p[]);
void printfArray3(char **p);
//为什么上面三个参数类型都能编译成功呢,应为我们声明的都是字符数组,
//所以步长值已经确定了,gcc编译器可以对该种指针进行操作了
void main() {
char *p[10] = {"weijinhao","langfang","beijing"};
printfArray1(p , 3);
printfArray2(p , 3);
printfArray3(p , 3);
system("pause");
}
void printfArray1(char *p[10] ,int num) {
int i = 0;
for(; i < num;i++ ) {
printf("%s\n",p[i]);
}
}
void printfArray2(char *p[] ,int num) {
int i = 0;
for(; i < num;i++ ) {
printf("%s\n",p[i]);
}
}
void printfArray3(char **p ,int num) {
int i = 0;
for(; i < num;i++ ) {
printf("%s\n",p[i]);
}
}
#include <stdio.h>
#include <stdlib.h>
/*
做函数参数的等价关系
char a[30] char *
char *a[30] char **
char a[20][30] char (*p)[30]
*/
void printfArray11(char a[10][20], int num);//步长值20
void printfArray22(char a[][20], int num);//步长值20
void printfArray33(char (*p)[20], int num);//步长值20
//为什么上面能通过编译器的编译呢?原理一样,
//我们传递的只是指针,但是我们只需要能够让编译器确定指针的步长值编译器就不会报语法错误
void main() {
char a[10][20] = {"weijinhao","langfang","beijing"};
printfArray11(a , 3);
printfArray22(a , 3);
printfArray33(a , 3);
system("pause");
}
void printfArray11(char a[10][20] , int num) {
int i = 0;
for( ;i <= num; i++) {
printf("%s\n" ,a[i]);
}
}
void printfArray22(char a[][20] , int num) {
int i = 0;
for( ;i <= num; i++) {
printf("%s\n" ,a[i]);
}
}
void printfArray33(char (*p)[20] ,int num) {
int i = 0;
for( ;i <= num; i++,p++) {
printf("%s\n" ,*p);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
该函数的作用:动态在堆上分配二维数组上
知识点1:在堆区域动态创建二维数组
知识点2:避免野指针的处理方法
*/
char **getMem(int count);
void main () {
char ** p = getMem(10);
p = NULL;
int i;
strcpy(p[0],"weijinhao0");
strcpy(p[1],"weijinhao1");
strcpy(p[2],"weijinhao2");
strcpy(p[3],"weijinhao3");
strcpy(p[4],"weijinhao4");
strcpy(p[5],"weijinhao5");
strcpy(p[6],"weijinhao6");
strcpy(p[7],"weijinhao7");
strcpy(p[8],"weijinhao8");
strcpy(p[9],"weijinhao9");
for( i = 0; i < 10; i++) {
printf("%s\n",p[i]);
}
for(i = 0; i < 10; i++) {
free(p[i]);
}
free(p);
p = NULL;
system("pause");
}
//动态创建二维数组的方法
char **getMem(int count)
{
int i = 0;
char **tmp = (char **)malloc(count*sizeof(char *));
for (i=0; i<count; i++)
{
tmp[i] = (char *)malloc(100);
}
return tmp;
}
企业级开发传递数组是基本上都是一个套路,首先将指针作为函数参数传递,然后在函数体中在使用数组的形式访问其中的没一个值,因为如果我们不传递数组的长度的话单凭借指针的话我们是不能确定数组长度的,自然而然我们会传递两个参数,一是指针,而是长度。
多维数组作为函数参数退化的原因
因为函数参数的传递实际上就是数据的拷贝,我们知道数组的大小是很大的,如果采用像普通数据类型一样的方式进行函数间的通讯的话,肯定是低效的,所以多维数组作为函数参数进行传递的话会退化成指针的形式,因为一个指针才4个字节,可以接受,所以很好。指针作为函数参数使用的三种形式
传递参数 char a[10] = {“”,””,………..} -----------------------------void printfArray(char *)传递参数 char* a[10] = {“”,””,………..} -----------------------------void prinftArray(char **)
传递参数 char a[10][20] = {“”,””,………..} -----------------------------void printfArray(char (*p)[20])
总结
一 是指针是一种数据类型,不管是指向什么的指针,几级指针,gcc编译器只为他分配4个字节的内存空间。二 是指针做函数参数的几种变化形式,我们别不认识,如何定义参数没有什么问题,但是一定要确定参数指针的步长值,如果不能确定,一定是错的。
下面的几个演示案例可以带我们更进一步的学习: