为什么数组类型的对象是不可修改的? [英] Why array type object is not modifiable?

查看:128
本文介绍了为什么数组类型的对象是不可修改的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据说<一个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 of int"; except when a is the operand of the sizeof or unary & operators, the expression will be converted to an expression of type "pointer to int" and its value will be the address of the first element in the array;
  • the type of the expression a[i] is int; it refers to the integer object stored as the i'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 like a = b or a = 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 of int", and it refers to the same chunk of memory that a 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 reassign a to refer to a different array object (same for the expression *ptr). You may assign a new value to a[i] or (*ptr)[i] (change the value of each array element), and you may assign ptr 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 declare

struct 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 like

struct 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 as

struct node {
  ... // same as above
  struct node left;
  struct node right;
};

because the type isn't complete when we declare the left and right members, and also because each left and right member would each contain left and right members of their own, each of which would contain left and right 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 variable a can also be part of a larger expression like a[i]; the operator is the subscript operator [] and the operands are the variables a and i. 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 like a[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 an int value, and you want to access that value, you simply use y in an expression, like

x = y;

The type of the expression y is int, so the declaration of y is written

int 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, like

x = a[i];

The type of the expression a[i] is int, so the declaration of the array is written as

int arr[N]; // for some value N.  

The "int-ness" of arr is given by the type specifier int; the "array-ness" of arr is given by the declarator arr[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" as

    a       -- 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 and b evaluate to pointer values in that context; it's equivalent to writing 3 = 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