如果 "argstr" 是 C 中的二维数组,argstr = argstr[0] 如何?为什么值没有不同?



我在程序下面写了。

#include<windows.h>
#include <stdio.h>
#include<tchar.h>
#define MAX_ARG 5 
#define MAX_COMMAND_LINE 10 
int _tmain(int argc, LPTSTR argv[]){
BOOL exitFlag = FALSE;
TCHAR command[MAX_COMMAND_LINE], *pc;
DWORD i, localArgc;
TCHAR argstr[MAX_ARG][MAX_COMMAND_LINE] = {"abcdef", "Dravid", "sachin", "ganguli" };
char name[10] = "Happy";
// Why argstr == argstr[0]
printf("%p   %p n", name,  name[0] );      // name != name[0]  
printf("%p  %p = %pn", argstr, argstr[0], *argstr);

// Why there is no difference if it increase left or right variable value? 
TCHAR *place = malloc(10 * sizeof(TCHAR *));
place = argstr[0];
printf("%c  %c n", *++place, *place ); // Incremented the left parameter
printf("%c  %c n", *place, *++place ); // Incremented the right parameter
return 0;
}

1( 为什么 argstr == argstr[0] ?

2(在printf中,计算是从哪一侧(右/左(进行的?

TCHAR> 为了清楚起见,如果argstr声明为

char argstr[MAX_ARG][MAX_COMMAND_LINE] = {"abcdef", "Dravid", "sachin", "ganguli" };

那么它的类型可以用英语表示为"MAX_COMMAND_LINE char数组的MAX_ARG数组"。 另一方面,argstr[0]的类型是"MAX_COMMAND_LINE char数组"。 这两个对象甚至没有兼容的类型,因此它们不能相等。

据我所知,您提供的代码实际上并没有测试它们的相等性。 也就是说,它无处试图评估argstr == argstr[0]. 如果是这样,并且编译器值得使用,那么它至少会警告操作数之间的类型不匹配。 但是,它可能仍接受代码,并且在运行时,比较的计算结果可能为 true。 为什么?

首先,您需要认识到,在几乎所有 C 上下文中,计算结果为数组的表达式都会衰减到指向数组第一个元素的指针。 特别是,由于argstrargstr[0]都指定数组,因此表达式 argstr == argstr[0] 等效于 &argstr[0] == &argstr[0][0]

此外,C 要求数组的第一个元素与数组本身具有相同的地址——也就是说,数组在其第一个元素之前不能包含任何填充。 因此,argstr[0][0]的地址与argstr[0]的地址相同。 这就是为什么如果编译器接受argstr == argstr[0]尽管类型不匹配,它很可能计算为 true。

您实际测试的是printf()如何打印这两个指针。 您在这里再次遇到类型问题,因为printf()%p字段描述符要求相应的参数具有类型 void * ,并且实际的相应参数具有不同的(指针(类型。 从形式上讲,由于这个原因,您的代码具有未定义的行为,但是在许多所有对象指针都具有相同表示形式的实现中,它可能会产生与此正确版本相同的结果:

printf("%p  %p = %pn", (void *) argstr, (void *) argstr[0], (void *) *argstr);

尽管不能保证,但很有可能以相同的方式格式化每个指针,因为它们都表示相同的地址。

首先,根据您的声明,argstr在内存中布局如下:

        +---+
argstr: |'a'| argstr[0][0]
        +---+ 
        |'b'| argstr[0][1]
        +---+
        |'c'| argstr[0][2]
        +---+
         ...
        +---+
        |'f'| argstr[0][5]
        +---+
        | 0 | argstr[0][6]
        +---+
         ...
        +---+
        | ? | argstr[0][9]
        +---+
        |'D'| argstr[1][0]
        +---+
        |'r'| argstr[1][1]
        +---+
         ...
        +---+
        | ? | argstr[1][9]
        +---+
        |'s'| argstr[2][0]
        +---+
         ...
        +---+
        | ? | argstr[4][9]
        +---+

所有 50 个数组元素按行主顺序依次排列(即,第一行的所有元素,后跟第二行的所有元素,依此类推(。 请注意,没有为任何地方的任何指针留出存储空间。 这意味着第一个子数组(&argstr[0][0](的第一个元素的地址与数组的第一个元素(&argstr[0](的地址相同,与数组(&argstr(的地址相同。

除非它是sizeof或一元&运算符的操作数,或者字符串文本用于初始化声明中的另一个数组,否则类型为"T的 N 元素数组"的表达式将被转换("衰减"(为"指向T的指针"类型的表达式,并且表达式的值将是数组第一个元素的地址。

printf调用中,表达式argstr的类型为"5 元素数组的 10 元素数组TCHAR"。 由于它不是sizeof或一元&运算符的操作数,因此将其转换为类型为"指向 10 元素数组的 TCHAR 的指针"或 TCHAR (*)[10] 的表达式,并且表达式的值是第一个元素的地址 ( &argstr[0] (。

在同一调用中,表达式argstr[0]的类型为"10 元素数组TCHAR"。 由于它不是sizeof或一元&运算符的操作数,因此将其转换为类型为"指向TCHAR的指针"或TCHAR *的表达式,并且表达式的值是第一个元素(&argstr[0][0](的地址。

管他呢? 为什么数组表达式的类型会变成指针?

数组下标运算符[]是根据指针算法定义的:表达式 a[i] 定义为 *(a + i) 。 给定一个地址a,从该地址偏移i元素(不是字节(并取消引用结果。 这是从 C 派生的 B 编程语言的延续。 在 B 中,创建了一个单独的指针作为数组定义的一部分;该指针将绑定到数组名称并指向数组的第一个元素,因此在 B 中&argstr&argstr[0]确实会有所不同。

Ritchie 在设计 C 时摆脱了数组指针,但他想保留 B 的数组语义,所以他创建了上面的数组转换规则。 在像 argstr[i][j] 这样的表达式中,子表达式 argstr 首先被转换为指针值,该指针值用 [i] 下标;这给了我们另一个数组表达式,该表达式被转换为一个新的指针表达式,该表达式下标为 [j] .

综上所述,给定声明TCHAR argstr[5][10],以下所有内容都是正确的:

    Expression        Type              Decays to        Value
    ----------        ----              ---------        -----
        argstr        TCHAR [5][10]     TCHAR (*)[10]    Address of argstr[0]
       &argstr        TCHAR (*)[5][10]  n/a              Address of argstr
       *argstr        TCHAR [10]        TCHAR *          Value of argstr[0]
     argstr[i]        TCHAR [10]        TCHAR *          Address of argstr[i][0]
    &argstr[i]        TCHAR (*)[10]     n/a              Address of argstr[i]
    *argstr[i]        TCHAR             n/a              Value of argstr[i][0]
  argstr[i][j]        TCHAR             n/a              Value of argstr[i][j]
 &argstr[i][j]        TCHAR *           n/a              Address of argstr[i][j]

argstr&argstr*argstrargstr[0]&argstr[0]&argstr[0][0]都产生相同的(数组第一个元素的地址(,但表达式的类型不同。

1( 为什么 argstr == argstr[0] ?

argstr 是一个多维数组。 argstr[0]仍然是一个数组,因此是一个地址,并且由于argstr数据以argstr[0]开头,内存中的地址是相同的。

2(在printf中,计算是从哪一侧(右/左(进行的?

你不会想知道的。你正在做的事情真的很危险,而且不便携。只是不要。

PS:在:

printf("%p   %p n", name,  name[0] );
%p不适用于name[0],它是

字符,而不是指针。 printf 将字符的值转换为指针的无效值。不太有趣的:)

另一方面,下面的值将产生相同的值:

printf("%p   %p n", name,  &name[0] );

因为数组是通过内部使用指针进行内存分配在 C 中实现的。假设一个 2D int 数组int a[5][4]; .指针用于指示为数组 a.It 分配的内存块的第一个地址可通过 a 或 a[0] 访问,因此 argstr 和 argstr[0] 指向同一 block.so argstr == argstr[0]。由于最低可寻址内存单元是字节,因此 a 或 a[0] 指向whole allocated block的第一个字节,即 (1byte * sizeof(datatype((,因为 4 字节表示整数。假设 a 的地址 = 1000000,所以 a 包含 1000000。在a中,5*5*sizeof(int(是分配的内存块的大小,其中:

    a[0
  • ][0] 或 a 指向第一行中的第一个块,大小为 4 字节(sizeof(int((
  • a[0][1] 或 *(a[0]+1( 或 *(a+1( 指向第一行中的第二个块,大小为 4 字节(sizeof(int((
  • a[1][0] 或 *(a + 1*5 + 0( 指向第二行的第一个块,大小为 4 字节
  • 等等。

正如您将注意到的,数组是按顺序分配的,因此要找到一行,我们需要将总行号乘以当前行号得到 a[1][0]。

相关内容

  • 没有找到相关文章