Do you master on array in C ?

因为新标准C99的支持变长数组, 差点儿C的标准特性就是看着gcc来的(Linux 内核严重依赖GCC)

int mani()
{
const int a = 10;
int array[a];
return 0;
}

这段代码能过编译吗?

在2012年是过不了的。2014年就能够了(时间改变一切啊~)

在VC++6.0上做的測试结果

sdev98\bin\hello.c(7) : error C2057: expected constant expressionf:\vc++6.0\microsoft visual studio\common\msdev98\bin\hello.c(7) : error C2466: cannot allocate an array of constant size 0f:\vc++6.0\microsoft visual studio\common\msdev98\bin\hello.c(7)
: error C2133: 'array' : unknown sizeError executing cl.exe.

过不了。

选VC6.0的原因是由于这家伙的编译器的规范和早期的ANSI是符合的非常好的.早期的ANSI标准不支持变长数组的特性

特别注意,因为眼下版本号(我用的4.8),已经根植了数组变长的特性,所以利用编译选项 --std=c90 --ansi 是不能全然恢复到原来的ANSI标准的!

亲測!

除非你自己又一次编译一个gcc 的早期仅支持ANSI标准的compiler出来去測试

可是!C99明文规定, C语言增加了变长数组的特性

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 10; char b[a]; int *p = &a; (*p)++; printf("%d %d\n", sizeof(b), a); return 0;
}

能过吗?能!

以下的能过吗?不能!

int a = 10;
int array[a] ;
int main()
{
return 0l
}

上面三个问题能不能答对都不重要了, 以下搞定数组的时候到了,逃避不了新特性——数组变长

一直说数组变长。这里事实上有两种类型的“变长”—— VLA (varible length array) & VM (varible modified,that is, a pointer to a VLA type)

节选自C99标准(数组的就以下这部分)

Array declarators

Constraints

回答,何为数组?

1          In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *.

以下if就開始分析情况了...

If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero.

假设数组的长度是限定的。那么array[exp]中的exp应该是一个整形数

假设是个常量表达式,这个表达式的值要大于0(知道这里肯定有人会说struct里array[0]的使用方法,别急后面会分析)

The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array
type derivation.

什么样的数组能够变长?

2           Only an ordinary identifier (as defined in 6.2.3) with both block scope or function prototype scope and no linkage shall have avariably
modified type.
If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.

我还是把6.2.3截屏出来,这样好论证。反正比較短

这里的ordinary identifier 最后一项“all other identifiers called ordinary identifiers” 。这里就注意这里的ordinary identifier就是四个选项“—” 的最后一项,而不包含前面三项。那么值得注意的就是struct不属于ordinary identifier

int a = 10;
int array[a];
int main()
{
return 0;
}

这段代码是过不了gcc的编译的,数组变长发生在block(就是main函数的{} )之外,

Semantics

3 If, in the declaration ‘‘T D1’’, D1 has one of the forms:

D[ type-qualifier-list opt assignment-expression opt ]

D[ static type-qualifier-list opt assignment-expression ]

D[ type-qualifier-list static assignment-expression ]

D[ type-qualifier-list opt * ]

and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list T ’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T ’’. 121)

数组两大类型,incomplete type 和complete type

4

先介绍了什么是incomplete type.

If the size is not present, the array type is anincomplete type.If
the size is * instead of being an expression, the array type is a
variable length array type of unspecified size, which can only be used in declarations with function prototype scope;

注意,这里的*,是省略的意思,不是真的在[ ]写个*

element_type name[size];

这里size缺失,形成element_type name[]; 叫varible length array可变长数组,事实上我们经经常使用

char string[] = "hello world!\n";

什么是complete type

such arrays are nonetheless
complete types. If the size is an integer constant expression and the element When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared. Thus, * can be used only in function declarations that are not definitions (see
6.7.5.3). type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type.

5 If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall
have a value greater than zero.

假设size是一个值不是整形常量表达式的时候,假设这样的情况发生在函数声明的时候,把这个不是常量表达式的size当作*,就是省略掉了,不外乎就是数组传參数是以指针的形式传递的,这里道出了数组传參的本质来源.

假设不是发生在函数声明的參数中,那么这个时候size必须是个大于0的整数.

The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of asizeof
operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.

不论什么varible length array的大小在其整个生命周期都不会发生变化.

#include <stdio.h>
#include <stdlib.h> int main()
{
int a = 10; char b[a]; int *p = &a; (*p)++; printf("%d %d\n", sizeof(b), a); return 0;
}

打印什么?10 11的原因就在于上面的解释

@凯旋冲锋 发现了一种极赞的做法

