【C语言进阶】题目练习

发布于:2025-07-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

1.箭形图案

思路: 

代码:

2. 公务员面试

分析:

代码 :

3. 判断结构体大小(1)

答案:

分析:

4.判断结构体大小(2)

答案:

分析:

5.宏定义+计算位段大小的选择题

分析: 

答案:

6.位段与指针

分析:

答案:

7. 结构体大小判别

分析: 

答案:

8.联合体的大小

分析:

答案:

9.大小端字节序

分析:

答案:

10.枚举选择题

答案:

11. 编程题:找出只出现一次的数字

分析1:

代码1:

思路2:

代码2:

12.atoi的实现

13.文件读写的选择题(1)

14.看代码说功能

15. 文件读写的选择题(2)

16.预处理的选择题

17.预处理的分析题

18.feof函数的选择题

19.宏替换的选择题(1)

20.宏替换的选择题(2)

21.写一个宏将一个整数的二进制位的奇数和偶数进行交换

思路:


1.箭形图案

KiKi学习了循环,BoBo老师给他出了一系列打印图案的练习,该任务是打印用“*”组成的箭形图案。

输入描述:

本题多组输入,每行一个整数(2~20)。

输出描述:

针对每行输入,输出用“*”组成的箭形。

输入

2

输出

    *
  **
***
  **
    *

思路: 

        可以把图形分为上下两部分,如果输入n上面就是n行,下面就是n+1行;

上半部分:n行

空格:第一行有四个空格,第二行有两个空格,我们可以把2个空格当做一组,第一行打印两组,第二行打印一组;每一行需要递减,所以内部循环减去外面的行数。

*:和行数有关,第一行是一个,第二行是两个。

下半部分:n+1行

空格:两个空格为一组,第一行是0组空格,第二行是1组空格,第三行是2组空格,就是行数-1。

*:第一行是3个*,第二行是2个*,第3行是1个*,每次从n+1开始,需要再减去行数。

代码:

#include<stdio.h>



int main() 
{
    int n = 0;
	while(scanf("%d",&n))
    {
// 上半部分
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n - i; j++)
		{
			printf("  "); // 先打印空格,两个为一组
		}
		for (int k = 0; k <= i; k++) // 第一行一个*,第二行两个*,跟行号有关
		{
			printf("*");
		}
		printf("\n");
	}
	// 下半部分
	for (int i = 0; i < n + 1; i++)
	{
		// 空格
		for (int j = 0; j < i; j++)
		{
			printf("  ");
		}
		for (int k = 0; k < n + 1 - i; k++)
		{
			printf("*");
		}
		printf("\n");
	}
    }
	
	return 0;
}

2. 公务员面试

描述

公务员面试现场打分。有7位考官,从键盘输入若干组成绩,每组7个分数(百分制),去掉一个最高分和一个最低分,输出每组的平均成绩。

(注:本题有多组输入)

输入描述:

一行,输入7个整数(0~100),代表7个成绩,用空格分隔。

输出描述:

一行,输出去掉最高分和最低分的平均成绩,小数点后保留2位,每行输出后换行。

示例1

输入:

99 45 78 67 72 88 60

输出:

73.00

分析:

        本身的逻辑不难,难的是如何oj,我们之前是每次读取一个数字,我们这道题可以一次读取一个数字,也能读取一个数字。

        若读取一个数字,我们需要一个变量n来记录读取数字的个数,每次读取n需要++;

        定义最大最小值并设立处置,每次读取一个数字就需要判断是否是最大或者最小值,以此保证最小最大值保持更新,一旦n ==7,说明数字够了,就开始计算平均值,需要将这些变量进行重新初始化以待下一次实例的调用。

代码 :

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>


int main() {

    int score = 0;
    int max = 0;
    int min = 100; 
    int n = 0;
    int ret = 0;
    while (scanf("%d", &score) == 1) 
    {
        n++;
        if(score > max)
        {
            max = score;
        }
        if (score < min ) 
        {
            min = score;
        }
        ret += score;
        if (n == 7) {
            printf("%.2lf\n",(ret - min - max)/5.0);
            n = 0;
            ret = 0;
            max = 0;
            min = 100;
        }
    }

    return 0;
}

3. 判断结构体大小(1)

判断以下两个结构体的大小:

#include<stdio.h>


struct A 
{
	int a;
	short b;
	int c;
	char d;
};


struct B 
{
	int a;
	short b;
	char c;
	int d;
};

int main() 
{
	struct A a = { 0 };
	struct B b = { 0 };
	printf("%d\n",sizeof(a)); // 16
	printf("%d\n",sizeof(b)); // 12
	return 0;
}

答案:

16,12

分析:

A:a占4个字节偏移量0-3;b占2个字节,对齐数是2,偏移量是4-5;c占4个字节,偏移量是4,6不是4的倍数,所以偏移量是8-11;最后一个d是占1个字节,所以偏移量是12;总共占用13个字节,13不是最大对齐数(4)的倍数,那么最近的倍数是16;

