
❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、洛谷刷题、C/C++基础知识知识强化补充、C/C++干货分享&学习过程记录
🍉学习方向:C/C++方向学习者
⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平
前言:距离我们学完C语言已经过去一段时间了,在学习了初阶的数据结构之后,博主还要更新的内容就是【C语言16天强化训练】,之前博主更新过一个【C语言刷题12天IO强训】的专栏,那个只是从入门到进阶的IO模式真题的训练。【C语言16天强化训练】既有IO型,也有接口型。和前面一样,今天依然是训练五道选择题和两道编程算法题,希望大家能够有所收获!

目录
[1.1 题目1](#1.1 题目1)
[1.2 题目2](#1.2 题目2)
[1.3 题目3](#1.3 题目3)
[1.4 题目4](#1.4 题目4)
[1.5 题目5](#1.5 题目5)
[2.1 自除数](#2.1 自除数)
[2.1.1 题目理解](#2.1.1 题目理解)
[2.1.2 思路](#2.1.2 思路)
[2.2 除自身以外数组的乘积](#2.2 除自身以外数组的乘积)
[2.2.1 题目理解](#2.2.1 题目理解)
[2.2.2 思路](#2.2.2 思路)
一、五道选择题
1.1 题目1
**题干:**下列程序的输出是( )
cpp
#include<stdio.h>
int main()
{
int a [12]= {1,2,3,4,5,6,7,8,9,10,11,12},*p[4],i;
for(i=0;i<4;i++)
p[i]=&a [i*3];
printf("%d\n",p[3][2]);
return 0;
}
A. 上述程序有错误 B. 6 C. 8 D. 12
解析:正确答案是D选项。
(1)p 是一个包含4个指针的数组,每个指针指向 int;
(2)循环中,p[ 0 ] = &a[0], p[1] = &a[3], p[2] = &a[6], p[3] = &a[9];
(3)p[ 3 ][ 2 ]相当于 *(p[3] + 2),即从 p[3](指向 a[9])向后偏移2个元素,也就是 a[11],其值为12。
因此输出12,答案是D选项。
1.2 题目2
**题干:**二维数组X按行顺序存储,其中每个元素占1个存储单元。若 X [ 4 ][ 4 ] 的存储地址为Oxf8b82140 ,X [ 9 ][ 9 ] 的存储地址为 Oxf8b8221c ,则 X [ 7 ][ 7 ] 的存储地址为( )
A. Oxf8b821c4 B. Oxf8b821a6 C. Oxf8b82198 D. Oxf8b821c0
解析:正确答案是A选项。
假设数组有 rows
行、cols
列(未知),按行存储。
设起始地址为 base
,则 X[i][j]
的地址 = base + i * cols + j
。
已知:
(1)X[4][4]:base + 4*cols + 4 = 0xf8b82140;
(2)X[9][9]:base + 9cols + 9 = 0xf8b8221c。
两式相减:
(9cols + 9) - (4cols + 4) = 5cols + 5 = 0xf8b8221c - 0xf8b82140 = 0xDC(十进制220),所以 5*(cols + 1) = 220 ⇒ cols + 1 = 44 ⇒ cols = 43。
我们现在来求 X[ 7 ][ 7 ]------
地址 = base + 7cols + 7
已知 base + 4cols + 4 = 0xf8b82140,所以
base + 7cols + 7 = (base + 4cols + 4) + (3cols + 3) = 0xf8b82140 + 343 + 3 = 0xf8b82140 + 129 + 3 = 0xf8b82140 + 132。
132的十六进制是 0x84
,所以 0xf8b82140 + 0x84 = 0xf8b821c4
。
1.3 题目3
**题干:**以下哪个选项可以正确描述 sizeof(double) ( )
A. 一个整型表达式 B. 一个双精度型表达式 C. 一个不合法的表达式 D. 一种函数调用
解析:正确答案是A选项。
sizeof
是运算符,返回类型或对象的大小(字节数),类型是 size_t
(无符号整型)。
所以 sizeof(double)
是一个整型表达式。
1.4 题目4
**题干:**下列代码运行后的结果是什么( )
cpp
int main()
{
char a = 'a',b;
printf("%c,", ++a);
printf("%c\n", b = a++);
return 0;
}
A. b,b B. b,c C. a,b D. a,c
解析:正确答案是A选项。
(1)a 初始为 ' a '(ASCII 97);
(2)++a:先自增,a 变为 ' b '(98),输出 ' b ';
(3)b = a++:先将a(当前为' b ')赋值给b,然后a自增为' c '。所以b为' b ',输出' b ';
(4)因此输出"b,b"。
1.5 题目5
**题干:**以下逗号表达式的值为( )
cpp
(x = 4 * 5 , x * 5),x + 5;
A. 25 B. 20 C. 100 D. 45
解析:正确答案是A选项。
逗号表达式的值是最后一个子表达式的值。
(1)先计算x = 4 * 5(即20),所以x = 20。
(2)然后计算x * 5(即100),但逗号表达式的值取最后一个,即 x * 5 为100?但注意整个表达式是 ((x = 4 * 5, x * 5) , x + 5),所以最外层逗号的值是x + 5。
(3)具体:内层 (x =20, x * 5) 的值为100(但x仍为20),然后外层逗号表达式值为 x + 5 = 25。
实际上,表达式是(x = 4 * 5,x * 5), x + 5,整个逗号表达式的值是最后一个(即x + 5)。
所以值为 20 + 5 = 25。
选择题答案如下:
1.1 D
1.2 A
1.3 A
1.4 A
1.5 A
校对一下,大家都做对了吗?
二、两道算法题
2.1 自除数
力扣链接:728. 自除数
力扣题解链接:逐位分解法解决【自除数】问题
题目描述:

2.1.1 题目理解
检查数字是否能被其每一位非零数字整除的数学问题。
这道题是接口型的,下面是C语言的模版(如果是IO型就可以不用管它们了)------

2.1.2 思路
算法说明:
1、辅助函数 isSelfDividing
(1)检查一个数是否是自除数。
(2)逐位取出数字,检查------
1)如果数字为0,直接返回false(自除数不允许包含0);
2)如果原数不能被该位数字整除,返回false。
(3)所有位都满足条件则返回true。
2、主函数:
(1)遍历从left到right的所有数字;
(2)对每个数字调用辅助函数检查是否为自除数;
(3)将满足条件的数字加入结果集。
代码演示:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
bool isSelfDividing(int num) {
int temp = num;
while (temp > 0) {
int digit = temp % 10;
if (digit == 0 || num % digit != 0) {
return false;
}
temp /= 10;
}
return true;
}
int* selfDividingNumbers(int left, int right, int* returnSize) {
int* result = (int*)malloc((right - left + 1) * sizeof(int));
int count = 0;
for (int i = left; i <= right; i++)
{
if (isSelfDividing(i))
{
result[count++] = i;
}
}
*returnSize = count;
return result;
}
时间复杂度 :O(n*d);
空间复杂度 :O(n)。
我们学习了C++之后也可以尝试用C++来实现一下,看看自己前段时间C++学得怎么样------

代码演示:
cpp
#include <vector>
using namespace std;
class Solution {
public:
vector<int> selfDividingNumbers(int left, int right)
{
vector<int> result;
for (int i = left; i <= right; i++)
{
if (isSelfDividing(i))
{
result.push_back(i);
}
}
return result;
}
private:
bool isSelfDividing(int num)
{
int temp = num;
while (temp > 0)
{
int digit = temp % 10;
if (digit == 0 || num % digit != 0)
{
return false;
}
temp /= 10;
}
return true;
}
};
时间复杂度: O(n*d)****,空间复杂度: O(n)****。
时间复杂度:O(n × d),其中n是数字范围大小,d是数字的平均位数;
空间复杂度:O(n)(存储结果)。
两种实现的核心逻辑相同,只是C++使用了vector容器,而C语言需要手动管理内存。
我们目前要写出来C++的写法是非常考验前面C++的学习情况好不好的,大家可以尝试去写一写,优先掌握C语言的写法,博主还没有介绍C++的算法题,之后会涉及的,敬请期待!
2.2 除自身以外数组的乘积
力扣链接:238. 除自身以外数组的乘积
力扣题解链接:前缀后缀乘积法解决【除自身以外数组的乘积】
题目描述:

2.2.1 题目理解
使用前缀和后缀乘积在O(n)时间内计算数组中每个元素除自身外所有元素乘积的算法问题。
2.2.2 思路
思路 :使用前缀乘积和后缀乘积的方法,避免使用除法。
1、第一次遍历(从左到右):
(1)计算每个元素左边所有元素的乘积;
(2)answer[ i ] = nums[0] * nums[1] * nums[2] ... * nums[i - 1]。
2、第二次遍历(从右到左):
(1)维护一个变量 right 表示当前元素右边所有元素的乘积;
(2)将左边的乘积与右边的乘积相乘得到最终结果;
(3)answer[ i ] = left_product * right_product。
这道题是接口型的,下面是C语言的模版(如果是IO型就可以不用管它们了)------

代码演示:
cpp
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
bool isSelfDividing(int num) {
int temp = num;
while (temp > 0)
{
int digit = temp % 10;
if (digit == 0 || num % digit != 0)
{
return false;
}
temp /= 10;
}
return true;
}
int* selfDividingNumbers(int left, int right, int* returnSize) {
int* result = (int*)malloc((right - left + 1) * sizeof(int));
int count = 0;
for (int i = left; i <= right; i++)
{
if (isSelfDividing(i))
{
result[count++] = i;
}
}
*returnSize = count;
return result;
}
时间复杂度 :O(n);
空间复杂度 :O(1)。
(1)时间复杂度:O(n) ------ 进行两次遍历;
(2)空间复杂度:O(1) ------ 除了结果数组外只使用了常数空间(C++版本为**O(n)**存储结果,但题目要求如此)。
我们学习了C++之后也可以尝试用C++来实现一下,看看自己前段时间C++学得怎么样------

代码演示:
cpp
class Solution {
public:
vector<int> selfDividingNumbers(int left, int right)
{
vector<int> result;
for (int i = left; i <= right; i++)
{
if (isSelfDividing(i))
{
result.push_back(i);
}
}
return result;
}
private:
bool isSelfDividing(int num)
{
int temp = num;
while (temp > 0)
{
int digit = temp % 10;
if (digit == 0 || num % digit != 0)
{
return false;
}
temp /= 10;
}
return true;
}
};
时间复杂度: O(n)****,空间复杂度: O(1)****。
我们目前要写出来C++的写法是非常考验前面C++的学习情况好不好的,大家可以尝试去写一写,优先掌握C语言的写法,博主还没有介绍C++的算法题,之后会涉及的,敬请期待!
结尾
本文内容到这里就全部结束了,希望大家练习一下这几道题目,这些基础题最好完全掌握!
往期回顾:
结语: 感谢大家的阅读,记得给博主"一键四连",感谢友友们的支持和鼓励!