S1E25:void指针和NULL指针 课后作业

测试题:
0. 你猜下边代码会打印多少?

#include <stdio.h>

int main()
{
        void a;

        printf("%d\n", sizeof(a));

        return 0;
}

答:4(错误

答案:会报错!那 void 既然是无类型,我们就不应该用它来定义一个变量,如果你一定要这么做,那么程序就会给你报错(我只是换个提问方式,看看多少童鞋会上当)。
不信你看:

1. 那 sizeof(void *) 呢?

答:4

答案:如果你回答是 4 个字节或 8 个字节,那么本题不能算你答对。因为指针的尺寸是与编译器的目标平台相关的。比如目标平台是 32 位的,那么 sizeof(void*) 就是 4,如果是 64 位的,那么 sizeof(void *) 就是 8,如果是 16 位的,那么就是 2 啦。

2. 如何有效地避免出现悬空指针?
注:悬空指针就是指向了不确定的内存区域的指针,通常对这种指针进行操作会使程序发生不可预知的错误。

答:指针在定义的时候,赋值地址或者NULL

答案:当你的指针不知道指向哪儿的时候,那么将它指向 NULL,以后就不会有太多的麻烦。比如定义一个指针变量的时候,你可以把它初始化为 NULL,这样至少可以确保它不是一个垂悬指针。

3. 对 NULL 指针进行解引用,结果是什么?

答:‘\0’(错误

答案:报错。无论什么操作系统,对空指针进行解引用都是非法的。

4. 请问下边定义有没有问题?

……
int *p = void *0;
……

答:有问题,int *类型的赋值的得是int *的

答案:报错。
NULL 的宏定义是

#define NULL ((void *)0)

这里是将 0 强制转换成 void 指针,所以要这么写才能通过编译:

int *p = (void *)0;

5. 请问下边代码会打印什么?

#include <stdio.h>

int main()
{
        int array[5] = {1, 2, 3, 4, 5};
        int *pi = &array[2];
        void *pv;

        pv = pi;
        pv++;
        pi = pv;

        printf("%d\n", *pi);

        return 0;
}

答:4(错误

答案:建议回答“4”的童鞋自己打一次代码……,
先晒下结果:

为何如此邪乎?
我们把代码修改如下,你或许就会知道答案:

#include <stdio.h>

int main()
{
        int array[5] = {1, 2, 3, 4, 5};
        int *pi = &array[2];
        void *pv;

        pv = pi;
        printf("%p, %p\n", pv, pi);

        pv++;
        pi = pv;
        printf("%p, %p\n", pv, pi);

        printf("%d\n", *pi);

        return 0;
}

程序实现如下:

猜到了吧?
由于 pv 是 void 类型指针,所以编译器并不知道其“跨度”是多少,因此 pv++ 只是简单的将地址加 1。
那么地址不正确,打印出来的值肯定就是错误的!
那打印出来的是“乱码”吗?
不是!如果没有猜错,你在电脑上得到的结果应该跟小甲鱼是一样的。
下边解释需要你懂得小端和大端的原理
pi 指向的是数组的第三个元素(即 array[2],其值为 3)
那么 array[2] 和 array[3] 在内存中的存放形式(小端)应该如下:


pv = pi 使得 pv 也是指向 0xbf93b10c 地址。
pv++ 刚才我们解释了,编译器只知道机械将其地址加 1,即 pv 现在指向的是 0xbf93b10d 地址。
pi = pv 使得 pi 被 pv 带上了歪路…… 
以整型的跨度打印 pi,那么编译器会从 pi 指向的地址开始(0xbf93b10d),找到四个字节的数据(0x00000004),并将它们打印出来。
由于是小端,所以 0x00000004 对应的人类可读十六进制就是 0x04000000,转换为十进制就是大家所看到的 67108864。

动动手:
0. 这道题是第 20 讲课后作业 动动手第 1 题的升级版,这一次我们允许用于自定义矩阵的尺寸和数据。
要求一:允许用户自定义输入两个矩阵的尺寸和数据
要求二:输出格式如下

要求三:由于我们还没有教动态内存分配,所以不允许使用 malloc 函数。但你可以使用 VLA 变长数组

答:忘记矩阵知识了(错误

答案:

#include <stdio.h>

int main()
{
        int m, p, n;
        int i, j, k, row;

        // 定义第一个矩阵
        printf("请输入第一个矩阵的尺寸(M * P):");
        scanf("%d * %d", &m, &p);
        int matrix_in_1[m][p];

        // 定义第二个矩阵
        printf("请输入第一个矩阵的尺寸(P * N):");
        scanf("%d * %d", &p, &n);
        int matrix_in_2[p][n];

        // 初始化存放乘积的二维数组
        // VAL数组不支持直接初始化操作
        int matrix_out[m][n];
        for (i = 0; i < m; i++)
        {
                for (j = 0; j < n; j++)
                {
                        matrix_out[i][j] = 0;
                }
        }

        // 让用户输入第一个矩阵
        printf("请输入第一个矩阵的值:\n");
        for (i = 0; i < m; i++)
        {
                for (j = 0; j < p; j++)
                {
                        scanf("%d", &matrix_in_1[i][j]);
                }
        }

        // 让用户输入第二个矩阵
        printf("请输入第二个矩阵的值:\n");
        for (i = 0; i < p; i++)
        {
                for (j = 0; j < n; j++)
                {
                        scanf("%d", &matrix_in_2[i][j]);
                }
        }

        // 计算乘积并保存
        for (i = 0; i < m; i++)
        {
                for (j = 0; j < n; j++)
                {
                        for (k = 0; k < p; k++)
                        {
                                matrix_out[i][j] += matrix_in_1[i][k] * matrix_in_2[k][j];
                        }
                }
        }

        printf("计算结果如下:\n");

        // row 取行数最大值
        row = m > p ? m : p;

        for (i = 0; i < row; i++)
        {
                printf("|  ");
                // 打印 matrix_in_1
                for (j = 0; j < p; j++)
                {
                        if (i < m)
                        {
                                printf("\b%d ", matrix_in_1[i][j]);
                                printf("|");
                        }
                        else
                        {
                                printf("\b\b\b     ");
                        }
                }
                // 打印 * 号
                if (i == row / 2)
                {
                        printf(" * ");
                }
                else
                {
                        printf("   ");
                }
                printf("|  ");
                // 打印 matrix_in_2
                for (j = 0; j < n; j++)
                {
                        if (i < p)
                        {
                                printf("\b%d ", matrix_in_2[i][j]);
                                printf("|");
                        }
                        else
                        {
                                printf("\b\b\b     ");
                        }
                }
                // 打印 = 号
                if (i == row / 2)
                {
                        printf(" = ");
                }
                else
                {
                        printf("   ");
                }
                // 打印 matrix_out
                printf("|  ");
                for (j = 0; j < n; j++)
                {
                        if (i < m)
                        {
                                printf("\b%d ", matrix_out[i][j]);
                                printf("|");
                        }
                        else
                        {
                                printf("\b\b\b      ");
                        }
                }
                printf("\n");
        }

        return 0;
}

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值