B: a占4个字节偏移量0-3;b占2个字节,对齐数是2,偏移量是4-5;c占1个字节,偏移量是6;d占4个字节,最小对齐数是4,7不是4的倍数,所以偏移量是8,占用8-11;总共占用12个字节,12是最大对齐数的倍数,所以答案是12;

4.判断结构体大小(2)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#pragma pack(4) // 4字节对齐
struct S1
{
	short a; // 0-2
	char d; // 3
	long b; // 4-7 
	long c;// 8-11 
};

struct S2
{
	long b;// 0-3
	short c;// 4-5
	char d;// 6
	long a;// 8-11 
};
struct S3
{
	short c;// 0-1
	long b;// 4-7
	char d;// 8
	long a;// 12-15
};
int main() 
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	printf("%d\n", sizeof(struct S3));

}

答案:

12 12 16

分析:

见注释,这里需要注意的是32位系统重long占4个字节。

5.宏定义+计算位段大小的选择题

分析: 

①首先需要判断位段占用的bit位数,首先第一个成员变量开辟一个字节的空间,8bit,第一个成员+第二个成员用了6bit,剩余2bit,此时第三个成员变量需要1个字节,需要单独开辟,第四个成员变量虽然只需要1bit,但仍然要开辟1字节,总共3字节。

②注意宏定义:3* 2 + 3 = 9

答案:

D

6.位段与指针

分析:

        首先定义一个char类型的数组,有四个元素;定义一个位段指针指向这个数组;将数组全部初始化为0,;

        位段的第一个成员是占用1个字节,剩下三个成员占用一个字节,所以这个位段整体占用两个字节。

        需要给成员赋值,第一个成员赋值为2,8bit足以存下;

第二个成员只有1bit需要存3,所以只能取低1位bit位;

第三个成员只有2bit需要存4,所以只能取低2位bit位;

第四个成员只有3bit需要存5,刚好可以存下。

此时内存分布如下图所示:

此时按照16进制打印两位来输出每一个字节那么就是:

答案:

0000  0010 -》 02

0010 1001 -》  29

0000 0000 -》  00

0000 0000 -》  00 

7. 结构体大小判别

分析: 

总大小是10B,最大对齐数是4,所以必须是4的倍数12B。

答案:

12字节 

8.联合体的大小

union Un
{
    short s[7];
    int n;
};

分析:

        联合体的奥义是成员公用内存,所以s占用14个字节,n占用4个字节,此时14个字节够用了,最后需要考虑最大对齐数是4,所以最终应该是4的倍数,16字节。

答案:

16

9.大小端字节序

注:32位cpu平台

分析:

 首先联合体是2B,这里分别访问数组的第一个元素和第二个元素,这里其实就是给两个字节填充数据;这里需要打印k变量,由于是两个字节,这里就存在字节序大小端的问题,在vs编译器中是采用小端存储,即低字节存在低地址高字节存在高地址(倒着存)那么输出就是0x3839(还原数据,先打印高地址再打印低地址)

 

答案:

3839 

10.枚举选择题

答案:

枚举从0开始,依次递增1,中途可以修改,再依次递增1;

11. 编程题:找出只出现一次的数字

一个数组中有两个数字出现一次其余数字出现了两次,找到这两个只出现了一次的数字。

力扣原题

分析1:

暴力求解,每个元素都要对n个元素进行比较,如果标记到两个元素相等cnt++,如果cnt是1,那么说明只和自己相等,那就是单独的数字了。

代码1:

#include<stdio.h>


void find_dog(int arr[], int sz)
{
	
	for (int i = 0; i < sz; i++)
	{
		int cnt = 0;
		for (int j = 0; j < sz; j++)
		{
			if (arr[i] == arr[j])
			{
				cnt++;
			}
		}
		// cnt = 1的时候需要记录下来
		if (cnt == 1) 
		{
			printf("%d是单独的数字!\n",arr[i]);
		}
	}
}


