学习深度学习框架源码有感 | 指针数组,数组指针结束篇
在上一篇文章学习深度学习框架源码有感 | 反正我不会C语言中,介绍了数组和多维数组名所代表的含义,及其和数组指针间的联系。本文将把多维数组剩余的内容介绍一下,并且讲一下文章指针篇 | 教你把多级指针玩弄于鼓掌中讲过的二级指针模型中的第一种模型指针数组的应用。之所以讲指针数组一方面是它容易和我们在文章学习深度学习框架源码有感 | 反正我不会C语言中提到过的数组指针搞混,另一方面,指针数组有着其强大的功能。
1.多维数组名作函数参数时的退化过程
所谓的退化过程,简单的举例来说,二维数组名代表的是数组的首行地址,当将二维数组名作为函数参数是,在子函数中,该数组名仅仅代表指向首行首元素的地址。这就是所谓的退化过程。如下代码所示,展示了数组名作函数参数时的退化过程。主函数中的num指的是这个二维数组第一行元素个数。而在子函数中不论二维数组的大小是多少,inum的值永远等于1。
int sub(char** table)
{
int inum = 0;
inum = (sizeof(table)/sizeof(*table));
return 1;
}
int main()
{
char* c_keyword[] = {
"while",
"case",
"static",
"do"
};
num = (sizeof(c_keyword)/sizeof(*c_keyword))
sub(c_keyword);
}
2.多维数组在内存中是线性存储的
如下图左图所示,是我们在讲多维数组名时候用到的内存模型图。但是真实情况下,多维数组在内存中并不是如左图所示存放的,而是如右图般线性存放的。
如下代码段,便可以证明二维数组在内存中是线性存储的。在函数外部利用二维数组的特性初始化数组元素值。并将二维数组第一行首元素的地址传入打印函数,用一维数组的特性打印。我们会发现两者的打印结果一致,说明了上述结论是成立的。
void printfArray(int *array, int size)
{
int i = 0;
for (i=0; i<size; i++)
{
printf("%d ", array[i]);
}
}
void main()
{
int a[3][4];
int i, j, tmp = 1;
for (i=0; i<3; i++)
{
for (j=0; j<4; j++)
{
a[i][j] = tmp++;
}
}
//把二维数组 当成 1维数组 来打印 证明线性存储
printfArray((int *)a, 12);
return ;
}
3.指针数组
指针数组作为二级指针的一种内存模型,有很强大的功能,接下里介绍三种指针数组的用法。
一:存放不同长度的字符串,并作一些针对字符串的操作。
如下所示代码段,主函数定义一个指针数组,并将该二维指针的数组名传入到所需调用函数中。前面我们讲过,数组名作函数参数时会发生退化,所以在子函数中我们调用数组时,发现数组是指向元素的,而不像我们之前介绍过的指向一行。整体函数实现了,找到当前指针数组中对应字符串的位置。
int searcheKeyTable(const char* table[], const int size, const char* key, int *pos)
{
int rv = 0;
int i = 0;
int inum = 0;
if (table==NULL || key==NULL || pos==NULL)
{
rv = -1;
printf("func searcheKeyTable:%d", rv);
return rv;
}
//间接的证明 数组做函数参数的退回
inum = (sizeof(table)/sizeof(*table));
for(i=0; i<size; i++)
{
if( strcmp(key, table[i]) == 0 )
{
*pos = i;
//break;
return rv;
}
}
//没有找到返回-1
if (i == size)
{
*pos = -1;
}
return rv;
}
#define DIM(a) (sizeof(a)/sizeof(*a))
int main411()
{
int inum = 0;
int pos = 0;
int a[10];
int i = 0;
//指针数组
char* c_keyword[] = {
"while",
"case",
"static",
"do"
};
searcheKeyTable( c_keyword, DIM(c_keyword),"do", &pos);
// ===> 带参数的宏
//searcheKeyTable( c_keyword, (sizeof(c_keyword)/sizeof(*c_keyword)),"do", &pos);
printf("pos:%d\n", pos);
return ;
}
二:作main函数的参数
如下代码段所示,我们经常会看到main函数中也会有一些参数,很多时候会产生困惑,为什么main函数也会有参数传入。其实道理很简单,main函数是由系统调用的一个子函数,因此会接收来自系统的参数。其实就是用来解析命令行参数的。
//main函数是操作系统调用的函数
//在程序执行的时候可以向main函数传递参数
/*
argc 命令行参数
argv 命令行参数数组
env 函数变量数组
int main();
int main(int argc);
int main(int argc, char *argv[])
*/
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]);
}
// for(i=0; argv[i]!=NULL; 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");
getchar();
}
三:指针数组有自我结束的能力
如下代码段所示,我们能够向指针数组中,添加各种不同类型的数据,因此我们可以通过在指针数组的最后手动添加空字符,使得指针数组能够具有自我结束的判别能力。
void main()
{
int inum = 0;
int pos = 0;
int a[10];
int i = 0;
//指针数组 自我结束能力
char* c_keyword[] = {
"while",
"case",
"static",
"do",
'\0'
};
char* c_keyword2[] = {
"while",
"case",
"static",
"do",
0
};
char* c_keyword3[] = {
"while",
"case",
"static",
"do",
NULL
};
for (i=0; c_keyword[i] != NULL; i++)
{
printf("%s\n", c_keyword[i]);
}
printf("\n....\n");
for (i=0; c_keyword2[i] != NULL; i++)
{
printf("%s\n", c_keyword2[i]);
}
printf("\n....\n");
for (i=0; c_keyword3[i] != NULL; i++)
{
printf("%s\n", c_keyword3[i]);
}
}
所以,在C语言中数组指针(二维数组)和指针数组都是很有用的两种内存模型,我们若是能够吃透这两种模型真正的含义。我相信我们将真正的学会指针和数组。Time~