何时将数组名称或函数名称“转换"为指针?(在 C 中) [英] When is an array name or a function name 'converted' into a pointer ? (in C)

查看:33
本文介绍了何时将数组名称或函数名称“转换"为指针?(在 C 中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

1) 误解 :

  • 每当在 C 语言中声明一个数组时,就会隐式地创建(数组的名称)一个指向数组第一个元素的指针.(是吗?我不这么认为!)

  • Whenever an array is declared in C language, a pointer to the first element of the array is created (the name of the array) implicitly. (Is it? I don't think so!)

的前两行这个页面(虽然我不确定信息的正确性)是一样的.

The first two lines of this page (though I am not sure about the correctness of the information) state the same.

正如我们所见,当我们声明一个数组时,会为数组的单元格分配一个连续的内存块,并且还会分配并初始化一个指针单元格(适当类型的)以指向数组的第一个单元格大批.

As we have seen, when we declare an array, a contiguous block of memory is allocated for the cells of the array and a pointer cell (of the appropriate type) is also allocated and initialized to point to the first cell of the array.

  • 但是当我输出包含in那个指针的地址和of那个指针的地址时,它们结果是一样的.所以,我认为指针毕竟不是创建的.

  • But when I output the address contained in that pointer and the address of that pointer, they turn out to be the same. So, I think a pointer is not created after all.

    2) 我从 this 问题.

    • 在大多数情况下,数组名称被转换为指针.

    谁能详细解释何时编译器决定将数组名转换为指针,以及为什么?

    Can anyone give a detailed explanation of WHEN the compiler decides to convert an array name into a pointer, and WHY?

    PS:请用功能解释相同的内容.同样在 this 链接中,给出了一个例子,说对于函数 int square(int,int), square, &square, *square, **square> 指的是同一个函数指针.你可以解释吗?

    PS: Please explain the same with functions. Also in this link, an example was given, saying that for a function int square(int,int), any of the square, &square, *square, **square refers to the same function pointer. Can you explain?

    代码片段

    int fruits[10];
    printf("Address IN constant pointer is %p
    ",  fruits);
    printf("Address OF constant pointer is %p
    ", &fruits); 
    

    输出:

    Address IN constant pointer is 0xbff99ca8
    Address OF constant pointer is 0xbff99ca8
    

    推荐答案

    数组类型的表达式被隐式转换为指向数组对象第一个元素的指针除非是:

    An expression of array type is implicitly converted to a pointer to the array object's first element unless it is:

    • 一元&运算符的操作数;
    • sizeof的操作数;或
    • 用于初始化数组对象的初始化程序中的字符串文字.
    • The operand of the unary & operator;
    • The operand of sizeof; or
    • A string literal in an initializer used to initialize an array object.

    第三种情况的一个例子是:

    An examples of the third case are:

    char arr[6] = "hello";
    

    hello" 是一个数组表达式,类型为 char[6](5 加 1 表示 '' 终止符).它不会转换为地址;"hello" 的完整 6 字节值被复制到数组对象 arr 中.

    "hello" is an array expression, of type char[6] (5 plus 1 for the '' terminator). It's not converted to an address; the full 6-byte value of of "hello" is copied into the array object arr.

    另一方面,在此:

    char *ptr = "hello";
    

    数组表达式 hello" decays"指向 'h' 的指针,该指针值用于初始化指针对象 ptr.(它真的应该是 const char *ptr,但这是一个附带问题.)

    the array expression "hello" "decays" to a pointer to the 'h', and that pointer value is used to initialize the pointer object ptr. (It should really be const char *ptr, but that's a side issue.)

    函数类型的表达式(例如函数名)被隐式转换为指向函数的指针除非它是:

    An expression of function type (such as a function name) is implicitly converted to a pointer to the function unless it is:

    • 一元&运算符的操作数;或
    • sizeof 的操作数(sizeof function_name 是非法的,不是指针的大小).
    • The operand of the unary & operator; or
    • The operand of sizeof (sizeof function_name is illegal, not the size of a pointer).

    就是这样.

    在这两种情况下,都没有创建指针对象.该表达式被转换为(衰减"为)一个指针值,也称为地址.

    In both cases, no pointer object is created. The expression is converted to ("decays" to) a pointer value, also known as an address.

    (在这两种情况下的转换"不是像由强制转换运算符指定的那样的普通类型转换.它不获取操作数的值并使用它来计算结果的值,就像 int-to-float 转换会发生的那样.而是数组或函数类型的 表达式 在编译时转换"为指针类型的表达式.在我看来,调整"这个词比转换"更清楚.)

    (The "conversion" in both these cases isn't an ordinary type conversion like the one specified by a cast operator. It doesn't take the value of an operand and use it to compute the value of the result, as would happen for an int-to-float conversion. Rather an expression of array or function type is "converted" at compile time to an expression of pointer type. In my opinion the word "adjusted" would have been clearer than "converted".)

    请注意,数组索引运算符 [] 和函数调用operator"都在() 需要一个指针.在像 func(42) 这样的普通函数调用中,函数名 func decays"指向函数的指针,然后在调用中使用该指针.(这种转换实际上不需要在生成的代码中执行,只要函数调用正确.)

    Note that both the array indexing operator [] and the function call "operator" () require a pointer. In an ordinary function call like func(42), the function name func "decays" to a pointer-to-function, which is then used in the call. (This conversion needn't actually be performed in the generated code, as long as the function call does the right thing.)

    函数规则有一些奇怪的后果.在大多数情况下,表达式 func 被转换为指向函数 func 的指针.在 &func 中,func 没有转换为指针,但 & 产生函数的地址,即指针值.在 *func 中,func 被隐式转换为一个指针,然后 * 取消引用它以产生函数本身,然后(在大多数上下文中)) 转换为指针.在****func中,这种情况反复发生.

    The rule for functions has some odd consequences. The expression func is, in most contexts, converted to a pointer to the function func. In &func, func is not converted to a pointer, but & yields the function's address, i.e., a pointer value. In *func, func is implicitly converted to a pointer, then * dereferences it to yield the function itself, which is then (in most contexts) converted to a pointer. In ****func, this happens repeatedly.

    (C11 标准草案说数组还有另一个例外,即当数组是新的 _Alignof 运算符的操作数时.这是草案中的错误,在最终版中更正已发布的 C11 标准;_Alignof 只能应用于带括号的类型名称,不能应用于表达式.)

    (A draft of the C11 standard says that there's another exception for arrays, namely when the array is the operand of the new _Alignof operator. This is an error in the draft, corrected in the final published C11 standard; _Alignof can only be applied to a parenthesized type name, not to an expression.)

    数组的地址及其第一个成员的地址:

    The address of an array and the address of its first member:

    int arr[10];
    &arr;    /* address of entire array */
    &arr[0]; /* address of first element */
    

    是相同的内存地址,但它们的类型不同.前者是整个数组对象的地址,类型为int(*)[10](指向10个int的数组的指针);后者是 int* 类型.这两种类型不兼容(例如,您不能合法地将 int* 值分配给 int(*)[10] 对象),并且指针算术行为对他们有不同的看法.

    are the same memory address, but they're of different types. The former is the address of the entire array object, and is of type int(*)[10] (pointer to array of 10 ints); the latter is of type int*. The two types are not compatible (you can't legally assign an int* value to an int(*)[10] object, for example), and pointer arithmetic behaves differently on them.

    有一个单独的规则规定,声明的数组或函数类型的函数参数在编译时(未转换)调整为指针参数.例如:

    There's a separate rule that says that a declared function parameter of array or function type is adjusted at compile time (not converted) to a pointer parameter. For example:

    void func(int arr[]);
    

    完全等同于

    void func(int *arr);
    

    这些规则(数组表达式的转换和数组参数的调整)结合起来,对 C 中数组和指针之间的关系造成了很大的混淆.

    These rules (conversion of array expressions and adjustment of array parameters) combine to create a great deal of confusion regarding the relationship between arrays and pointers in C.

    comp.lang.c FAQ 的第 6 节很好地解释了细节.

    Section 6 of the comp.lang.c FAQ does an excellent job of explaining the details.

    这方面的权威来源是 ISO C 标准.N1570 (1.6 MB PDF) 是2011年标准的最新草案;这些转换在第 6.3.2.1 节的第 3(数组)和第 4(函数)段中指定.该草案错误地引用了 _Alignof,实际上并不适用.

    The definitive source for this is the ISO C standard. N1570 (1.6 MB PDF) is the latest draft of the 2011 standard; these conversions are specified in section 6.3.2.1, paragraphs 3 (arrays) and 4 (functions). That draft has the erroneous reference to _Alignof, which doesn't actually apply.

    顺便说一下,您示例中的 printf 调用是完全不正确的:

    Incidentally, the printf calls in your example are strictly incorrect:

    int fruits[10];
    printf("Address IN constant pointer is %p
    ",fruits);
    printf("Address OF constant pointer is %p
    ",&fruits); 
    

    %p 格式需要 void* 类型的参数.如果 int*int(*)[10] 类型的指针与 void* 具有相同的表示并作为参数传递方式,就像大多数实现的情况一样,它可能会起作用,但不能保证.您应该显式地将指针转换为 void*:

    The %p format requires an argument of type void*. If pointers of type int* and int(*)[10] have the same representation as void* and are passed as arguments in the same way, as is the case for most implementations, it's likely to work, but it's not guaranteed. You should explicitly convert the pointers to void*:

    int fruits[10];
    printf("Address IN constant pointer is %p
    ", (void*)fruits);
    printf("Address OF constant pointer is %p
    ", (void*)&fruits);
    

    那为什么会这样呢?问题是数组在某种意义上是 C 中的二等公民.您不能在函数调用中通过值传递数组作为参数,也不能将它作为函数结果返回.要使数组有用,您需要能够对不同长度的数组进行操作.将 strlen 函数分别用于 char[1]char[2]char[3] 和等等(所有这些都是不同的类型)将非常笨拙.因此,数组是通过指向其元素的指针来访问和操作的,指针算法提供了一种遍历这些元素的方法.

    So why is it done this way? The problem is that arrays are in a sense second-class citizens in C. You can't pass an array by value as an argument in a function call, and you can't return it as a function result. For arrays to be useful, you need to be able to operate on arrays of different lengths. Separate strlen functions for char[1], for char[2], for char[3], and so forth (all of which are distinct types) would be impossibly unwieldy. So instead arrays are accessed and manipulated via pointers to their elements, with pointer arithmetic providing a way to traverse those elements.

    如果一个数组表达式没有衰减为一个指针(在大多数情况下),那么你对结果就无能为力了.C 源自早期的语言(BCPL 和 B),它们甚至不一定区分数组和指针.

    If an array expression didn't decay to a pointer (in most contexts), then there wouldn't be much you could do with the result. And C was derived from earlier languages (BCPL and B) that didn't necessarily even distinguish between arrays and pointers.

    其他语言能够将数组作为一流类型处理,但这样做需要额外的功能,而这些功能不符合 C 的精神",而 C 仍然是一种相对低级的语言.

    Other languages are able to deal with arrays as first-class types but doing so requires extra features that wouldn't be "in the spirit of C", which continues to be a relatively low-level language.

    我不太确定以这种方式处理函数的理由.确实没有函数类型的,但是语言可能需要一个函数(而不是函数指针)作为函数调用的前缀,需要一个显式的* 间接调用运算符:(*funcptr)(arg).能够省略 * 是一种方便,但不是很大.这可能是历史惯性和对数组处理的一致性的结合.

    I'm less sure about the rationale for treating functions this way. It's true that there are no values of function type, but the language could have required a function (rather than a pointer-to-function) as the prefix in a function call, requiring an explicit * operator for an indirect call: (*funcptr)(arg). Being able to omit the * is a convenience, but not a tremendous one. It's probably a combination of historical inertia and consistency with the treatment of arrays.

    这篇关于何时将数组名称或函数名称“转换"为指针?(在 C 中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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