int main()
{
	int arr[10] = { 1,2,3,4,5,1,2,3,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	find_dog(arr, sz);
	return 0;
}

思路2:

        可以利用异或的特性,两个数字异或如果相同结果就是0,相异结果就是1,所以将这所有的数字全部异或,最后的结果一定不为0,我们假定最后异或出来的结果的最后一位是1,那么相当于最后一位是相异的,那么我们可以按照最后一位是0或者1将所有数字分成两组,这两组分别进行异或,就能得到最终相异的数字。

①所有数字异或得到不为0的结果。

②从结果中找到二进制的某一位是1。

③旨在按照此位进行分组,组内进行异或,最后的结果就是其中一个单独的数字。

核心思想:按照根据异或的原理将两个单独的数字分别分为两组,组内进行异或(其余数字都是成对,异或就是0),最终每组只剩下那个单独的数字。

代码2:

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumber(int* nums, int numsSize, int* returnSize) 
{
    int sum = 0;
    int* ret = malloc(2 * sizeof(int));
    int pos = 0;
    int dog1 = 0;
    int dog2 = 0;
    for(int i = 0;i < numsSize;i++)
    {
       // 1.全部异或得到一个数字
        sum ^= nums[i];
    }
    // 2. 根据这个数字的第n位为1进行分组
    // 3.计算第n位为1
    for(int i = 0;i < 32;i ++)
    {
       if(((sum >> i)&1) == 1)
       {
            pos = i;
            break;
       } 
    }
     //4. 按照第pos位进行分组,组内进行异或
       for(int i = 0;i < numsSize;i++)
       {
            if((nums[i] >> pos)&1 == 1)
            {
                dog1 ^= nums[i];
            }else
            {
                dog2 ^= nums[i];
            }
       }
    ret[0] = dog1;
    ret[1] = dog2;
    *returnSize = 2;
    return ret;
}

12.atoi的实现

        即字符串转换成整数,例如“123456” -》 123456、“-123456” -》 -123456,遇到非数字的时候停止转化,“-123abc456” -》 -123;

        需要考虑以下几点:

①空指针。assert判断。

②空字符串。字符串只有\0,如果返回0,那就会和“0”产生歧义。

③空格。使用isspace判断是否是空格,是空格那么str++跳过空格。

④+-号。定义一个正负号的变量,如果遇到+变量置为1,遇到-变量置为-1,最后返回只需要结果*flag即可。

⑤非数字字符。若字符串为123ABC,应该输出123

正常处理:需要将每一位的数字转换成整型,存入变量,接下来变量只需要*10 + 新这一位的数字即可。

⑥越界问题:当字符串内的数字非常大,此时就可能发生越界,此时我们需要判断是正数越界还是负数越界

做到这里,我们可以将简易版的代码写出:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include <limits.h>
// 判断这个字符串是否合法
enum STATUS
{
	VALID,
	INVALID
}status = INVALID;


int my_atoi(const char* str)
{
	// 正负标记
	int flag = 1;
	long long ret = 0;
	assert(str);
	// 空字符串,返回非法0
	if (*str == '\0')
	{
		return 0;
	}
	// 判断空白字符,跳过
	while (isspace(*str))
	{
		str++;
	}
	// 遇到+-号
	if (*str == '+')
	{
		flag = 1;
		str++;
	}
	else if (*str == '-')
	{
		flag = -1;
		str++;
	}
	// 正常处理数字字符串
	while (*str != '\0')
	{
		if (isdigit(*str)) 
		{
			ret = ret * 10 + flag * (*str - '0');// *str是字符,需要-字符'0'转换成对应的数字
			// 判断是否越界
			if (ret > INT_MAX || ret < INT_MIN) 
			{
				status = INVALID;
				return 0;
			}
		}else
		{
			// 不是数字字符
			status = INVALID;
			return str;
		}
			str++;
	}
	// 没有提前返回说明是正常处理完毕
	if(*str == '\0')
	{
		status = VALID;
	}
	return ret;
}

int main()
{
	char arr[100] = "123456";
	int ret = my_atoi(arr);
	if (status == INVALID)
	{
		printf("数字不合法!:%d\n", ret);
	}
	else if (status == VALID)
	{
		printf("数字为:%d\n", ret);
	}
	return 0;
}

测试:

含非数字字符:

 

负数: 

溢出:

 

13.文件读写的选择题(1)

B:getchar适用于标准输入流。

14.看代码说功能

统计文件的字符个数 

15. 文件读写的选择题(2)

 

D:sprintf是把格式化的数据写入字符串中。

16.预处理的选择题

 C:链接阶段会查找符号表,看这个函数是否存在。

17.预处理的分析题

判断变量的类型

#define INT_PTR int*
typedef int* int_ptr

INT_PTR a,b;
int_ptr c,d;

替换完毕后是:int *a,b,那么a是int*类型,b是int类型;

下面不一样,把int*当做一个整体的类型,c,d都是int*类型。

18.feof函数的选择题

A:错误,解析详见B选项。

19.宏替换的选择题(1)

答案:70

直接替换计算: 2*(4 + Y(5+1)),Y(5+1)是31。

20.宏替换的选择题(2)

答案:B 

21.写一个宏将一个整数的二进制位的奇数和偶数进行交换

思路:

        假如红色位置是偶数位,绿色位置是奇数位;将奇数位的数字全部拿出来,向右移动一位;同理将偶数位的数字都拿出来,向左移动一位,最后相加即可。 

        怎么拿?以偶数位举例,与一个偶数位都是1,奇数位都是0的32位数字即可。

 

#define SWAP_BIT(n) (((n&0x55555555)<<1) + ((n&0xaaaaaaaa)>>1))

int main() 
{
	int n = 10;
	printf("%d\n", SWAP_BIT(n));
	return 0;
}

 这里用10进行测试,1010 -> 0101(5)


网站公告

今日签到

点亮在社区的每一天
去签到