图片 8

内部存款和储蓄器地址,指针和数组

By admin in 编程 on 2019年8月27日

娓娓道来c指针 (3)指针和数组

(3)指针和数组

在c中指针和数组似乎有着千丝万缕的关系。其实它们不是一回事:指针是指针,数组是数组,两者不相同。

说它们有关系,不过是因为常见这样的代码:

int main()
{
 int array[] = {1,2,3,4,5};
 int n = sizeof(array) / sizeof(int);
 int *p = array;
 int i;
 for (i = 0; i < n; i++)
  printf("p[%d]...%d\n", i, p[i]);
 system("pause");
 return 0;
}

运行

图片 1

在上面的代码中,指针和下标运算符的结合使用,给人一种指针和数组一样的感觉。

本质是:数组名是一个指向数组起始元素的常量指针。这也是数组和指针的唯一联系!

之所以可以使用 p[i] 来访问数组中的元素,是因为在编译器中 p[i]
被解释为
*(p+i),这仍然是指针的功能。对编译器而言,用p[i]表达*(p+i)的含义是没有意义的,它只是为了让人看着舒服,用着方便。这是语法糖<
喎?” target=”_blank”
class=”keylink”>vc3Ryb25nPqO6PC9wPgo8cD4gICAgcFtpXcrHKihwJiM0MztpKbXEvPK1pdC0t6ijrMq1vMrJz6Os1sHJ2bbU09qx4NLrxvfAtMu1o6xbXdXi0fm1xNTLy+O3+83qyKu/ydLUsru05tTaoaM8L3A+CjxwPiAgICC/ycrHttTT2sjLwODAtMu1o6wqKHAmIzQzO2kptcTQtLeo1Nq94rbByc+xyL3PwKfE0aOs0LTG8MC00rLC6bezKLz8yOvBv7TzKaGj0vK0y6OsY9Pv0dTS/cjrW13Uy8vjt/uhozwvcD4KPHA+ICAgIL7Nz/HV4tH5o6zV4tCpvfa99srHzqrBy8jDyMvA4Mjd0tfA7b3itvjS/cjrtcS5psTco6y1xMi3v8nS1MjDztLDx7jQyty1vbHgs8zT79HUtcTM8MPbzra1wCjI3dLX18XK1imjrNPQyrHO0sPHs8bV4tCpuabE3M6q0++3qMzHKHN5bnRheCBzdWdhciC78tXfIHN5bnRhY3RpYyBzdWdhcimhozwvcD4KPHA+0tTJz9Wq19ShttX3t/5j1rjV66G3o6y96LTLzca89tXisb7K6aGjyunW0NK71eu8+9GqtdjWuLP2o7rWu9PQ1NrJ+cP30+++5NbQo6xbXbLFse2078r91+m1xLqs0uWjrNTase2078q91tCjrFtd0+vK/dfpzt652KOhPC9wPgo8cD7X3L3hxvDAtL7NysejrL+0JiMyMDI4NDvK/dfptcTTw7eoo7pwW2ldo6zG5Mq1yscqKHAmIzQzO2kptcTT77eozMejrHDI1Mi7ysfWuNXro6zT68r91+myos7eudjPtaGjPC9wPgo8cD48YnI+CjwvcD4KPHA+1rjV67rNyv3X6bXEsrvNrNautKajrLu5v8nS1LTTz8LD5rXEwP3X07+0s/Y8L3A+CjxwPjwvcD4KPHByZSBjbGFzcz0=”brush:java;”>void
fun(int *array) { printf(” sizeof(array)…%d\n”, sizeof(array)); }
int main() { int array[] = { 1, 2, 3, 4, 5 }; printf(”
sizeof(array)…%d\n”, sizeof(array)); fun(array); return 0; }运行

图片 2

从运行结果看,指针和数组确实不同。这揭示了c语言中传递数组时的规则:传递过去的是地址,是指向数组起始元素的地址。之所以这样,基于两点;

