C语言入坑指南-数组之谜
前言
在C语言中,数组和指针似乎总是“暧昧不清”,有时候很容易把它们混淆。本文就来理一理数组和指针之间到底有哪些异同。
数组回顾
在分析之前,我们不妨回顾一下数组的知识。数组是可以存储一个固定大小的相同类型元素的顺序集合。为了便于我们说明,假设有以下数组声明:
int a[5];
char b[] = "hello";
- 数组大小必须在编译期就作为一个常数确定下来。
- 但C99中引入了变长数组,允许数组的维度是表达式 ,但在数组分配内存时,其表达式的值可以被求出。
- 数组下标运算实际上都是通过指针进行的,也就是说a[4]与*(a+4)是等价的,甚至你会发现和4[a]也是一样的。
- 数组名一般代表了指向该数组下标为0的元素的指针,并且printf("%s\n",hello)与printf("%s\n",&hello[0])等效。
数组和指针不相等
考虑下面的声明:
int c[4];//假设int占4字节
int *d;
对于上面的声明,编译器会给c预留内存空间4*4字节,并且数组名代表着指向数组第一个元素的指针。但对于d,却只为指针本身保留了内存空间。
所以此时有下面的操作:
c[3]; //合法
*(c+3); //合法
*d; //不合法,d指向了内存中不确定位置
c++; //不合法,一维数组名是指针常量,常量不能被修改掉
d++; //可通过编译
另外,下面的两种情况也是不一样的:
char c[] = "hello";
char *d = "hello";
前者对字符数组a进行了初始化,后者将d指向了字符串常量。字符串常量存储在只读区,
因此有下面的操作:
c[0] = 'H'; //合法,可修改数组内容
*d = 'H'; //不合法,字符串常量内容不可更改
d[0] = 'H' //不合法
数组名的含义
绝大多数情况,数组名都代表着指向该数组中下标为0的元素的指针,但是有例外:
int e[4];//假设int为4字节
sizeof(e);
上面的sizeof(e)的值并非4或8(指针占用空间),而是4*4 = 16。也就是说,当数组名被用作运算符sizeof的参数时,它的计算结果是整个数组的大小,而非第一个元素的指针大小。
再来看下面这种情况:
int temp[5];
char *p = &temp;
char *q = temp;
在这里,p和q的值是一样的,含义却不一样,前者是指向数组的指针,而后者是指向该数组中下标为0的元素的指针。因此p+1指向了temp的末尾,而q+1指向了temp的第2个元素。
数组长度计算
如何计算数组长度?考虑下面的代码:
int f[] = {1,2,3,4,5,6};
int *g = f;
size_t len_f = sizeof(f)/sizeof(int)//正确计算方法
size_t len_g = sizeof(g)/sizeof(int)
上面的len_f和len_g的值相等吗?显然并不相等。事实上,只有len_f得到了数组f的长度,而len_g的值并没有任何实际意义。
不能作为参数的数组
所谓的数组不能作为参数,并不是指声明的数组不能作为参数传递,而是指当数组名作为参数时,数组名会被转换为指向该数组下标为0的元素的指针。
而下面的两种声明,其实也是等效的:
size_t arrayLen(const int *arr);
size_t arrayLen(const int arr[]);
我们来看一个例子,说明数组作为参数的情况:
#include <stdio.h>
int arraySum(const int arr[])
{
unsigned int loop = 0;
/*循环前计算好长度,提高性能*/
unsigned int len = sizeof(arr)/sizeof(int);
int sum = 0;
if(NULL == arr)
{
return 0;
}
for(loop = 0; loop < len; loop++)
{
sum+=arr[loop];
}
return sum;
}
int main(void)
{
int a[] = {1,2,3,4,5,6};
int sum = arraySum(a);
printf("arr sum is %d",sum);
return 0;
}
我们运行上面的程序,发现最终结果并不是我们预期的21,而是3。问题在于,a作为参数传入到arraySum中时,它是作为指针的,那么在函数内部计算sizeof(arr)自然只是得到了指针占用的内存大小。对于64位程序,这个大小是8,那么len的值为2,最终只计算了两个元素的和。
思考:该如何修改上面的程序才能得到正确的结果?
总结
我们来总结一下前面的核心内容:
- 数组下标运算实际上都是通过指针进行的。
- 数组名代表着指向该数组中下标为0的元素的指针,但有例外:sizeof(数组名)返回整个数组的大小,而非指针大小;&数组名返回一个指向数组的指针,而不是指向该数组中下标为0的元素的指针的指针。
- 数组名作为参数时,数组名会被转换成指向该数组下标为0的元素的指针。
- 指针操作可能比下标操作效率高,但可维护性却不一定有下标操作好。
- 数组和指针不相等。
最新文章
- react-native 学习之TextInput组件篇
- [Unity3d]调试问题之UI/Image不显示
- html小知识点汇总(浏览器导航上显示图标、div无高度时试着清除浮动、文字环绕图片、字体加粗、div按百分比分、已有的不合适的class,针对特定的标签进行修改)
- C#获取并写入ORACLE数据库中中英文字符集问题
- PP 创建BOM
- 【转】CentOS环境下yum安装LAMP(Linux+Apache+Mysql+php)
- Libevent 定时器
- HDU 3555 数位dp入门
- java静态变量、静态方法和静态代码段
- [改善Java代码]不能初始化泛型参数和数组
- Windows使用virtualenv搭建flask开发环境
- 解析XML文件示例
- Winform使用DevExpress的WaitDialogForm画面
- mysql排行榜sql的实现
- TCP之超时和重传
- String.format()【演示具体的例子来说明】
- sql2005数据库置疑修复断电崩溃索引损坏 数据库索引错误修复/数据库表损坏/索引损坏/系统表混乱等问题修复
- Android游戏开发实践(1)之NDK与JNI开发04
- luogu P1494 岳麓山上打水 [iddfs]
- HDU1754-ZKW线段树