Do you master on array in C ?

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标准不支持变长数组的特性

但是!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 a
variably
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 an
incomplete 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 a
sizeof 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 a
flexible 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 油画 英国伦敦国家美术馆

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

时间: 2024-10-20 05:21:21

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

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

在官方的解释中,如[mdn] The slice() method returns a shallow copy of a portion of an array into a new array object. 简单的说就是根据参数,返回数组的一部分的copy.所以了解其内部实现才能确定它是如何工作的.所以查看V8源码中的Array.js     可以看到如下的代码: 一.方法  ArraySlice,源码地址,直接添加到Array.prototype上的"入口",内部经过参数.类型

算法(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],输出[5,6].时间复杂度要求是O(n),空间复杂度要求O(1) 思路:看许多网友说用set数据结构去做,这就违反了空间复杂度的要求,所以排序还是要本地做的.遍历数组,把这个数字放到它该待的地方:比如a[0]=4,那么我们就把4放到a[3]处,然后把a[3]处的7放到a[6]处,把a[6]处3放在a[2]处,

当罗密欧遇到朱丽叶... ...当指针遇到数组

题目是扯淡. 无视 他们说, 题目要长长长长.... 当罗密欧遇到朱丽叶的时候, 看官们都很happy... 古典唯美悲情爱情嘛~ 然而捏. 数组遇到指针的时候, 我就差点panic了... 这里特别感谢 @Fantasy @凯旋冲锋 还有一起关注问题讨论问题的Essential On Linux的道友们 事情的起因源于这里的一个hash表的实现,里面用到了二级指针(还有三次解引用...) 第一感觉不科学, 然后debug, 程序没有挂 ...他居然没有挂... ----------------

Lintcode: Sort Colors II 解题报告

Sort Colors II 原题链接: http://lintcode.com/zh-cn/problem/sort-colors-ii/# Given an array of n objects with k different colors (numbered from 1 to k), sort them so that objects of the same color are adjacent, with the colors in the order 1, 2, ... k. 注意

php操作redies封装的类

<?php /** * Redis 操作,支持 Master/Slave 的负载集群 * * @author jackluo */ class RedisCluster{ // 是否使用 M/S 的读写集群方案 private $_isUseCluster = false; // Slave 句柄标记 private $_sn = 0; // 服务器连接句柄 private $_linkHandle = array( 'master'=>null,// 只支持一台 Master 'slave'

算法(1)数组

写在前面:研究操作系统,习惯了用C,但是在做算法题甚至构建大型系统时,C真的是噩梦.还是用C++比较好,基本算法很成熟,并可基于此实现更复杂的算法.那就边写算法边捡起来好久不用的C++吧! 题目:数组中的k差对(K-diff Pairs).输入为一个数组A和一个整数k,找到数组中 所有的数值对pairs(i,j),其中A[i]和A[j]之间差的绝对值是k.比如[3,1,4,1,5],k=2,ouput=2,这样的差值对是(1,3)和(3,5),其中1虽然在数组中出现过2次,但是只算一次.(题目:

Lintcode: Interleaving Positive and Negative Numbers 解题报告

Interleaving Positive and Negative Numbers 原题链接 : http://lintcode.com/zh-cn/problem/interleaving-positive-and-negative-numbers/ Given an array with positive and negative integers. Re-range it to interleaving with positive and negative integers. 注意 Yo

PHP Redis 集群封装类

<?php /**  * Redis 操作,支持 Master/Slave 的负载集群  *  * @author V哥  */ class RedisCluster{       // 是否使用 M/S 的读写集群方案     private $_iSUSECluster = false;       // Slave 句柄标记     private $_sn = 0;       // 服务器连接句柄     private $_linkHandle = array(         'm

PHP 操作redis 封装的类

1 <?php 2 /** 3 * Redis 操作,支持 Master/Slave 的负载集群 4 * 6 */ 7 class RedisCluster{ 8 9 // 是否使用 M/S 的读写集群方案 10 private $_isUseCluster = false; 11 12 // Slave 句柄标记 13 private $_sn = 0; 14 15 // 服务器连接句柄 16 private $_linkHandle = array( 17 'master'=>null,/