从效率上考虑,若是把整个数组赋值过去,太耗时,也耗空间。还不如传地址过去,使用同一份内容。在c语言设计之初,赋值操作就仅限于基本类型(char、int、float……),而数组是聚合类型。
这给我们的编程启示是:传递数组时,不要忘了把数组大小也传递过去。否则,函数那边由于不知道数组大小,极易越界。应这样设计函数
void fun(int *array, int n),n是数组大小。
还有一点需要指出,即使函数被设计成void fun(int array[],
int),array依然被看成是指针。也就是说即使数组带了长度,如 int
array[5],该长度也会被编译器忽略掉。一句话:形参中的数组统统看成指针。
总结:只有在函数形参中,仅有这一种情况,声明的数组,如 int
array[]会被看作是指针。其它情况下,指针与数组并无联系。
还有一点,对于 int
array[5];array表示指向数组起始元素的指针,那么&array又是什么呢?实验下:

int main()
{
 int array[] = { 1, 2};
 printf("   array...%p\n", array);
 printf("  &array...%p\n", &array);
 printf("&array+1...%p\n", &array+1);
 return 0;
}

运行 图片 3
分析实验结果:0031FCEC与0031FCE4相差8,而sizeof(array)就是8。
结论就是:array和&array都是指针,但类型不同。array的类型是int*,而&array的类型是int(*)[2]。array是指向普通int类型的指针;&array是数组指针,该数组元素是int类型的,且数组大小是2。
至于array和&array两者的值是一样的,应该很好理解。

那么为什么int(*)[2]表示的是数组指针呢?这需要透彻理解c的声明语法,后序讲解。

(3)指针和数组 (3)指针和数组
在c中指针和数组乎有着千丝万缕的关系。其实它们不是一回事:指针是指针,数组是数组,两…

在c中指针和数组似乎有着千丝万缕的关系。其实它们不是一回事:指针是指针,数组是数组,两者不相同。

C语言学习入门 (三) sizeof 函数、内存地址、数组

sizeof可以用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。

sizeof(10);

char c = ‘a’;

sizeof(c);

sizeof(float);

地址

1.计算机中的内存是以字节为单位的存储空间。内存的每一个字节都有一个唯一的编号,这个编号就称为地址。

凡存放在内存中的程序和数据都有一个地址,也就是说,一个函数也有自己的内存地址

2.
当定义一个变量时,系统就分配一个带有唯一地址的存储单元来存储这个变量。比如:

char a = ‘A’;// A的ASCII值为65

int b[] = {66,33};

在16bit编译器环境下,系统为a、b分别分配1个字节、2个字节的存储单元。变量存储单元的第一个字节的地址就是该变量的地址。

图片 4

可以看出,变量a的地址是ffc3;变量b的地址是ffc1。内存中存储的都是2进制数据

