P131. 指针-3-数组和指针
说明:
本篇记录的是自学C语言的相关过程记录,参考资料是B站郝斌老师的C语言自学教程。本人之前学过一点python,但是觉得C语言还是有必要学一下的。因为刚开始学C语言,所以本篇文章会不断更新。
因为是学习记录,所以条理可能并不是很清晰,后续感觉学的差不多的时候会重新整理一下。
指针和一维数组
一维数组名:
一维数组名是个指针常量,它存放的是一维数组第一个元素的地址。
# include <stdio.h>
int main(void)
{
int a[5]; // a 是数组名,5是数组元素的个数,元素就是变量 a[0] -- a[4]
printf("%#X\n", &a[0]);
printf("%#X\n", a);
// 一维数组a的地址和第一个元素a[0]的地址是一样的。
return 0;
}
下标和指针的关系:
如果
p
是一个指针变量,则p[i]
永远等价于*(p+i)
# include <stdio.h>
int main(void)
{
/*
数组和下标的关系:
*/
// 未初始化:
int a[5]; // 未初始化的数组a,包含5个元素,默认是垃圾值。
printf("%d\n", a[0]); // 结果正是垃圾值。
// 不完全初始化:
int b[5] = {1,2,3}; // 不完全初始化时,未赋值的元素默认为0
printf("%d, %d\n", b[1], b[4]); // 输出为 2, 0
// 完全初始化:
int c[5] = {1,2,3,4,5};
printf("%d\n", c); // 一维数组 c 的地址
printf("%d\n", &c[0]); // 一维数组 c 的第一个元素的地址
// 数组与下标的关系:p[i] 永远等价于 *(p+i)
printf("%d\n", *c); // 数组 c 存放的是 c[0] 的地址,所以 c 指向 c[0],所以 *c 就等同于 c[0],也就是 *c 的值为 1
printf("%d\n", *(c+2)); // *(c+2) 存放的是 c[2] 的地址,(c+2)指向c[2],所以 *(c+2)等同于 c[2],也就是3
// 由 c[0] 到 c[2],内存地址移动了2个单元,因为元素是int类型,也就是2*4=8个字节,内存地址向后移动8个字节。
printf("%d\n", &c[0]); // 1941961344
printf("%d\n", &c[2]); // 1941961352 二者差值正好是8
return 0;
}
如果一个函数需要处理一个一维数组,那么需要接收该数组的那些信息?(后者说,如果要确定一个数组,那么需要哪些信息?)
答:需要两个参数(数组的首地址,数组长度)
# include <stdio.h> /* 利用函数处理数组,需要2个信息:数组名称和数组长度。 详情如下所示: */ // 定义一个函数,读取一个一维数组的每个元素以及相应的地址。 void f(int * pArr, int len) { // pArr 是一维数组名称,存放的是第一个元素的位置,pArr指向第一个元素,所以 *pArr就是数组的第一个元素。 // len 是数组的长度。 printf("%d\n", *pArr); // 输出值应该和数组的第一个元素一样 printf("%d\n", pArr); // 输出的地址也应该和数组的第一个元素一样 // 根据第一个元素的地址以及一维数组的长度,遍历数组中的每一个元素及其地址 int i; // i 是地址增量 for (i=0; i<len; ++i) printf("element = %d, index = %d, address = %d, %d, %d\n", *(pArr+i), i, pArr+i, &pArr[i], &*(pArr+i)); /* 这里的 pArr 是 int * 类型, i 是 int类型。所以 pArr + i 就表示从pArr的地址向后移动 i 个单元(int类型,也就是 i*4个字节). */ } int main(void) { int a[5] = {1,2,3,4,5}; int b[6] = {-1,-2,-3,-4,-5,-6}; int c[100] = {1, 99, 22, 33}; // 一维数组名是一个指针常量,它存放的是数组中第一个元素的地址。 // 也就是说 数组 a 的类型是 int * 所以函数的数组名称也得是 int * 类型。 f(a, 5); printf("%d\n", a[0]); printf("%d\n", &a[0]); return 0; }
根据上述代码可以学到:
**1. pArr+i,&pArr[i] 和 &*(pArr+i) 都是数组中第i+1个元素的地址; **
2. 虽然 pArr 是 int * 类型, i 是 int 类型,但是二者可以相加,也就是说此处的 pArr+i 表示内存地址向后移动 i 个单元,由于 i 是 int 类型,所以实际内存地址在数值上 + 4*i
指针变量的运算:
- 指针变量不能加、乘、除,只能相减,就是说两个指针之间只能做减法。
- 两个指针相减也是有要求的:当两个指针变量指向的是同一块连续空间中的不同存储单元,二者才可以相减(二者之间的差值是元素个数之差,不是字节之差)。
# include <stdio.h> int main(void) { int a[5]; int * p; int * q; p = &a[1]; q = &a[4]; printf("p address = %d\n", p); printf("q address = %d\n", q); printf("q - p adderss = %d\n", q-p); return 0; }
一个指针变量占几个字节?
答:32位操作系统占4个字节,64位操作系统占8个字节。
一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占4(32位)或8(64位)个字节。
sizeof(数据类型)
,返回的是该数据类型所占的字节数。
sizeof(变量名称)
,返回的是该变量所占的字节数。例如:
sizeof(int) = 4
,sizeof(char) = 1
,sizeof(double) = 8
# include <stdio.h> int main(void) { char ch = 'A'; int i = 99; double x = 66.6; char * p = &ch; int * q = &i; double * r = &x; printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r)); return 0; } /* 郝斌老师课程中,输出是 4 4 4 自己电脑中输出的是 8 8 8 */
至于原因,也可以理解(以代码中的
x
和*r
为例):
首先要理解指针变量存储的是内存地址,代码中指针变量
r
存放的是普通变量x
的内存地址(r
指向x
),而且x
有8个字节。在内存中,一个字节有一个地址编号,所以
x
有8个字节,也就有8个地址编号。指针变量
r
存放的是x
的内存地址,更确切地说,存放的是x
中第一个字节对应的地址编号。那么此时就有一个问题:指针变量
r
只存放了x
中的第一个字节对应的地址编号,但是r
又指向x
,那么r
是怎么仅仅通过第一个字节的地址编号来确定整个x
的?答:
r
通过数据类型来确定长度(上述代码中x
的类型是double
,也就是说长度是8),由此来确定该连续空间中的后面7个字节的地址。至于为什么此处的输出和老师课程上的输出不一致,原因也很简单:
- 郝斌老师那个时候电脑中CPU的控制线有32条,所以可以控制32位,也就对应这4个字节。
- 现在电脑中CPU控制线有64条,所以也就可以控制64位,对应8个字节(因为暂时没有学计算机组成原理,所以此处推测是64位。后续学完计组之后再来加以确认)。