#include <stdio.h>
#include <stdlib.h> int main()
{
const int a = 10; char b[a]; printf("%d %d\n", sizeof(b), a); int *p = &a; (*p)++; printf("%d %d\n", sizeof(b), a); return 0;
}

看。输出结果是

10 10

11 11

为什么?怎么就变了不是说好整个生命周期数组长度不变的么,不是说好sizeof都不受影响么?wait,看看那个const。不能再赞的技巧,这里涉及到了一次指针的强制类型转换

看到int *p = &a; 这里&a 是const int a的地址,极其对于这个地址怎么解释呢?看它的数据类型——const int

类似的,char c;&c 就会把这个地址解释为储存的是一个char类型的变量.

Just think about it.

int*p = &a; p 指向的是一个int类型的变量,&a 指向的是一个const int的变量。上帝,假设你对p 解引用,然后你是能够改变p指向的值的,为什么?由于p告诉你它指向的类型是int类型 你想怎么改怎么改,而const int不行!

说白了就是同一个内存地址,你使用了不同的方法去解释它。一种解释方法是p,一种解释方法是&a

把这两者结合起来会发生非常奇异的作用,hold住。

当你通过a去訪问这块内存的时候是read only的(const),当你去通过p去訪问这块内存的时候是read&write的。int类型嘛.

a++;是不同意的。你尝试通过read only的方式去改变a标记的内存的数据

(*p)++;是同意的。p的訪问方式是 r w

你会非常淡定的发现仅仅有一个error!warning和我猜測的一样,是有强制类型转换.

可是不确定这个做法的后果,没有研究它对stack其它储存位置的影响,这差点儿是一个“魔法师的魔术",可是你要知道它发生在stack上面,并且之前array的大小事实上定下来了。我怀疑这样的做法是一种”欺骗性的“。并没有实质上的去改变数组的大小,能够说是一种“合法的数组越界” (关于这点我们能够继续讨论,这样的做法对于stack内存布局的影响)

6      For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value.
If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.

这里多维数组的compatible问题我还不是非常明确。以至于后面图片的的那个demo我也不是非常明确,这里还要请

Essential On C & linux 的teammate多多不吝赐教

以下開始放样例了。介绍数组的使用方法,以及违法情况

7 EXAMPLE 1

float fa[11], *afp[17];

declares an array of float numbers and an array of pointers to float numbers.

8 EXAMPLE 2

Note the distinction between the declarations

extern int *x;

extern int y[];

The first declares x to be a pointer to int; the second declares y to be an array of int of unspecified size (an incomplete type), the storage for which is defined elsewhere.

9EXAMPLE 3

The following declarations demonstrate the compatibility rules for variably modified types.

extern int n;
extern int m;
void fcompat(void)
{
int a[n][6][m];
int (*p)[4][n+1];
int c[n][n][6][m];
int (*r)[n][n][n+1];
p = a; // invalid: not compatible because 4 != 6
r = c; // compatible, but defined behavior only if n == 6 and m == n+1 这里不是非常明确。感觉r 和c 和不来啊,总认为维数都不一样
}

10 EXAMPLE 4

All declarations of variably modified (VM) types have to be at either block scope or function prototype scope.

这句话就死死的把指向变长数组的指针——VM 的使用范围仅限于block 内部

Array objects declared with the static or extern storage-class specifier cannot have a variable length array (VLA) type.

不论什么具有静态或者外部链接特性的objects,都不能VLA(这差点儿可以理解为,VLA仅仅发生在栈上,假设我的观点有误,希望可以交流讨论)

However, an object declared with the static storage-class specifier can have a VM type(that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to
be ordinary identifiers and cannot, therefore, be members of structures or unions.

VM最后不能是结构体member

 

图中的 int(*s)[m] 就是 VM

最后不忘记讨论数组size == 0的情况。这里仅仅能用在结构体里面

这是一种非常经典的做法。以至于C99标准里面有特别声明。不是所谓的“奇迹淫巧”,这里时有正规说明的!

哈哈

对于用法,以下的link我有总结

http://blog.csdn.net/cinmyheart/article/details/28985843

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called aflexible array member. With two exceptions,
the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length. 106) Second, when a . (or
->)

operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger
than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior
is undefined if any attempt is made to access that element or to generate a pointer one past it.

这样的是flexible array member,注意区分 VLA和 VM

林荫道 霍贝玛 荷兰 1689年 140 × 103cm 油画 英国伦敦国家美术馆

    此图展现了乡野的漂亮风光。画面上宁静的乡间景致看似平淡。却耐人寻味。使观者心旷神怡。对称的小树于平稳中见动感。随小道向前远望还能看到左旁的教堂尖顶,右旁两幢高顶茅屋,车辙印在泥泞的村道上,表现出一种正在延续着的平静而艰难的生活,占有大部分画面的天空则云蒸霞蔚。美得令人陶醉。这幅画中还带着一种忧伤的讽刺意味。长长的林荫大道在灰暗的地平线处消失到一点,画中这样的忧伤的讽喻也是霍贝玛现实生活的真实反映。



