数组和指针

10 篇文章 0 订阅

目录

 

1.什么是声明,什么是定义?

2.数组和指针是如何访问的?

3.当你“定义为指针,但以数组方式引用”时会发生什么?

4. 数组和指针的其他区别

5. 什么时候数组和指针相同?

6. 多维数组的内存布局

7. 指针数组

8 数组和指针时如何被编译器修改的


1.什么是声明,什么是定义?

定义只能出现在一个地方确定对象的类型并分配内存,用于创建新的对象。例如:int my_array[100];
声明可以多次出现描述对象的类型,用于指代其他地方定义额对象(例如在其他文件里)例:extern int my_array[];

区分定义和声明:

声明相当于普通的声明:它所说明的并非自身,而是描述其他地方创建的对象

定义相当于特殊的声明:它为对象分配内存

extern对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。由于并未在声明中为数组分配内存,所以并不需要提供关于数组长度的信息。

2.数组和指针是如何访问的?

编译器为每个变量分配一个地址,这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值只有在运行时才可知。如何需要用到变量中存储的值,编译器就发出指令从指定地址读入变量值并将它存于寄存器中

这就是为什么extern char a[]与extern char a[100]等价的原因。这两个声明都提示a是一个数组,也就是一个内存地址,数组内的字符可以从这个地址找到。编译器并不需要知道数组总共有多长,因为它只产生偏离起始地址的偏移地址。从数组提取一个字符,只要简单地从符号表显示的a的地址加上下标,需要的字符就位于这个地址中。

相反,如果声明extern char *p,它将告诉编译器p是一个指针(在许多现代的机器里它是个四字节的对象),它指向的对象是一个字符。为了取得这个字符,必须得到地址p的内容,把它作为字符的地址并从这个地址中取得这个字符。指针的访问灵活的多,但需要增加一次额外的提取。

3.当你“定义为指针,但以数组方式引用”时会发生什么?

 编译器会:

a.取得符号表中p的地址,提取存储于此处的指针

b.把下标所表示的偏移量与指针的值相加,产生一个地址

c.访问上面这个地址,取得字符.

4. 数组和指针的其他区别

数组和指针的区别
指针数组
保存数据的地址保存数据

间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据

如果指针有一个下标[i],那就把指针的内容加上i作为地址,从中提取数据

直接访问数据,a[i]只是简单以a+i为地址取得数据
通常用于动态数据结构通常用于存储固定数目且数据类型相同的元素
相关的函数未malloc() free()隐式分配和删除
通常指向匿名数据自身即为数据名

数组和指针都可以在它们的定义中用字符串常量初始化.尽管看上去一样,底层的机制却不相同。

定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。例如,下面的定义创建了一个字符串常量(为其分配了内存):

char *p = "breadfruit";

注意只有对字符串常量才是如此,不能指望为浮点数之类的常量分配空间,如:

float *pip = 3.141;//错误,无法通过编译

在ANSI C中,初始化指针时所创建的字符串常量被定义为只读。如果试图通过指针修改这个字符串的值,程序就会出现未定义的行为。在有些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改。

数组也可以用字符串常量进行初始化:

char a[] = "gooseberry";

与指针相反,由字符串常量初始化的数组是可以修改的。

5. 什么时候数组和指针相同?

声明本身还可以进一步分为3种情况:

a.外部数组(external array)的声明

b.数组的定义(定义时声明的一种特殊情况,它分配内存空间,并可能提供一个初始值)

c.函数参数的声明

所有作为函数参数的数组名总是可以通过编译器转换为指针

 

什么时候数组和指针时相同的

规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针

规则2:下标总是与指针的偏移量相同

规则3:在函数参数的声明中,数组名被编译器当做指向该数组第一个元素的指针

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char a[] = "helloworld";
    char *p = a;
    printf("a:0x%x,&a[0]:0x%x,p:0x%x,&a:0x%x, &p:0x%x\n", a, &a[0], p, &a, &p);
    printf("a[1]:%d,*(p+1):%d,p[1]:%d,*(a+1):%d\n", a[1], *(p+1), p[1], *(a+1));
    return 0;
}
a:0x61ff05,&a[0]:0x61ff05,p:0x61ff05,&a:0x61ff05, &p:0x61ff00
a[1]:101,*(p+1):101,p[1]:101,*(a+1):101

可以看到,a的值是第一个元素a[0]的地址。

6. 多维数组的内存布局

假如我们具有以下声明:

char pea[4][6];

pea[i][j]将被编译器解析为:

