数组的元素是否在内存中连续保存?

时间:2015-07-19 16:51:18

标签: c++ arrays string memory

我正在学习编程范例教程并做一些与记忆相关的练习。我刚刚提取了两个问题(请忘记代码的样子,抱歉这是错误的代码,但我只是想从中学习指针的东西)。原始代码使lsearch尽可能通用,因此它使用所有指针。

  1. 我有一个关于访问数组元素的问题:

    当我们访问数组a[]的第四个元素时,我们使用*(a+3); 当我们访问字符串数组char * names[]的第3个元素时,我们使用*(char**)((char*)names+sizeof(char*)*2)。 我们是否假设数组的元素在内存中连续保存?

  2. 关于访问字符串数组元素的问题2:为什么我们需要在(char*)(基地址)前面添加names,我尝试不添加(char*)和代码因分段错误而崩溃。我打印的是(char*)的大小,它是8,而sizeof(names)是32.为什么我们需要(char*)?我真的认为地址是32位,如果我们把它转换成8位,计算机应该很难找到它指向的元素。 谢谢

  3. #include<iostream>
    #include<string>
    #include<typeinfo>
    int main()
    {
            int a[5] = {1,2,3,4,5};
            std::cout << *(a+3) << std::endl;
    
    
            char* names[4] = {"James", "Dustin", "Rich", "Ed"};
            std::cout << *(char**)((char*)names+2*sizeof(char*)) << std::endl;
            //std::cout << sizeof(names) << " " << sizeof(char*) << std::endl;
    }
    

    以下是stanford 107的原始代码。

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    using namespace std;
    
    int StrCmp(void* vp1, void* vp2)
    {
            char *s1 = *(char**)vp1;
            char *s2 = *(char**)vp2;
            if(strcmp(s1,s2)==0){
                    return 0;
            }
            return 1;
    }
    
    char ** lsearch(void *key, void* base, int size, int elemSize, int (*cmpFn)(void *, void *))
    {
            for(int i=0; i<size; ++i){
                    char* addr = (char*) base + elemSize *i;
                    if (cmpFn(key, addr)==0) return (char**)addr;
            }
            return NULL;
    }
    
    int main()
    {
            char *notes[] = {"Ab", "F#", "B", "Gb", "D"};
            char* favorStr = "F#";
            char ** found = lsearch(&favorStr,notes,5,sizeof(char*),StrCmp);
            if(found!=NULL) cout << *(char**)found << endl;
            else cout << "not found" << endl;
    }
    

1 个答案:

答案 0 :(得分:3)

根据C ++标准(8.3.4阵列)

  

1 ...数组类型的对象包含一个连续分配的非空集   N个T型子对象。

从帖子应用数组定义意味着只有四个指针(数组的元素)到字符串文字才会在连续的内存范围内分配。这段内存不包括字符串文字本身。

在C ++中,字符串文字具有常量字符数组的类型。当它们被用作这样的初始化器时

char* names[4] = {"James", "Dustin", "Rich", "Ed"};

然后它们被隐式转换为const char *类型的指针,指向数组的第一个元素。

所以定义像

这样的数组会更好
const char* names[4] = {"James", "Dustin", "Rich", "Ed"};

如果要输出数组的第三个元素(考虑到它是一个指针),那么你必须写

std::cout << ( const void * ) ( names + 2 ) << std::endl;

如果要输出此元素指向的字符串文字,则应编写简单的

std::cout << *( names + 2 ) << std::endl;

至于表达

*(char**)((char*)names+2*sizeof(char*)) 

然后子表达式(char*)names重新解释类型为char *[4] like an array pf type char [4 * sizeof(char *)]的数组。所以表达

(char*)names+2*sizeof(char*)

产生与表达式

相同的值
( names + 2 )

例如声明

std::cout << ( const void * )( names + 2 ) << std::endl;

std::cout << ( void * )( (char*)names + 2*sizeof(char*) ) << std::endl;

将产生相同的输出

我认为如果要运行这个示范程序会更清楚

#include <iostream>

int main() 
{
    const char* names[4] = {"James", "Dustin", "Rich", "Ed"};

    for ( size_t i = 0; i < 4; i++ ) 
    {        
        std::cout << ( const void * )( names + i ) << std::endl;
    }

    std::cout << std::endl;

    for ( size_t i = 0; i < 4; i++ ) 
    {        
        std::cout << ( const void * )*( names + i ) << std::endl;
    }

    std::cout << std::endl;

    for ( size_t i = 0; i < 4; i++ ) 
    {        
        std::cout << ( const void * )( ( char * )names + i * sizeof( char * ) ) << std::endl;
    }

    std::cout << std::endl;

    for ( size_t i = 0; i < 4; i++ ) 
    {        
        std::cout << ( const void * )*( ( const char ** )( ( const char * )names + i * sizeof( char * ) ) ) << std::endl;
    }

    std::cout << std::endl;
}

程序输出可能看起来像

0x7fffc0f639f0
0x7fffc0f639f8
0x7fffc0f63a00
0x7fffc0f63a08

0x40d884
0x40d88a
0x40d891
0x40d896

0x7fffc0f639f0
0x7fffc0f639f8
0x7fffc0f63a00
0x7fffc0f63a08

0x40d884
0x40d88a
0x40d891
0x40d896

在此程序中,( names + 0 )( names + 1 )( names + 2 )( names + 3 )是索引为0, 1, 2, 3的数组名称的相应元素的地址。 表达式*( names + 0 )*( names + 1 )*( names + 2 )*( names + 3 )是存储在此元素中的值。

表达式( char * )names + i * sizeof( char * )其中i位于0-3范围内 是数组元素的相同地址。和表达 *((const char **)((const char *)names + i * sizeof(char *)))给出数组元素的相同值。