为什么数组类型的对象是不可修改的? [英] Why array type object is not modifiable?
问题描述
据说<一个href=\"http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Flvalue.htm\">here这
术语修改的左值是用来强调的是,左值允许指定的对象进行以及检查改变。以下对象类型的左值,但不能修改左值:
- 数组类型
- 一个不完整的类型
- 一个const限定的类型
- 其成员之一的结构或联合类型限定为一个const类型
块引用>由于这些左值不可修改,它们不能出现在赋值语句的左侧。
为什么数组类型的对象是不可修改的?是不是正确的写
INT I = 5,[10] = {0};
一个由[i] = 1;?结果
而且,什么是一个不完整的类型?解决方案假设声明
int类型的[10];
那么以下全部为真:
- 中的前pression类型的
A
是10个元素的数组INT
;除了当A
是操作数的sizeof
或一元&安培;
运营商,前pression将被转换为类型的前pression指针INT
,其价值将是第一个地址元素数组中;- 中的前pression类型的
A [I]
是INT
;它指的是存储为整数对象I
'个数组元素;- 的前pression 的
A
可能不是一个赋值的目标,因为C不会像对待其他变量数组,所以你可以不写东西像A = b
或A =的malloc(N * sizeof的* A)
或类似的东西。您会发现我一直强调这个词前pression。有记忆,我们上保留10个整数,我们用它来指的内存块的符号(如pressions)块之间的差异。我们可以参考其与前pression
A
。我们也可以创建一个指向数组:INT(* PTR)[10] =&放大器;一个;
这位前pression
* PTR
也有输入的10个元素的数组INT
,并它指的是相同的内存块的A
引用。C没有治疗阵列前pressions(
A
,* PTR
),如前$ P $其他类型的pssions和的区别之一是,一个前pression的阵列的类型可能不是一个赋值的目标。你不能重新分配A
来引用不同的数组对象(同为前pression* PTR
)。您的可能的分配一个新值A [I]
或(* PTR)[我]
(改变每个数组的值的元素的),你可以指定PTR
来指向不同的数组:INT B〔10],C [10];
.....
PTR =和b;
.....
PTR =和C;至于第二个问题...
这是的不完整的的类型缺乏规模的信息;声明像
结构foo的;
INT酒吧[];
工会bletch;所有创建不完全类型,因为没有足够的信息让编译器确定有多少存储预留该类型的对象。您不能创建不完全类型的对象;例如,你不能声明
结构美孚myFoo;
除非你完成
结构美孚
的定义。但是,您可以创建的指针的不完全类型;例如,你的可能的声明结构美孚* myFooPtr;
在没有完成的
结构美孚
的定义,因为指针只是存储对象的地址,你不需要知道类型的表示该大小。这使得有可能以限定自引用类型,例如结构节点{
T键; //任何类型T
Q VAL; //对于任何键入Q
结构节点*离开;
结构节点*权利;
};为
结构节点
类型定义不是的完整的,直到我们打的结束}
。既然我们可以声明一个指向一个不完整的类型,我们没事。但是,我们不能定义结构作为结构节点{
... //与上述相同
结构节点离开;
结构节点权;
};因为当我们声明的类型不完整的
离开
和右键
成员,又因为每个离开
和右键
成员将分别包含离开
和右键
自己,每个将包含成员离开
和右键
成员的及其的自己,以及和和。这是伟大的结构和联合,但什么
INT栏[];
???
我们已经声明了符号
栏
,并表示,这将是一个数组类型,但大小是在这一点不得而知。的最后的,我们将不得不与一个大小,但这样的符号可在环境中使用,其中数组大小没有意义或有必要定义它。没有一个良好的,非人为的例子从我的头顶来说明这一点,虽然。修改
答复意见在这里,因为不会有空间在评论部分什么我想写(这个晚上,我在一个冗长的心情)。你问:
这是否意味着每一个变量是前pression?
块引用>这意味着,任何变量可以是一个前pression,或离pression的一部分。这里是如何的语言标准定义的术语的前pression 的:
6.5防爆pressions 的结果
1的的前pression 的是运营商和操作数的序列,可指定的计算
值,或者指定的对象或功能,或产生副作用,或者
执行它们的组合。
块引用>例如,变量
A
全部由自己算作一个前pression;它指定我们定义为持有10整数数组对象。它也计算到所述阵列的所述第一元素的地址。变量A
,也可以像较大的前pression的一部分[I]
;运营商是下标运算符[]
和操作数是变量A
和I
。这当然pression指定数组的一个成员,它的计算结果currectly存储在该成员的值。反过来这前pression可以是一个较大的前pression像A [I] = 0
的一部分。
和还让我明确指出,在声明int类型的[10],做了[]表示数组类型
块引用>是的,没错。
在C,声明基于所述类型的前pressions的,而不是对象的类型。如果你有一个名为一个简单的变量
是
存储的INT
价值,你要访问该值,您只需使用是
在离pression,像X = Y;
该类型的前pression的的
是
是INT
,所以y的声明
写INTÿ;
如果,在另一方面,你有一个的阵列的
INT
价值观,你要访问一个特定的元素,你可以使用数组名和下标运算符沿索引来访问该值,如X = A [I]
该类型的前pression的的
A [I]
是INT
,所以阵列的声明被写成INT ARR [N]; //对于一些价值N.
在
INT
-ness的改编
通过类型说明符给定的INT
;的改编
的阵性是由声明符改编[N]给出
。该声明符给我们的目标正在申报(改编
)与不被类型说明符给出了一些附加的类型信息以及名称(是一个N元素的数组)。声明阅读为A - A
一个[N] - 是一个N个元素的数组
诠释一个[N];整型 -修改 2
毕竟,我还没有告诉你的真正的原因阵列前pressions是不可修改的左值。因此,这里的又一篇章,以这本书的答案。
ç没有被弹簧完全从丹尼斯里奇的心灵形成;它是从已知为B较早语言(其从BCPL衍生)衍生 1 B为一个无类型的语言。;它没有不同类型的整数,浮点数,文字记录等相反,一切都只是一个固定长度的字或细胞(基本上是一个无符号整数)。存储器被视为细胞的线性阵列。当分配B中的阵列,例如
自动垂直[10];
编译器分配的11个细胞;连续10个细胞数组本身,以及被结合到含有V的第一单元的位置的单元:
+ ---- +
五:| | ----- +
+ ---- + |
... |
+ ---- + |
| | &LT; ---- +
+ ---- +
| |
+ ---- +
| |
+ ---- +
| |
+ ---- +
...当里奇被加入
结构
类型C,他意识到这安排是造成他一些问题。例如,他想创建一个结构类型重新present在一个文件或目录表中的条目:结构{
INT i编号;
焦炭名[14];
};他想要的结构不只是描述一个抽象的方式进入,还要重新present在实际的文件表项中的比特,它不具有一个额外的细胞或字来存储的位置阵列中的第一个元素。于是,他摆脱了它 - 而不是留出单独的位置来存储第一个元素的地址,他写了C,使得该阵列前pression评估时的第一个元素的地址将被计算出来。
的这的是为什么你不能做这样的事情。
int类型的[N],B [N];
A = B;因为无论
A
和B
评估为指针的值的在这方面;它等同于写3 = 4
。没有什么记忆,实际上商店的数组中的第一个元素的地址;在翻译阶段的编译器只是计算它。
<分> 1。这是所有的从纸张 c语言的发展 子>It is stated here that
The term modifiable lvalue is used to emphasize that the lvalue allows the designated object to be changed as well as examined. The following object types are lvalues, but not modifiable lvalues:
- An array type
- An incomplete type
- A const-qualified type
- A structure or union type with one of its members qualified as a const type
Because these lvalues are not modifiable, they cannot appear on the left side of an assignment statement.
Why array type object is not modifiable? Isn't it correct to write
int i = 5, a[10] = {0}; a[i] = 1;
?
And also, what is an incomplete type?解决方案Assume the declaration
int a[10];
then all of the following are true:
- the type of the expression
a
is "10-element array ofint
"; except whena
is the operand of thesizeof
or unary&
operators, the expression will be converted to an expression of type "pointer toint
" and its value will be the address of the first element in the array;- the type of the expression
a[i]
isint
; it refers to the integer object stored as thei
'th element of the array;- The expression
a
may not be the target of an assignment because C does not treat arrays like other variables, so you cannot write something likea = b
ora = malloc(n * sizeof *a)
or anything like that.You'll notice I keep emphasizing the word "expression". There's a difference between the chunk of memory we set aside to hold 10 integers and the symbols (expressions) we use to refer to that chunk of memory. We can refer to it with the expression
a
. We can also create a pointer to that array:int (*ptr)[10] = &a;
The expression
*ptr
also has type "10-element array ofint
", and it refers to the same chunk of memory thata
refers to.C does not treat array expressions (
a
,*ptr
) like expressions of other types, and one of the differences is that an expression of array type may not be the target of an assignment. You cannot reassigna
to refer to a different array object (same for the expression*ptr
). You may assign a new value toa[i]
or(*ptr)[i]
(change the value of each array element), and you may assignptr
to point to a different array:int b[10], c[10]; ..... ptr = &b; ..... ptr = &c;
As for the second question...
An incomplete type lacks size information; declarations like
struct foo; int bar[]; union bletch;
all create incomplete types because there isn't enough information for the compiler to determine how much storage to set aside for an object of that type. You cannot create objects of incomplete type; for example, you cannot declare
struct foo myFoo;
unless you complete the definition for
struct foo
. However, you can create pointers to incomplete types; for example, you could declarestruct foo *myFooPtr;
without completing the definition for
struct foo
because a pointer just stores the address of the object, and you don't need to know the type's size for that. This makes it possible to define self-referential types likestruct node { T key; // for any type T Q val; // for any type Q struct node *left; struct node *right; };
The type definition for
struct node
isn't complete until we hit that closing}
. Since we can declare a pointer to an incomplete type, we're okay. However, we could not define the struct asstruct node { ... // same as above struct node left; struct node right; };
because the type isn't complete when we declare the
left
andright
members, and also because eachleft
andright
member would each containleft
andright
members of their own, each of which would containleft
andright
members of their own, and on and on and on.That's great for structs and unions, but what about
int bar[];
???
We've declared the symbol
bar
and indicated that it will be an array type, but the size is unknown at this point. Eventually we'll have to define it with a size, but this way the symbol can be used in contexts where the array size isn't meaningful or necessary. Don't have a good, non-contrived example off the top of my head to illustrate this, though.EDIT
Responding to the comments here, since there isn't going to be room in the comments section for what I want to write (I'm in a verbose mood this evening). You asked:
Does it mean every variables are expression?
It means that any variable can be an expression, or part of an expression. Here's how the language standard defines the term expression:
6.5 Expressions
1 An expression is a sequence of operators and operands that specifies computation of a value, or that designates an object or a function, or that generates side effects, or that performs a combination thereof.For example, the variable
a
all by itself counts as an expression; it designates the array object we defined to hold 10 integer values. It also evaluates to the address of the first element of the array. The variablea
can also be part of a larger expression likea[i]
; the operator is the subscript operator[]
and the operands are the variablesa
andi
. This expression designates a single member of the array, and it evaluates to the value currectly stored in that member. That expression in turn can be part of a larger expression likea[i] = 0
.And also let me clear that, in the declaration int a[10], does a[] stands for array type
Yes, exactly.
In C, declarations are based on the types of expressions, rather than the types of objects. If you have a simple variable named
y
that stores anint
value, and you want to access that value, you simply usey
in an expression, likex = y;
The type of the expression
y
isint
, so the declaration ofy
is writtenint y;
If, on the other hand, you have an array of
int
values, and you want to access a specific element, you would use the array name and an index along with the subscript operator to access that value, likex = a[i];
The type of the expression
a[i]
isint
, so the declaration of the array is written asint arr[N]; // for some value N.
The "
int
-ness" ofarr
is given by the type specifierint
; the "array-ness" ofarr
is given by the declaratorarr[N]
. The declarator gives us the name of the object being declared (arr
) along with some additional type information not given by the type specifier ("is an N-element array"). The declaration "reads" asa -- a a[N] -- is an N-element array int a[N]; -- of int
EDIT2
And after all that, I still haven't told you the real reason why array expressions are non-modifiable lvalues. So here's yet another chapter to this book of an answer.
C didn't spring fully formed from the mind of Dennis Ritchie; it was derived from an earlier language known as B (which was derived from BCPL).1 B was a "typeless" language; it didn't have different types for integers, floats, text, records, etc. Instead, everything was simply a fixed length word or "cell" (essentially an unsigned integer). Memory was treated as a linear array of cells. When you allocated an array in B, such as
auto V[10];
the compiler allocated 11 cells; 10 contiguous cells for the array itself, plus a cell that was bound to V containing the location of the first cell:
+----+ V: | | -----+ +----+ | ... | +----+ | | | <----+ +----+ | | +----+ | | +----+ | | +----+ ...
When Ritchie was adding
struct
types to C, he realized that this arrangement was causing him some problems. For example, he wanted to create a struct type to represent an entry in a file or directory table:struct { int inumber; char name[14]; };
He wanted the structure to not just describe the entry in an abstract manner, but also to represent the bits in the actual file table entry, which didn't have an extra cell or word to store the location of the first element in the array. So he got rid of it - instead of setting aside a separate location to store the address of the first element, he wrote C such that the address of the first element would be computed when the array expression was evaluated.
This is why you can't do something like
int a[N], b[N]; a = b;
because both
a
andb
evaluate to pointer values in that context; it's equivalent to writing3 = 4
. There's nothing in memory that actually stores the address of the first element in the array; the compiler simply computes it during the translation phase.
1. This is all taken from the paper The Development of the C Language这篇关于为什么数组类型的对象是不可修改的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!