一维数组< 喎?” target=”_blank”
class=”keylink”>vc3Ryb25nPjwvcD4KPHA+Crao0uW1xNDOyr3OqqO6PHN0cm9uZz7A4NDNICDK/dfpw/tb1KrL2Lj2yv1dPC9zdHJvbmc+PC9wPgo8cD4KaW50IGFbNV07IDwvcD4KPHA+CmludCBbXSBhOyDV4tH50LTKx7TttcQgsrvNrNPaamF2YdC01Nqx5MG/w/vHsLrztrzQ0CAgx9K2qNLlyrHQ6NKq1ri2qNSqy9i49sr9PC9wPgo8cD4Ks/XKvLuvo7o8L3A+CjxwPgppbnQgYVsyXSA9IHs4LDEwfTs8L3A+CjxwPgrG5Mq1z+C1sdPao7o8L3A+CjxwPgppbnQgYVsyXTs8L3A+CjxwPgphWzBdID0gODs8L3A+CjxwPgphWzFdID0gMTA7PC9wPgo8cD4K1KrL2CYjMjA1NDA7wdCx7b/J0tTKx8r91+nL+dPQ1KrL2LXEs/UmIzIwNTQwO6Os0rK/ydLUysfHsMPmsr+31tSqy9i1xLP1JiMyMDU0MDs8L3A+CjxwPgppbnQgYVs0XSA9IHsyLDV9OyAg1eLR+b7NuLMmIzIwNTQwO7j4YVswXT0yO2FbMV09NTsgCiBhWzJdPWFbM109xKzIzyYjMjA1NDA7MDwvcD4KPHA+Cjxicj4KPC9wPgo8cD4KtbG2qNLlsqLWsb3Ts/XKvLuvyrEgv8nS1LK71ri2qNSqy9i49sr9PC9wPgo8cD4KaW50IGFbXSA9IHsyLDMsNX07PC9wPgo8cD4Kyv3X6bP1yry7r8qxtcS4syYjMjA1NDA7t73Kvda7xNzTw9Payv3X6bXEtqjS5aOstqjS5dauuvPWu8Tc0ru49tSqy9jSu7j21KrL2LXYuLMmIzIwNTQwOzwvcD4KPHA+CmludCBhWzNdOzwvcD4KPHA+CmEgPSB7MSwyLDN9Oy8vtO3O86GjIAog0vLOqiDK/dfpw/thILT6se2x5MG/tcS12Na3o6zKx7j2s6PBv6OsuPizo8G/uLMmIzIwNTQwO77NtO3ByzwvcD4KPHA+Cjxicj4KPC9wPgo8cD4KtbHK/dfp1/fOqsq1ss7KsaOs0vLOqrSrtd21xMrH0ru49rXY1rejrMv50tTQzrLOuMSx5KOsyrWyztKyu+G4xLHkPC9wPgo8cD4KPHN0cm9uZz62/s6syv3X6Twvc3Ryb25nPjwvcD4KPHA+Crao0uXQzsq9o7o8c3Ryb25nPsDg0M0gIMr91+nD+1vQ0Mr9XVvB0Mr9XTwvc3Ryb25nPjwvcD4KPHA+CmludCBhWzJdWzNdOy8vubIy0NAzwdCjrDa49tSqy9ijrAogvLQgwb249rD8uqzI/bj21KrL2LXE0rvOrMr91+k8L3A+CjxwPgo8YnI+CjwvcD4KPHA+CtTaxNq05tbQtcS05rSix+m/9qOswP3I52ludCBhWzJdWzJdPC9wPgo8aW1nIHNyYz0=””
width=”400″ height=”150″ alt=”\”>

(注意:a[0]、a[1]也是数组,是一维数组,而且a[0]、a[1]就是数组名,因此a[0]、a[1]就代表着这个一维数组的地址)

1> 数组a的地址是ffc1,数组a[0]的地址也是ffc1,即a = a[0];

2>
元素a[0][0]的地址是ffc1,所以数组a[0]的地址和元素a[0][0]的地址相同,即a[0]
= &a[0][0];

3> 最终可以得出结论:a = a[0] =
&a[0][0],以此类推,可以得出a[1] = &a[1][0]

printf(“%p\n%p\n%p”, a, a[0], &a[0][0]);

二维数组的初始化

* 按行进行初始化

int a[2][3] = { {2,2,3}, {3,4,5} };

* 按存储顺序进行初始化(先存放第1行,再存放第2行)

int a[2][3] = {2,2,3,3,4,5};

* 对部分元素进行初始化

int a[2][3] = { {2}, {3,4} };

int b[3][3] = { { }, { , ,2}, {1,2,3}};

* 如果只初始化了部分元素,可以省略行数,但是不可以省略列数

int a[][3] = {1,2,3,4,5,6};

int a[][3] = {{1,2,3}, {3,5}, {}};

为什么不能只省略列数呢? 因为int a[2][]={1,2,3,4,5,6}
这样无法确定元素在哪一行。

(三) sizeof 函数、内存地址、数组
sizeof 可以用来计算一个变量或者一个常量、一种数据类型所占的内存字节数。
sizeof(10);…

说它们有关系,不过是因为常见这样的代码:

int main()
{
    int array[]= {1,2,3,4,5};
    int n=sizeof(array)/sizeof(int);
    int *p=array;
    int i;
    for(int i=0; i<n; i++)
    {
        printf("p[%d]...%d\n",i,p[i]);
        printf("*(p+%d)...%d\n",i,*(p+i));
        printf("------------------------------\n");
    }

    system("pause");
    return 0;
}

