• c 语言中的数组和指针


    1 如何把数组初始化成全 0

    声明的局部变量数组,如果没有初始化,那么数组保存在栈上,数组的内容是不确定的。

    局部变量不像全局变量,全局变量如果没初始化,那么默认是全 0。

    将数组初始化为 0 的方式主要有以下 3 种,本人在开发过程中习惯于使用第一种,即使用 {0} 将数组初始化为全 0。

    (1)数组声明的时候使用 {0} 初始化

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. int a[10] = {0};
    7. int b[10][10] = {0};
    8. for (int i = 0; i < 10; i++) {
    9. printf("a[%d] = %d\n", i, a[i]);
    10. }
    11. for (int i = 0; i < 10; i++) {
    12. for (int j = 0; j < 10; j++) {
    13. printf("b[%d][%d] = %d\n", i, j, b[i][j]);
    14. }
    15. }
    16. return 0;
    17. }

     (2)memset()

    使用 memset() 将数组设置成 0。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. int a[10] = {0};
    7. int b[10][10] = {0};
    8. for (int i = 0; i < 10; i++) {
    9. printf("a[%d] = %d\n", i, a[i]);
    10. }
    11. for (int i = 0; i < 10; i++) {
    12. for (int j = 0; j < 10; j++) {
    13. printf("b[%d][%d] = %d\n", i, j, b[i][j]);
    14. }
    15. }
    16. return 0;
    17. }

    (3)使用 for 循环将数组元组逐个设置为 0

    2 指针加减运算

    指针的加减运算移动的单位是指针指向的数据的大小。

    而不是把指针看做一个整型数,在这个数的基础上加减。

     2.1 一级指针

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct test {
    6. int a;
    7. char name[10];
    8. long data;
    9. };
    10. int main() {
    11. char c;
    12. char *pc = &c;
    13. int a = 0;
    14. int *pa = &a;
    15. struct test t;
    16. struct test *pt = &t;
    17. printf("pc: %p, pc + 1: %p\n", pc, pc + 1);
    18. printf("pa: %p, pa + 1: %p\n", pa, pa + 1);
    19. printf("pt: %p, pt + 1: %p\n", pt, pt + 1);
    20. printf("sizeof(struct test): %d\n", sizeof(struct test));
    21. printf("&t: %p, &t.a: %p, &t.name: %p, &t.data: %p\n", &t, &t.a, &t.name, &t.data);
    22. return 0;
    23. }

    pc 指向的数据类型是 char 类型,所以 pc + 1,地址移动 1 个字节。

    pa 指向的数据类型是 int 类型,所以 pa + 1,地址移动了 4 个字节。

    pt 指向的数据类型是 struct test 类型,所以 pt + 1,地址移动了 24 个字节。

    结构体的大小并不是所有结构体成员大小的和,因为结构体中的成员需要对齐。

    怎么才算对齐 ? 成员地址 % 成员大小 等于 0

    从上边的打印可以看出来,成员 a 的地址是 0x7ffd20d18350 对 4 可以整除;

    成员 name 是一个数组,不是以数组为整体进行对齐,而是以数组中的元素进行对齐;

    name 最后一个元素的地址是 0x7ffd20d1835e,但是 data 却不能保存到 0x7ffd20d1835f 处,因为这个地址对 data 的长度不能整除,所以 data 保存在了 0x7ffd20d18360 处。

    指针的减法,得到的是两个指针之间相隔的元素的个数。

    1. #include
    2. #include
    3. #include
    4. int main() {
    5. int a[10] = {0};
    6. int *pa = a;
    7. int *pa5 = a +5;
    8. printf("pa5 - pa: %d\n", pa5 - pa);
    9. return 0;
    10. }

    2.2 二级指针

    二级指针指向的数据类型是一级指针,所以二级指针加减运算的时候移动的单位是一个一级指针的大小。

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct test {
    6. int a;
    7. char name[17];
    8. long data;
    9. };
    10. int main() {
    11. char c;
    12. char *pc = &c;
    13. char **ppc = &pc;
    14. int a = 0;
    15. int *pa = &a;
    16. int **ppa = &pa;
    17. struct test t;
    18. struct test *pt = &t;
    19. struct test **ppt = &pt;
    20. printf("ppc: %p, ppc + 1: %p\n", ppc, ppc + 1);
    21. printf("ppa: %p, ppa + 1: %p\n", ppa, ppa + 1);
    22. printf("ppt: %p, ppt + 1: %p\n", ppt, ppt + 1);
    23. printf("sizeof(struct test): %d\n", sizeof(struct test));
    24. printf("&t: %p, &t.a: %p, &t.name: %p, &t.data: %p\n", &t, &t.a, &t.name, &t.data);
    25. return 0;
    26. }

    3 数组加减运算

    3.1 数组 sizeof()

    数组名其实是一个指针,一维数组的数组名是一个一级指针 int *,二维数组的数组名是一个二级指针 int **。但是在使用 sizeof() 计算数组的大小的时候,并不是返回的一个指针的大小,而是返回的数组占用的空间。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. int a[10] = {0};
    7. int b[10][10] = {0};
    8. int *pa = a;
    9. int **pb = b;
    10. printf("sizeof(a): %d, sizeof(&a): %d, sizeof(pa): %d\n", sizeof(a), sizeof(&a), sizeof(pa));
    11. printf("sizeof(b): %d, sizeof(&b): %d, sizeof(pb): %d\n", sizeof(b), sizeof(&b), sizeof(pb));
    12. return 0;
    13. }

    3.2 一维数组加减运算

    一维数组的数组名是一级指针 int *,使用数组名做加减运算,移动的单位是数组元素的长度。

    一维数组的数组名取地址,然后做加减运算,移动的单位是整个数组的大小。

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    7. printf("a: %p, a + 1: %p\n", a, a + 1);
    8. printf("&a: %p, &a + 1: %p\n", &a, &a + 1);
    9. printf("*a: %d\n", *a);
    10. return 0;
    11. }

    使用数组名做运算,a 和 a + 1 相差 4 个字节,即一个数组元素的长度。

    使用数组名取地址做运算, &a 和 &a + 1相差整个数组的大小,4 * 10 = 40,即 0x28。

    数组的加减运算有自己的特殊之处,并不能完全按照指针的加减运算来使用。

    如果按照指针的加减运算,那么 &a + 1 偏移的长度应该是一个 int * 的长度,在 64 为机器上是 8。

    3.3 二维数组加减运算

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. int a[4][4] = {{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}, {31, 32, 33, 34}};
    7. printf("a: %p, a + 1: %p\n", a, a + 1);
    8. printf("*a: %p, *a + 1: %p\n", *a, *a + 1);
    9. printf("&a: %p, &a + 1: %p\n", &a, &a + 1);
    10. return 0;
    11. }

    二维数组的数组名是一个二级指针 int **, 加减运算移动的单位是一行的大小,这个例子中行和列都是 4,所以移动单位是 16。

    二维数组名使用 * 取值之后就是一级指针,指向某一行,而指向某一行的指针和一维数组的数组名是类似的,移动的单位是一个元素的长度。

    二维数组的数组名取地址再做加减运算,和一维数组是类似的,移动的单位是整个数组的大小。

    4 字符数组

    1. #include
    2. #include
    3. #include
    4. int main() {
    5. char *a = "hello";
    6. char b[] = "hello";
    7. printf("sizeof(a): %d, strlen(a): %d\n", sizeof(a), strlen(a));
    8. printf("sizeof(b): %d, strlen(b): %d\n", sizeof(b), strlen(b));
    9. printf("\n");
    10. printf("a: %p, a + 1: %p\n", a, a + 1);
    11. printf("b: %p, b + 1: %p\n", b, b + 1);
    12. return 0;
    13. }

    a 是一个 char * 指针,b 是一个 char 数组的数组名,两者还是有区别的。

    sizeof(a) 是一个指针的大小,在 64 位系统中,长度是 8。

    sizeof(b) 是一个数组的大小,长度是 6,包括字符串最后的结束符 '\0'。

    strlen(a) 和 strlen(b) 是相同的,均是字符串的长度。使用 strlen() 获得的字符串的长度是不包括最后的字符串结束符 '\0' 的。

     

    5 指针数组,数组指针

    指针数组只的是一个数组,数组中的元素都是指针。

    数组指针,可以叫数组的指针,是指一个指针,指向一个数组。

    如下代码中,pa 是一个指针数组,数组长度是 2,可以保存两个指针。

    ap 是数组指针,指向了数组 b。

    1. #include
    2. #include
    3. #include
    4. int main() {
    5. int a[4] = {1, 2, 3, 4};
    6. int b[5] = {5, 6, 7, 8, 9};
    7. int *pa[2] = {a, b};
    8. int *ap = b;
    9. for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
    10. printf("a[%d] = %d\n", i, pa[0][i]);
    11. }
    12. for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++) {
    13. printf("b[%d] = %d\n", i, ap[i]);
    14. }
    15. return 0;
    16. }

    如下代码需要深入理解指针数组与数组指针的概念,记录如下。

    1. #include
    2. #include
    3. #include
    4. int main() {
    5. char *str[] = {"Welcome", "to", "Fortemedia", "Nanjing"};
    6. for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
    7. printf("str[%d] = %p\n", i, str[i]);
    8. }
    9. char **p = str + 1;
    10. printf("1111, *p: %s\n", *p); // to
    11. p++; // p 指向 Fortemedia
    12. printf("2222, *p: %s\n", *p);
    13. p--; // p 指向 to
    14. str[0] = (*p++) + 2; // p++ --> p 指向 Fortemedia, + 2 --> 超出数组范围
    15. printf("3333, *p: %s\n", *p);
    16. str[1] = *(p + 1); // p + 1 --> p + 1 指向 Nanjing,p 不变
    17. printf("4444, *p: %s\n", *p);
    18. str[2] = p[1] + 3; // p[1] --> Nanjing + 3 --> jing,因为 *p 的值也保存在 str[2] 这个位置,所以 p 也改变了
    19. printf("p[0]: %s, *p: %s, str[2]: %s, str[1]: %s\n", p[0], *p, str[2], str[1]);
    20. str[3] = p[0] + (str[2] - str[1]); // p[0] --> Fortemedia, str[2] jing, str[1] Nanjing,str[2] - str[1] = 3,p[0] + 3 指向 Nanjing 的最后一个字符 g
    21. printf("%s\n", str[0]);
    22. printf("%s\n", str[1]);
    23. printf("%s\n", str[2]);
    24. printf("%s\n", str[3]);
    25. return 0;
    26. }

  • 相关阅读:
    Vue生命周期
    TypeError: Object of type bool_ is not JSON serializable(PKL转json)
    十五章:Java反射机制
    Python 数据库应用教程:安装 MySQL 及使用 MySQL Connector
    【锁的区别】C++线程库和POSIX线程库锁的区别
    安全带佩戴识别高空作业
    【附源码】Python计算机毕业设计数据时代下的疫情管理系统
    【vue导入导出Excel】vue简单实现导出和导入复杂表头excel表格功能【纯前端版本和配合后端版本】
    机械设计基础总复习
    go多样化定时任务通用实现与封装
  • 原文地址:https://blog.csdn.net/weixin_38184628/article/details/136145751