Do you master on array in C ?的更多相关文章

  1. 观V8源码中的array.js,解析 Array.prototype.slice为什么能将类数组对象转为真正的数组?

    在官方的解释中,如[mdn] The slice() method returns a shallow copy of a portion of an array into a new array o ...

  2. 算法(3)Rotate Array

    题目:将一个n个元素的数组右移k位,比如n=7,k=3,对数组[1,2,3,4,5,6,7]作如下旋转[5,6,7,1,2,3,4] 思路:[5,6,7,1,2,3,4],不知大家看出来了没有呢,两次 ...

  3. 算法(2) Find All Numbers Disappeared in an Array

    题目:整数数组满足1<=a[i]<=n(n是数组的长度),某些元素出现一次,某些元素出现两次,在数组a[i]中找到[1,n]区间中未出现的数字.比如输入[4,3,2,7,8,2,3,1], ...

  4. 算法(1)K-diff Pairs in an Array

    写在前面:研究操作系统,习惯了用C,但是在做算法题甚至构建大型系统时,C真的是噩梦.还是用C++比较好,基本算法很成熟,并可基于此实现更复杂的算法.那就边写算法边捡起来好久不用的C++吧! 题目:数组 ...

  5. Redis 使用说明 安装配置 主从复制

        开机加自启动: echo "redis-server /etc/redis.conf" >>/etc/rc.local    以前有想过用 Memcache 实 ...

  6. Greenplum 在Linux下的安装

    1.实验环境 1.1.硬件环境 Oracle VM VirtualBox虚拟机软件:三台Linux虚拟机:Centos 6.5:数据库:greenplum-db-4.3.9.1-build-1-rhe ...

  7. yii1.1.3主从(多从)、读写分离配置

    从新配置main.php片段 代码如下 ----------------------------------------------------------- 'db'=>array( 'con ...

  8. 优化过的redis封装类

    转http://www.cnblogs.com/jackluo/p/3410192.html <?php /** * RedisCluster 群redius操作类 * * //创建连接 * $ ...

  9. PHP Redis 集群封装类

    <?php /**  * Redis 操作,支持 Master/Slave 的负载集群  *  * @author V哥  */ class RedisCluster{        // 是否 ...

随机推荐

  1. Android面试题收集(有具体答案)

    Android面试题目及其答案 1.Android dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念 DVM指dalivk的虚拟机.每个Android应用程序都在它自己的进程中执行,都 ...

  2. 【图像处理】Gabor过滤器

    Gabor内核参考wiki 使用实数Real的公式计算核函数代码: Mat getGaborFilter(float lambda, float theta, float sigma2,float g ...

  3. (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

    本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...

  4. N-Queens And N-Queens II [LeetCode] + Generate Parentheses[LeetCode] + 回溯法

    回溯法 百度百科:回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步又一次选择,这样的走不通就退回再走的技术为回溯法 ...

  5. Swift - 设置网格UICollectionView的单元格间距

    要设置单元格cell的间距(水平间距,垂直间距)可进行如下设置: 方法1:在storyboard中设置 选择Collection View后在面板里设置Min Spacing相关属性(这里也可以设置单 ...

  6. Hibernate持久化对象状态

    在Hibernate中,持久化对象再被操作过程中分为三个时期.这三个时期和session周期相关. 各自是瞬时(Transient),持久太(persistent)和游离态(Detached) 瞬时状 ...

  7. goldengate 简单配置 oracle to oralce

    做oracle时配置的,goldengate 是同步异构数据库最好的工具.这个是基于oracle to oracle 单向复制 添加增量复制进程   add extract process -- -- ...

  8. 将n进制的数组压缩成字符串(0-9 a-z)同一时候解压

    比如一个3进制的数组: [1 1 2 2 2 0 0] 用一个字符串表示... 此类题目要明白两点: 1. 打表:用数组下标索引字符.同一时候注意假设从字符相应回数字: int index = (st ...

  9. [黑马程序员] I/O

    ---------------------- ASP.Net+Android+IO开发..Net培训.期待与您交流! ---------------------- 0. IO流概述: Java对数据的 ...

  10. 内网穿透神器ngrok(转)

    相信做Web开发的同学们,经常会遇到需要将本地部署的Web应用能够让公网环境直接访问到的情况,例如微信应用调试.支付宝接口调试等.这个时候,一个叫ngrok的神器可能会帮到你,它提供了一个能够在公网安 ...