01 数组的下标可以是负数
C语言中使用数组,一般来说都是这样的:
int ary[5] = {1, 2, 3, 4, 5};
ary[0] = 10;
语法规定,数组的长度必须是个常量,保证数组所占的内存大小不变。
数组下标从 0 开始,依次往后递增。
于是大部分同学都把它当作了默认的规则。
其实,数组的长度可以是0,叫做柔性数组,目的是让结构体变成可变长度,不过也是使用 malloc 进行空间的申请。
另外,数组的下标也可以是负数,正数表示向后访问,负数表示向前访问。
比如:
int ary[5] = {1, 2, 3, 4, 5};
int *p = &ary[2];
p[-2] = 0;
如果不太明白,可以看下下面的内存示意图:
对于二维数组,同样可以使用负数的下标来访问元素。
int ary[3][3] = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}};
int (*p)[3] = ary + 2;
printf(" %dn ", p[-1][-1]);
指针p指向数组的第三行,p[-1]表示第二行的首元素地址,p[-1][-1]表示第二行首元素的前面一个元素,即第一行最后一个元素
注意:二维数组在内存中也是连续的。
使用负数作为下标来访问数组,切记内存不要越界,访问的内存一定是存在的,否则肯定有可能导致程序崩溃聪明的同学应该也能发现,这个所谓的负数其实只是指针的加减运算而已,坦白说并没有什么用。
02 数组名可以放在括号中
在 C语言中,数组和指针在使用上是等价的,数组可以使用指针来表示,指针也可以通过下标来访问,这在上一个讨论中已经看到,如:
ary[i] = 0x55;
*(ary + i) = 0x55;
*(i + ary) = 0x55;
以上三行代码的效果是一样的, 冷知识在于,编译器也支持这样的用法:
i[ary] = 100; // 提醒一下,数组名称放到了[]里面
在 MDK 中验证一下,如下,编译通过,0 error ,0 waring。
这种用法放到到二维数组中也是一样的道理,例子如下:
int ary[3][3] = {{1, 2, 3}, {2, 3, 4}, {3, 4, 5}};
printf("%dn",2[array][2]);
当然我们一点也不推荐这种写法,这样的冷知识,我们了解一下,以备和面试官侃天说地时用。
03. sizeof是一个运算符
在初学C语言的时候,我们刚刚接触到整型,浮点型数据,总是用 sizeof 来判断数据类型占用的内存空间,比如,我们常常会这样写:
float sum;
int bytes = sizeof(sum);
这样来使用sizeof,就会使我们误以为,sizeof是一个函数。但是实际上,你像下面这样写,也是没有问题的:
int bytes = sizeof sum; //
从上面可以观察到,sizeof 绝对不是一个函数,因为没有括号一样可以运行,所以他本质上是一个 C语言的关键字或者看做运算也可以,如同 +, – , * , / 一样,而不是sqrt()这种函数。但是要注意,如果是求整型关键字就必须要加括号,比如:
int bytes = sizeof (float); //
这地方用运算来看待 sizeof 也是可以理解的,因为 float 是一个关键字,他前面只能加 unsinged,或者 const 这些关键字。 这里你可以假想是我们将 0 进行强制类型转换为 float,然后去计算这个变量占用空间的大小,如:
int bytes = sizeof (float) 0; // 这里可以假想是 0 被省略了
04. 空格不都是没用的
一般情况下,学校老师会告诉我们,大部分情况下,空格在C语言程序中是无关紧要的,或者说除了预处理器那部分代码,空格无关紧要。但是,可以尝试思考一下下面这两条语句:
printf("Hello
World");
printf("Hello
World");
看出这两行代码的区别了吗?其实下面那条是不合法的,就是因为转义字符后面多加了一个空格。当然,这个例子是特意为了说明这个问题举得,这么短的代码实在也没有必要换行。