当您声明指向char数组的指针时,内存中存储了什么?

时间:2017-04-17 23:18:20

标签: c++ arrays pointers

让我们说

char arr[] = "test";

我读到数组就像一个指向字符串的指针。因此,当我这样做时:

cout << arr << endl;

我得到test。当我做的时候

char *ptr = arr

变量ptr现在应该存储arr指针的地址。但是,如果我这样做

cout << ptr << endl

我得到test。如果它基本上是一个指向指针的指针,为什么不进行“测试”:

cout << *ptr << endl; 

有人可以根据内存的分配情况向我解释一下吗?

3 个答案:

答案 0 :(得分:0)

&#34;&lt;&lt;&# operator将 char * 视为C字符串。因此它意味着arr [0-end]和ptr [0-end]; 当你这样做时:

  

char * ptr = arr

您只需为同一目标创建一个新的 char * 。因此,它也被视为C字符串并且隐含地&#34; cout&lt;&lt;&#打印它指向的字符。

这仅适用于 char * ;输入:

  

int a [] = {1,2};           cout&lt;&lt; a&lt;&lt; ENDL;

只需打印 a 数组的地址(第一个元素的地址)。

答案 1 :(得分:0)

char arr[] = "test";

声明数组并初始化为&#34; test&#34;,并且让基地址为1000

char *ptr = arr

声明字符类型指针并将其初始化为相同的基址1000

cout << ptr << endl

打印整个字符串&#34; test&#34;因为1000通过了 但* ptr给出1000的值,而ptr是char类型所以......

cout << *ptr << endl; 

第一个问题&#39; t&#39;打印出现在地址1000.

答案 2 :(得分:0)

铊组成; dr

在C ++中,数组的名称会自动转换为指向其第一个元素的指针。

完整答案

存储在内存中的内容可能因编译器而异,但让我们让一个编译器告诉我们,gcc 6.3.0 for x86_64。 -S标志告诉gcc编译为人类可读的低级汇编代码。 -O标志告诉它优化。我们可以使用g++ -Wall -Wextra -Wpedantic -Wconversion -std=c++14 -O -S来编译以下文件:

char arr[] = "test";
char* ptr = arr;
char* ptr2 = &arr[0];
constexpr unsigned int arr_size = sizeof(arr)/sizeof(arr[0]); // 5
char (*ptr3)[arr_size] = &arr; // A pointer to an array of arr_size chars.
char* const optimized_out = arr;

我会稍微编辑一下输出,以便更容易理解。我们从此命令(以.s结尾)获得的文件稍微重新排列的版本如下:

        .data


        .globl  arr
arr:
        .ascii "test\0"


        .globl  ptr
        .align 8
ptr:
        .quad   arr


        .globl  ptr2
        .align 8
ptr2:
        .quad   arr


        .globl  ptr3
        .align 8
ptr3:
        .quad   arr

那么,这说什么呢? .data声明意味着我们正在声明已编译代码的数据段的内容。这适用于我们可以修改其内容的变量。

.globl声明表示arr是可以与其他源文件链接的符号。未缩进的行arr:ptr:等是表示当前地址的标签。因此,当我们稍后链接到arr:时,我们将链接到.data段内的地址,即我们告诉汇编器放在那里的任何字节。这些是五个ASCII字符test和终止NUL。

同样,ptr是一个全局变量,是.data段内的地址。这里有一个新的指令,.align 8。这意味着将指针放在可被8整除的地址上。(如果gcc实际上以这种方式放置文件,则需要在数组中的五个字节和对齐的指针之间浪费三个额外的填充字节;事实上,它最后放arr所以它不需要。)在x86_64上,对齐的内存读取比未对齐的读取更快。

然后,x86_64程序集中的.quad是一个64位变量,指针的大小。 (64位是16位的4倍,现代64位桌面CPU的远端祖先,8086,是一个16位字的机器。所以quad代表四字。)

此64位内存位置中存储了什么?值arr:,它是五字节.ascii数组的地址。

您会注意到ptr2ptr3在程序集中都有相同的定义。该标准保证数组的名称衰减或隐式转换为指向数组第一个元素的指针。并且数组的地址与其第一个元素的地址相同;在任何数组元素之前都不能有任何填充。

在C ++中,您不能在没有char[]的情况下将char*的地址分配给reinterpret_castchar *this_does_not_work = &arr;不起作用。这只是因为他们有不同的类型。数组的类型为char[5],将ptr3声明为指向五个char对象数组的指针的语法为char (*ptr3)[5]。在这种情况下,为了“简单”,我为arr的大小定义了一个符号常量,以防我们传递给arr的字符串发生变化。数组的大小除以元素的大小等于数组中的元素数。 (该标准保证始终如此。)

地址&arrarr&arr[0]均由标准保证相同;他们之间的唯一区别是他们的类型。您会注意到程序集文件实际上不包含任何类型信息;这允许您在另一个文件中声明类似extern char* const ptr3;的内容并让它工作。如果你还给它-g标志,GCC会将这些信息存储在符号表中,以便进行调试。

您会注意到源文件中有两个没有相应汇编语言定义的变量,constexpr变量arr_sizeconst变量optimized_out。事实上,如果你告诉它不要优化,gcc将包括这两个。使用-O标志,它不会为编译时已知的小常量分配内存;它只会将5替换为arr_sizearr替换为optimized_out。但是,如果你曾经拿过他们的地址,就需要在内存中的某处存储这些变量的副本,例如&optimized_out

其中一些在C中略有不同,而在于C ++。