图片 5

在上面的代码中,指针和下标运算符的结合使用,给人一种指针和数组是一样的感觉。

本质是:数组名是一个指向数组起始元素的常量指针。这也是数组和指针的唯一联系!

之所以可以使用 p[i] 来访问数组中的元素,是因为在编译器中 p[i]
被解释为
*(p+i),这仍然是指针的功能。对编译器而言,用p[i]表达*(p+i)的含义是没有意义的,它只是为了让人看着舒服,用着方便。这是语法糖: p[i]是*(p+i)的简单写法,实际上,至少对于编译器来说,[]这样的运算符完全可以不存在。可是对于人类来说,*(p+i)的写法在解读上比较困难,写起来也麻烦(键入量大)。因此,c语言引入[]运算符。 就像这样,这些仅仅是为了让人类容易理解而引入的功能,的确可以让我们感受到编程语言的甜蜜味道(容易着手),有时我们称这些功能为语法糖(syntax
sugar 或者 syntactic
sugar)。以上摘自《征服c指针》,借此推荐这本书。书中一针见血地指出:只有在声明语句中,[]才表达数组的含义,在表达式中,[]与数组无关!总结起来就是,看似数组的用法:p[i],其实是*(p+i)的语法糖,p仍然是指针,与数组并无关系。

指针和数组的不同之处,还可以从下面的例子看出

void fun(int array[5])
{
    printf("  sizeof(array)...%d\n", sizeof(array));
}
int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    printf("  sizeof(array)...%d\n", sizeof(array));
    fun(array);
    return 0;
}

图片 6

从运行结果看,函数形参虽然用数组的方式进行了声明,但仍然被当做指针。这揭示了c语言中传递数组时的规则:传递过去的是地址,是指向数组起始元素的地址。之所以这样,基于两点;

从效率上考虑,若是把整个数组赋值过去,太耗时,也耗空间。还不如传地址过去,使用同一份内容。
在c语言设计之初,赋值操作就仅限于基本类型(char、int、float……),而数组是聚合类型。
这给我们的编程启示是:传递数组时,不要忘了把数组大小也传递过去。否则,函数那边由于不知道数组大小,极易越界。应这样设计函数
void fun(int *array, int n),n是数组大小。
还有一点需要指出,即使函数被设计成void fun(int array[5], int
n),array依然被看成是指针。也就是说即使数组带了长度,该长度也会被编译器忽略掉。一句话:形参中的数组统统看成指针。
既然如此,还不如直接写成void
fun(int *array, int n)。指针的形式,更能表达本意。

再思考:如果p[i]是*(p+i)的意思,由于加法具有交换律:p+i=i+p,那么i[p]同样可以表达p[i]的意思,是这样的吗?实验验证:

int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    int n = sizeof(array) / sizeof(int);
    int *p = array;
    int i;
    for (i = 0; i < n; i++)
        printf("%d[p]...%d\n", i, i[p]);
    return 0;
}

图片 7

实验证明,我们的猜想是正确的:p[i]确实是*(p+i)的语法糖。i[p]这样的写法是否很逆天呢!

总结:只有在函数形参中,仅有这一种情况,声明的数组,如 int
array[]会被看作是指针。其它情况下,指针与数组并无联系。

&array的含义

还有一点,对于 int
array[5];array表示指向数组起始元素的指针,那么&array又是什么呢?实验下

int main()
{
    int array[] = { 1, 2};
    printf("   array...%p\n", array);
    printf("  &array...%p\n", &array);
    printf("&array+1...%p\n", &array+1);
    return 0;
}

图片 8

结论就是:array和&array都是指针,但类型不同。array的类型是int*,而&array的类型是int(*)[2]。array是指向普通int类型的指针;&array是数组指针,该数组元素是int类型的,且数组大小是2。

至于array和&array两者的值是一样的,应该很好理解。

 

补充

标量(scalar):简单讲,标量就是指char、int、double和枚举类型等数值类型,再加上指针。

至于数组、结构体和共用体这样将多个标量进行组合的类型,我们称之为聚合类型(aggregate)。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 澳门新葡亰官网app 版权所有