*(*(pea + i) + j)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char a[4][6] = {"hello", "world" , "cloud", "sunny"};
    int i = 0, j = 0;
    for(i = 0; i < 4; i++)
    {
        printf("&a[%d]:0x%x,a[%d]:0x%x, a+%d:0x%x, *(a+%d):0x%x, *(*(a+%d)+1):%c\n", i, &a[i], i, a[i], i, a+i, i, *(a+i), i, *(*(a+i) + 1));
        for(j = 0; j < 6; j++)
        {
            printf("*(a+%d)+%d:=0x%x,*(*(a+%d)+%d):%c, a+%d+%d:0x%x\n", i, j, *(a + i) + j, i, j, *(*(a + i) + j), i, j, a + i + j);
        }
    }
    return 0;


}
&a[0]:0x61fee0,a[0]:0x61fee0, a+0:0x61fee0, *(a+0):0x61fee0, *(*(a+0)+1):e
*(a+0)+0:=0x61fee0,*(*(a+0)+0):h, a+0+0:0x61fee0
*(a+0)+1:=0x61fee1,*(*(a+0)+1):e, a+0+1:0x61fee6
*(a+0)+2:=0x61fee2,*(*(a+0)+2):l, a+0+2:0x61feec
*(a+0)+3:=0x61fee3,*(*(a+0)+3):l, a+0+3:0x61fef2
*(a+0)+4:=0x61fee4,*(*(a+0)+4):o, a+0+4:0x61fef8
*(a+0)+5:=0x61fee5,*(*(a+0)+5): , a+0+5:0x61fefe
&a[1]:0x61fee6,a[1]:0x61fee6, a+1:0x61fee6, *(a+1):0x61fee6, *(*(a+1)+1):o
*(a+1)+0:=0x61fee6,*(*(a+1)+0):w, a+1+0:0x61fee6
*(a+1)+1:=0x61fee7,*(*(a+1)+1):o, a+1+1:0x61feec
*(a+1)+2:=0x61fee8,*(*(a+1)+2):r, a+1+2:0x61fef2
*(a+1)+3:=0x61fee9,*(*(a+1)+3):l, a+1+3:0x61fef8
*(a+1)+4:=0x61feea,*(*(a+1)+4):d, a+1+4:0x61fefe
*(a+1)+5:=0x61feeb,*(*(a+1)+5): , a+1+5:0x61ff04
&a[2]:0x61feec,a[2]:0x61feec, a+2:0x61feec, *(a+2):0x61feec, *(*(a+2)+1):l
*(a+2)+0:=0x61feec,*(*(a+2)+0):c, a+2+0:0x61feec
*(a+2)+1:=0x61feed,*(*(a+2)+1):l, a+2+1:0x61fef2
*(a+2)+2:=0x61feee,*(*(a+2)+2):o, a+2+2:0x61fef8
*(a+2)+3:=0x61feef,*(*(a+2)+3):u, a+2+3:0x61fefe
*(a+2)+4:=0x61fef0,*(*(a+2)+4):d, a+2+4:0x61ff04
*(a+2)+5:=0x61fef1,*(*(a+2)+5): , a+2+5:0x61ff0a
&a[3]:0x61fef2,a[3]:0x61fef2, a+3:0x61fef2, *(a+3):0x61fef2, *(*(a+3)+1):u
*(a+3)+0:=0x61fef2,*(*(a+3)+0):s, a+3+0:0x61fef2
*(a+3)+1:=0x61fef3,*(*(a+3)+1):u, a+3+1:0x61fef8
*(a+3)+2:=0x61fef4,*(*(a+3)+2):n, a+3+2:0x61fefe
*(a+3)+3:=0x61fef5,*(*(a+3)+3):n, a+3+3:0x61ff04
*(a+3)+4:=0x61fef6,*(*(a+3)+4):y, a+3+4:0x61ff0a
*(a+3)+5:=0x61fef7,*(*(a+3)+5): , a+3+5:0x61ff10

a[i] 相当于 *(a+i)

a[i][j]相当于 *(*(a+i)+j)

7. 指针数组

可以通过声明一个一维指针数组,其中每个指针指向一个字符串来取得类似二位字符数组的效果.这种形式的声明如下:

char *pea[4];

这种数组必须用指向为字符串而分配内存的指针进行初始化,

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char *pea[4];
    int i = 0;
    for(i = 0; i < 4; i++)
    {
        pea[i] = malloc(6 * sizeof(char));
    }
    for(i = 0; i < 4; i++)
    {
        printf("pea[%d]:0x%x\n", i, pea[i]);
    }
    strcpy(pea[0], "hello");
    strcpy(pea[1], "world");
    strcpy(pea[2], "cloud");
    strcpy(pea[3], "sunny");
    for(i = 0; i < 4; i++)
    {
        printf("pea[%d]:0x%x,pea[%d]:%s, &pea[%d]:0x%x, *(pea[%d]):%c, *(*(pea+%d)):%c\n", i, pea[i], i, pea[i], i, &pea[i], i, *pea[i], i, *(*(pea + i)));

    }
    return 0;

}

 

pea[0]:0x6c0d28
pea[1]:0x6c0d48
pea[2]:0x6c0d58
pea[3]:0x6c0d68
pea[0]:0x6c0d28,pea[0]:hello, &pea[0]:0x61fefc, *(pea[0]):h, *(*(pea+0)):h
pea[1]:0x6c0d48,pea[1]:world, &pea[1]:0x61ff00, *(pea[1]):w, *(*(pea+1)):w
pea[2]:0x6c0d58,pea[2]:cloud, &pea[2]:0x61ff04, *(pea[2]):c, *(*(pea+2)):c
pea[3]:0x6c0d68,pea[3]:sunny, &pea[3]:0x61ff08, *(pea[3]):s, *(*(pea+3)):s

8 数组和指针时如何被编译器修改的

数组的数组会被改写为"数组的指针”,而不是"指针的指针"

实参所匹配的形式参数
数组的数组

char  c[8][10];

char (*)[10];数组指针
指针数组char *c[15];char **c;指针的指针
数组指针(行指针)char (*c)[64];char (*c)[64];不改变
指针的指针char **c;char **c;不改变

你之所以能在main()函数中看到char **argv这样的参数,是因为argv是个指针数组(即char *argv[])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值