c语言 练习题二

1、公务员面试

描述:

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

(注:本题有多组输入)

输入描述:

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

输出描述:

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

示例1:

输入:99 45 78 67 72 88 60

输出:73.00

我的代码:

#include<stdio.h>

int main()
{
	int arr[8];
	int i = 0;
	int j = 0;
	int n = 0;
	int exp = 0;
	int sum = 0;
	double average = 0;

	while (scanf("%d %d %d %d %d %d %d", &arr[0], &arr[1], &arr[2], &arr[3], &arr[4], &arr[5], &arr[6]) != EOF)
	{
		//从小到大排列
		for (i = 0; i < 6; i++)
		{
			for (j = 0; j < 6 - i; j++)
			{
				if (arr[j] > arr[j + 1])
				{
					exp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = exp;
				}
			}
		}

		//计算平均值
		sum = 0;
		for (i = 1; i <= 5; i++)
		{
			sum = sum + arr[i];
		}
		average = sum / 5.0;

		//打印
		printf("%.2lf", average);
		printf("\n");
	}

	return 0;
}

代码:


2、计算一元二次方程

描述:

从键盘输入a, b, c的值,编程计算并输出一元二次方程ax2 + bx + c = 0的根,当a = 0时,输出“Not quadratic equation”,当a ≠ 0时,根据△ = b2 - 4*a*c的三种情况计算并输出方程的根。

输入描述:

多组输入,一行,包含三个浮点数a, b, c,以一个空格分隔,表示一元二次方程ax2 + bx + c = 0的系数。

输出描述:

针对每组输入,输出一行,输出一元二次方程ax2 + bx +c = 0的根的情况。

  如果a = 0,输出“Not quadratic equation”;

  如果a ≠  0,分三种情况:

△ = 0,则两个实根相等,输出形式为:x1=x2=...

△  > 0,则两个实根不等,输出形式为:x1=...;x2=...,其中x1  <=  x2。

△  < 0,则有两个虚根,则输出:x1=实部-虚部i;x2=实部+虚部i,即x1的虚部系数小于等于x2的虚部系数,实部为0时不可省略。实部= -b / (2*a),虚部= sqrt(-△ ) / (2*a)

所有实数部分要求精确到小数点后2位,数字、符号之间没有空格。

示例1:

输入:0.0 3.0 3.0

输出:Not quadratic equation

示例2:

输入:1 2 1

输出:x1=x2=-1.00

示例3:

输入:2.0 7.0 1.0

输出:x1=-3.35;x2=-0.15

示例4:

输入:2 2 5

输出:x1=-0.50-1.50i;x2=-0.50+1.50i

示例5:

输入:4 0 0

输出:x1=x2=0.00

我的代码:

#include<stdio.h>
#include<math.h>

int main()
{
	double a = 0;
	double b = 0;
	double c = 0;
	while (scanf("%lf %lf %lf", &a, &b, &c) != EOF)
	{
		if (a == 0)
		{
			printf("Not quadratic equation\n");
		}
		else
		{
			if (b * b - 4 * a * c == 0)
			{
				printf("x1=x2=%.2lf\n", (-b + 0) / (2 * a));
			}
			else if (b * b - 4 * a * c > 0)
			{
				printf("x1=%.2lf;x2=%.2lf\n", ((-b - sqrt(b * b - 4 * a * c)) / (2 * a)), ((-b + sqrt(b * b - 4 * a * c)) / (2 * a)));
			}
			else
			{
				printf("x1=%.2lf-%.2lfi;x2=%.2lf+%.2lfi\n", (-b + 0) / (2 * a), sqrt(-(b * b - 4 * a * c)) / (2 * a), (-b + 0) / (2 * a), sqrt(-(b * b - 4 * a * c)) / (2 * a));
			}
		}
	}

	return 0;
}

代码:

#include <stdio.h>
#include <math.h>

#define ESP 0.00000001
int main()
{
    float a = 0.0f;
    float b = 0.0f;
    float c = 0.0f;
    while (scanf("%f %f %f", &a, &b, &c) != EOF)
    {
        //指定一个精度
        if (fabs(a - 0.0) < ESP)
        {
            printf("Not quadratic equation\n");
        }
        else
        {
            float disc = b * b - 4 * a * c;
            if (fabs(disc - 0.0) < ESP)//表示==0
            {
                printf("x1=x2=%.2f\n", (-b + sqrt(disc)) / (2 * a));
            }
            else if (disc >= ESP)//>0
            {
                printf("x1=%.2f;x2=%.2f\n", (-b - sqrt(disc)) / (2 * a), (-b + sqrt(disc)) / (2 * a));
            }
            else //<0
            {
                float real = (-b) / (2 * a);
                float image = sqrt(-disc) / (2 * a);
                printf("x1=%.2f-%.2fi;x2=%.2f+%.2fi\n", real, image, real, image);
            }
        }
    }

    return 0;
}

注:

1.浮点数是有-0的概念的,在计算时,应该给-b+0(如下图所示),如果不加的话, 当b=0时,前面有负号输出的结果就是-0,与标准输出不符(如下图所示)

2.浮点数直接进行比较是不精确的,因为0.0...01并不是零,而要是与0相比的话,可能是相等的,因为计算机比的时候总会取小数点后有限位。

3.在浮点数比较时,应该给一个精度(如上面的ESP),规定在小于这个精度的时候就可以认为是等于0的


3、获得月份天数

描述:

KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。

输入描述:

多组输入,一行有两个整数,分别表示年份和月份,用空格分隔。

输出描述:

针对每组输入,输出为一行,一个整数,表示这一年这个月有多少天。

示例1:

输入:2008 2

输出:29

我的代码:

#include<stdio.h>

int judge(int y)
{
	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
	{
		return 1;
	}
	else
	{
		return 0;
	}

}

int main()
{
	int y = 0;
	int m = 0;
	while (scanf("%d %d", &y, &m) != EOF)
	{
		//如果年份是闰年
		if (judge(y)==1)
		{
			if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
			{
				printf("%d", 31);
			}
			else if (m == 2)
			{
				printf("%d", 29);
			}
			else
			{
				printf("%d", 30);
			}
		}

		//如果年份是平年
		if (judge(y) == 0)
		{
			if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12)
			{
				printf("%d", 31);
			}
			else if (m == 2)
			{
				printf("%d", 28);
			}
			else
			{
				printf("%d", 30);
			}
		}
		printf("\n");
	}
	return 0;
}

代码:


4、字符串左旋

描述:

实现一个函数,可以左旋字符串中的k个字符。

例如:

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

代码1:

#include <stdio.h>
#include <string.h>

void left_move(char* str, int k)
{
	int i = 0;
	for (i = 0; i < k; i++)
	{
		//每次旋转一个字符
		char tmp = *str;
		int len = strlen(str);
		int j = 0;
		for (j = 0; j < len - 1; j++)
		{
			*(str + j) = *(str + j + 1);
		}
		*(str + len - 1) = tmp;
	}
}

int main()
{
	char arr[] = "abcdef";
	int k = 0;
	scanf("%d", &k);
	left_move(arr, k);

	printf("%s\n", arr);
	return 0;
}

代码2:

#include <stdio.h>
#include <string.h>

void reverse(char* left, char* right)
{
	while (left<right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}

void left_move(char* str, int k)
{
	int len = strlen(str);
	k %= len;
	reverse(str, str+k-1);//前
	reverse(str+k, str+len-1);//后
	reverse(str, str+len-1);//整体
}

int main()
{
	char arr[] = "abcdef";
	int k = 0;
	scanf("%d", &k);
	left_move(arr, k);

	printf("%s\n", arr);
	return 0;
}

注:

1.代码1的思路:将第一个字符存在tmp中,然后将后面每一个字符往前移动一个,最后将tmp中的字符存在最后一个字符的位置。如果要移动k个字符,前面的操作执行k次即可

2.代码2的思路:如果左旋k个字符,那么先将字符串前k个字符进行逆序,然后将字符串除前k个字符外其余的字符进行逆序,最后将整个字符串再进行一次逆序即可。此方法k不能大于字符串的长度,因此需要k %= len操作


5.杨氏矩阵

描述:

有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。

要求:时间复杂度小于O(N)(也就是说不能遍历整个数组)

代码1:

#include <stdio.h>
#include <string.h>

void find_int_arr(int arr[3][3], int r, int c, int k)
{
	int x = 0;
	int y = c-1;
	while (y >= 0 && x <= r-1)
	{
		if (arr[x][y] < k)
		{
			x++;
		}
		else if (arr[x][y] > k)
		{
			y--;
		}
		else
		{
			printf("找到了,下标是:%d %d\n", x, y);
			return;
		}
	}
	printf("找不到\n");
}

int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int k = 0;
	scanf("%d", &k);

	find_int_arr(arr, 3, 3, k);

	return 0;
}

运行结果1:

代码2:

#include <stdio.h>
#include <string.h>

void find_int_arr(int arr[3][3], int *px, int *py, int k)
{
	int x = 0;
	int y = *py-1;
	while (y >= 0 && x <= *px-1)
	{
		if (arr[x][y] < k)
		{
			x++;
		}
		else if (arr[x][y] > k)
		{
			y--;
		}
		else
		{
			*px = x;
			*py = y;
			return;
		}
	}
	*px = -1;
	*py = -1;
}

int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	int k = 0;
	int x = 3;
	int y = 3;

	scanf("%d", &k);

	//参数是返回型参数
	find_int_arr(arr, &x, &y, k);
	if (x == -1 && y == -1)
	{
		printf("找不到了\n");
	}
	else
	{
		printf("找到了,下标是:%d %d\n", x, y);
	}

	return 0;
}

运行结果2:

注:

1.矩阵右上角的数是这一行里面的最大值这一列里面最小的,因此如果要找的数大于右上角的数,那就排除矩阵第一行(行的值+1),要是小于右上角的数,那就排除矩阵最后一列(列的值-1),依次用排除后的矩阵右上角元素进行判断,直到行数值小于二维数组行的值或列数值大于0为止

2.代码1中如果我们还需要在主函数里使用找到的坐标,那么我们需要把左边返回给主函数,但是不能return两个值。我们可以利用传址调用来解决,此时参数为返回型参数,如代码2所示


6.选择题

题目名称:

定义一个函数指针,指向的函数有两个int形参并且返回一个函数指针,返回的指针指向一个有一个int形参且返回int的函数?下面哪个是正确的?

题目内容:

A.int (*(*F)(int, int))(int)

B.int (*F)(int, int)

C.int (*(*F)(int, int))

D.*(*F)(int, int)(int)

答案:A

注:

1.A选项:int (* (*F)(int, int) )(int)中代码(*F)(int, int)的F是一个函数指针,指向的函数参数为(int,int),将其去掉剩余的便是其返回类型,去掉(*F)(int, int)A选项剩余int (*  )(int),该返回类型返回一个形参为int,返回为int的函数指针。A选项符合题意

B选项:int (*F)(int, int)中F是一个函数指针,指向函数的参数为(int,int),返回类型是int

C选项:int (* (*F)(int, int) )中代码(*F)(int, int)的F是一个函数指针,指向的函数参数为(int,int),将其去掉剩余的便是其返回类型,去掉(*F)(int, int)C选项剩余int (*),这里int(*)与int *是一样的,函数指针的返回类型是整型指针int*

D选项:D选项代码有问题


7.选择题

题目名称:

声明一个指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*,正确的是

题目内容:

A.(int *p[10])(int*)

B.int [10]*p(int *)

C.int (*(*p)[10])(int *)

D.int ((int *)[10])*p

答案:C

注:

1.A选项:(int *p[10])(int*)是一个返回值为int,参数为一个整型int的函数指针数组

B选项:p先和后面的圆括号相结合,p是一个函数名,代码错误

C选项:p先和*结合,说明p是一个指针;(*p)[10]说明指针p指向的是一个数组,数组10个元素;C选项代码将(*p)[10]删除得到int (* )(int *),说明指针p指向的是一个数组,数组10个元素,每个元素都是一个函数指针,所以该数组是一个存放函数指针的数组,函数的返回值是int,参数是int*符合题意,选项C正确

D选项:选项有问题


8.选择题

题目名称:

设有以下函数void fun(int n,char *s){……},则下面对函数指针的定义和赋值均是正确的是:

题目内容:

A.void (*pf)(int,char); pf=&fun;

B.void (*pf)(int n,char *s); pf=fun;

C.void *pf(); *pf=fun;

D.void *pf(); pf=fun;

答案:B

注:

1.A选项和B选项:A选项pf函数指针参数为(int,char),B选项pf函数指针参数为(int,char*)而fun函数参数为(int,char*),A选项错误,B选项正确。定义函数指针变量时形参有无变量名都可

2.C选项和D选项中pf是一个函数名,错误


9.判断一个字符串是否为另一个字符串旋转结果

题目内容:

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:

给定s1 =AABCD和s2 = BCDAA,返回1

给定s1=abcd和s2=ACBD,返回0

代码1:

#include <stdio.h>
#include <string.h>

void left_move(char* str, int k)
{
	int i = 0;
	for (i = 0; i < k; i++)
	{
		//每次旋转一个字符
		char tmp = *str;
		int len = strlen(str);
		int j = 0;
		for (j = 0; j < len - 1; j++)
		{
			*(str + j) = *(str + j + 1);
		}
		*(str + len - 1) = tmp;
	}
}

int is_left_move(char*arr1, char* arr2)
{
	int len = strlen(arr1);
	int i = 0;
	for (i = 0; i < len; i++)
	{
		left_move(arr1, 1);
		if (strcmp(arr1, arr2) == 0)
			return 1;
	}
	return 0;
}

int main()
{
	char arr1[] = "ABCD";
	char arr2[] = "BCDAA";
	//判断arr2是不是arr1旋转得到的
	int ret = is_left_move(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

代码2:

#include <stdio.h>
#include <string.h>

int is_left_move(char* arr1, char* arr2)
{
	int len1 = strlen(arr1);
	int len2 = strlen(arr2);
	if (len1 != len2)
		return 0;

	strncat(arr1, arr1, len1);
	if (strstr(arr1, arr2))
		return 1;
	else
		return 0;
}

int main()
{
	char arr1[20] = "AABCD";
	//AABCDAABCD
	char arr2[] = "BCDAA";
	//判断arr2是不是arr1旋转得到的
	int ret = is_left_move(arr1, arr2);
	printf("%d\n", ret);
	return 0;
}

注:

1.当给一个字符串后面再追加一个相同的字符串,那么追加后的字符串里面就有追加前字符串旋转的所有可能结果,如代码2所示。使用这种方法,如果两个字符串的长度不相等就无法判断(因为长度不相等一个字符串肯定不是另一个字符串旋转的结果,但是此方法如果判断AABCD和BCD这样的字符串,得到的是相等的),所以我们要先对两个字符串长度进行判断,如果两个字符串不相等直接返回0

2.自己给自己追加的时候,用strcat函数会存在潜在的BUG,因为strcat函数自己给自己追加会把源字符串第一个字符覆盖到其'\0'的位置,因为最后的'\0'已经被覆盖,所以是无法追加'\0'的,因此这个追加是不会停下来的,如下图所示 


10.选择题

题目名称:

有如下宏定义和结构定义

#define MAX_SIZE A+B
struct _Record_Struct
{
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);

当A=2, B=3时,pointer分配( )个字节的空间。

题目内容:

A.20

B.15

C.11

D.9

答案:D

注:

1.分析可得_Record_Struct位段占用3个字节,sizeof(struct _Record_Struct) * MAX_SIZE将MAX_SIZE替换成A+B而A=2, B=3时,因此sizeof(struct _Record_Struct) * MAX_SIZE可以被替换成sizeof(struct _Record_Struct) * 2+3为3*2+3=9,选D选项


11.选择题

题目名称:

下面代码的结果是( )

int main()
{
  unsigned char puc[4];
  struct tagPIM
  {
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}

题目内容:

A.02 03 04 05

B.02 29 00 00

C.02 25 00 00

D.02 29 04 00

答案:B

注:

1.puc是数组名,是数组首元素地址,将其赋值给pstPimData指针变量,该指针变量是struct tagPIM*位段类型,将该数组所占字节全都置为0后,用pstPimData指针变量对数据进行修改,因为这个位段大小为2个字节,所以pstPimData位段类型指针变量只能访问puc数组前两个字节的内存空间

2.pstPimData->ucPim1 = 2:ucPim1成员1个字节空间,2的二进制为10占两个比特位,可以放下,因此第一个字节空间为00000010
  pstPimData->ucData0 = 3,ucData0成员有1个比特位空间,3的二进制为11占两个比特位,放不下,只能放下最右边的1;pstPimData->ucData1 = 4,ucData1成员有2个比特位空间,4的二进制为100占三个比特位,放不下,只能放下右边的两位00;pstPimData->ucData2 = 5,ucData2成员有3个比特位空间,5的二进制为101占三个比特位,能放下,因此第二个字节数据为00101001

 3.%x为十六进制打印,%02x为打印两位十六进制数字,打印出来数字为:02 29 00 00


12.选择题

题目名称:

在X86下,有下列程序

#include<stdio.h>
int main()
{
  union
  {
    short k;
    char i[2];
  }*s, a;
  s = &a;
  s->i[0] = 0x39;
  s->i[1] = 0x38;
  printf(“%x\n”,a.k);
  return 0;
}

输出结果是( )

题目内容:

A.3839

B.3938

C.380039

D.不确定

答案:A

注:

1.因为是联合体,所以short k和char i[2]共用两个字节空间,将第一个字节放入十六进制39第二个字节放入十六进制38,如下图所示

 用短整型类型成员k进行访问,打印两个字节数据,因为是小段存储,所以打印出来为0x3839


13.有序序列的合并

描述:

输入两个升序排列的序列,将两个序列合并为一个有序序列并输出。

数据范围: 1 \le n, m \le 1000 \1≤n,m≤1000  , 序列中的值满足 0 \le val \le 30000 \0≤val≤30000 

输入描述:

输入包含三行,

第一行包含两个正整数n, m,用空格分隔。n表示第二行第一个升序序列中数字的个数,m表示第三行第二个升序序列中数字的个数。

第二行包含n个整数,用空格分隔。

第三行包含m个整数,用空格分隔。

输出描述:

输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重新进行升序序列排列合并。

示例1:

代码1:

#include <stdio.h>

int main()
{
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);//
    int arr1[1000];
    int arr2[1000];
     //输入第一个数组
    int i = 0;
    int k = 0;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr1[i]);
    }
    //输入第二个数组
    for (i = 0; i < m; i++)
    {
        scanf("%d", &arr2[i]);
    }
    //合并输出
    int j = 0;
    i = 0;
    while (i < n && j < m)
    {
        if (arr1[i] < arr2[j])
        {
            printf("%d ", arr1[i]);
            i++;
        }
        else
        {
            printf("%d ", arr2[j]);
            j++;
        }
    }
    if (j == m)
    {
        for (; i < n; i++)
        {
            printf("%d ", arr1[i]);
        }
    }
    else
    {
        for (; j < m; j++)
        {
            printf("%d ", arr2[j]);
        }
    }
    return 0;
}

代码2:

#include <stdio.h>

int main()
{
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);
    int arr1[1000];
    int arr2[1000];
    int arr3[2000];
     //输入第一个数组
    int i = 0;
    int k = 0;
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr1[i]);
    }
    //输入第二个数组
    for (i = 0; i < m; i++)
    {
        scanf("%d", &arr2[i]);
    }
    //合并输出
    int j = 0;
    i = 0;
    while (i < n && j < m)
    {
        if (arr1[i] < arr2[j])
        {
            arr3[k++] = arr1[i];
            i++;
        }
        else
        {
            arr3[k++] = arr2[j];
            j++;
        }
    }
    if (j == m)
    {
        for (; i < n; i++)
        {
            arr3[k++] = arr1[i];
        }
    }
    else
    {
        for (; j < m; j++)
        {
            arr3[k++] = arr2[j];
        }
    }

    //打印
    for (i = 0; i < k; i++)
    {
        printf("%d ",arr3[i]);
    }
    return 0;
}

注:

1.本题将两个字符串的内容按数值大小从小到大打印即可,用i和j分别指向第一个和第二个序列0下标的位置,然后比较序列arr1[i]和arr2[j]的大小,如果arr1[i]较小则打印arr1[i]的内容并且i++,如果arr2[j]较小则打印arr2[j]的内容并且j++,依次类推,直到打印完两个序列中一个序列的所有内容,最后将未打印的序列剩下内容打印即可

2.代码1是直接将序列打印出来,没有保存,如果要将合并后的序列存起来,如代码2所示,思路和代码1相同


14.变种水仙花

描述:

变种水仙花数 - Lily Number:把任意的数字,从中间拆分成两个数字,比如1461 可以拆分成(1和461),(14和61),(146和1),如果所有拆分后的乘积之和等于自身,则是一个Lily Number。

例如:

655 = 6 * 55 + 65 * 5

1461 = 1*461 + 14*61 + 146*1

求出 5位数中的所有 Lily Number。

输入描述:

输出描述:

一行,5位数中的所有 Lily Number,每两个数之间间隔一个空格。

代码:

#include <stdio.h>
#include <math.h>

int main()
{
    int i = 0;
    for (i = 10000; i <= 99999; i++)
    {
        int j = 0;
        int sum = 0;
        for (j = 1; j <= 4; j++)
        {
            int m = i % (int)pow(10, j);
            int n = i / pow(10, j);
            sum += m * n;
        }
        if (i == sum)
        {
            printf("%d ", i);
        }
    }

    return 0;
}

15.找单身狗

题目名称:

找单身狗

题目内容:

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

代码1:

#include <stdio.h>
#include <math.h>

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
	//1. 先异或在一起
	int ret = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		ret ^= arr[i];
	}
	//2. 计算ret的二进制中第几个位是1
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if (((ret >> i) & 1) == 1)
		{
			pos = i;
			break;
		}
	}
	//3.按照第pos位为0或1来分组
	int n = 0;
	int m = 0;
	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
		{
			n ^= arr[i];
		}
	}

	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 0)
		{
			m ^= arr[i];
		}
	}
	printf("%d %d\n", n, m);

	return 0;
}

代码2:

#include <stdio.h>
#include <math.h>

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
	//1. 先异或在一起
	int ret = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		ret ^= arr[i];
	}
	//2. 计算ret的二进制中第几个位是1
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if (((ret >> i) & 1) == 1)
		{
			pos = i;
			break;
		}
	}
	//3.按照第pos位为0或1来分组
	int n = 0;
	int m = 0;
	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
		{
			n ^= arr[i];
		}
	}
	m = ret ^ n;

	printf("%d %d\n", n, m);

	return 0;
}

注:

1.当一个数组只有一个数字出现一次的时候将所有数字异或,便可得到该数字(相同的数字异或为0,0和一个数字异或结果为该数字)

2.我们可以对该数组进行分组,分组的要求:

(1)只出现1次的2个数字,分别到2个组中,一个组中有一个

(2)每个组都满足,只有1个数字出现一次,其他数字都是成对出现的

然后将两个组所有的数字分别异或在一起,得到的两个数字即为所求数字

3.相同的数字其二进制位一定相同。将所有数字异或,得到的便是两个单身狗数字异或的结果,因为异或相同为0不同为1,因此所有二进制位异或的结果中二进制位1的位是两个单身狗数字二进制位不同的位。所以所有数字异或,我们找出一个二进制位为1的位,然后将所有数字该位为0的分为一组,所有数字该位为1的分为一组,就可以满足上述的分组条件。

如1 2 3 4 5 1 2 3 4 6进行分组,5的二进制位为101,6的二进制位为110,所有数字进行异或也就是5和6异或得011,我们就按最右边二进制位进行数组分类,也就是所有数字最低位为1的放到一个数组,最低位为0的放到一个数组,如下面所示:

最低位为1的数组:1 1 3 3 5

最低位为0的数组:2 2 4 4 6


16.模拟实现atoi函数

题目内容:

模拟实现atoi

-------------------------------------------------------------------------------------------------------------------------

atoi函数介绍:

参数:

const char *string:字符串名字(首地址)

返回:

int:转化后的整数值

功能:

把一个字符串转化成对应的整型

代码:

#include<stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>

enum State
{
	INVALID,//非法
	VALID   //合法
};

enum State status = INVALID;

int my_atoi(const char* str)
{
	assert(str);
	//空字符串
	if (*str == '\0')
		return 0;

	//空白字符(跳过)
	while (isspace(*str))
	{
		str++;
	}
	int flag = 1;
	
	//+-
	if (*str == '+')
	{
		str++;
		flag = 1;
	}
	else if (*str == '-')
	{
		str++;
		flag = -1;
	}

	long long n = 0;
	while (isdigit(*str))
	{
		n = n * 10 + flag*(*str - '0');
		//越界的值
		if (n > INT_MAX || n < INT_MIN)
		{
			return 0;
		}
		str++;
	}
	if (*str == '\0')
	{
		//合法返回
		status = VALID;
		return (int)n;
	}

	return (int)n;
}


int main()
{
	int ret = my_atoi("   -111");//"0"
	if (status == VALID)
		printf("%d\n", ret);
	else
		printf("非法返回\n");

	return 0;
}

注:

1.进行模拟实现atoi函数时要考虑的全面一些:

(1)传的是空指针:assert(str)

(2)传的是空字符串:返回0并且status为INVALID

(3)传的是空白字符(不考虑12 34这种情况,因为这种情况库函数里的atoi也只返回12,需要考           虑的是    123这种情况):跳过空白字符

(4)传的字符串里面有正负号,比如"-123":正负号判断,用flag记录

(5)传的字符串里面有非数字字符:返回非数字字符前的数字字符对应的数值并且status为                   INVALID

(6)传的字符串转换成数字后一个int整型类型存不下:如果超出整型最大值和最小值就返回0,             并且status为INVALID


17.选择题

下列关于文件名及路径的说法中错误的是:

A.文件名中有一些禁止使用的字符

B.文件名中一定包含后缀名

C.文件的后缀名决定了一个文件的默认打开方式

D.文件路径指的是从盘符到该文件所经历的路径中各符号名的集合

答案:B

注:

1.文件名中有一些禁止使用的字符,创建一个文本使用不能包含的字符命名,系统会提示如下

 2.文件名可以不包含后缀名

 3.文件的后缀名决定了一个文件的默认打开方式,比如说.txt默认打开方式为记事本,这个默认打开方式可以人为修改,当遇到没有后缀名的文件时,系统会让你选择打开方式

4.文件路径指的是从盘符到该文件所经历的路径中各符号名的集合

例如:c:\code\test.txt

c:\code\test.txt
文件路径文件名主干文件后缀

18.选择题

C语言中关于文件读写函数说法不正确的是:

A.fgetc是适用于所有输入流字符输入函数

B.getchar也是适用于所有流的字符输入函数

C.fputs是适用于所有输出流的文本行输出函数

D.fread是适用于文件输入流的二进制输入函数

答案:B

注:getchar、sacnf函数只能适用于标准输入流,printf函数只能适用于标准输出流


19.判断程序的功能

下面程序的功能是什么? 

int main()
{ 
  long num=0;
  FILE *fp = NULL;
  if((fp=fopen("fname.dat","r"))==NULL)
  {
    printf("Can’t open the file! ");
    exit(0):
  }
  while(fgetc(fp) != EOF)
  { 
    num++;
  }
  printf("num=%d\n",num);
  fclose(fp);
  return 0;
}

A.拷贝文件

B.统计文件的字符数

C.统计文件的单词数

D.统计文件的行数

答案:B

注:

1.exit(0): exit是退出的意思,exit函数只要一执行,程序就结束;与return 0是有区别的,比如在函数中如果是return 0那么程序会退出该函数从主函数中函数调用下一行继续执行,在函数中如果是exit(0)那么程序直接结束不会再返回主函数


20.选择题

以下关于头文件,说法正确的是

A.#include<filename.h>,编译器寻找头文件时,会从当前编译的源文件所在的目录去找

B.#include“filename.h”,编译器寻找头文件时,会从通过编译选项指定的库目录去找

C.多个源文件同时用到的全局整数变量,它的声明和定义都放在头文件中,是好的编程习惯

D.在大型项目开发中,把所有自定义的数据类型、函数声明都放在一个头文件中,各个源文件都只需要包含这个头文件即可,省去了要写很多#include语句的麻烦,是好的编程习惯

答案:D

注:

1.选项c是错误的,多个源文件同时用到的全局整数变量,它的声明和定义都放在头文件中,因为每个源文件都要包含头文件,那么进行预编译,每一个源文件里面都会有定义这个全局变量的代码,在链接的时候会引起冲突,如下图所示


21.交换奇偶位

内容:

写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

代码:

#include<stdio.h>
#define SWAP(x) (((x&0xaaaaaaaa)>>1) + ((x&0x55555555)<<1))

int main()
{
	int a = 11;
	printf("%d\n", SWAP(a));
	return 0;
}

注:

1.第一步:把这个整数的二进制位所有偶数位保留,所有奇数位置0

                (与上10101010101010101010101010101010,这个数十六进制就是0xaaaaaaaa)

   第二步:把这个整数的二进制位所有奇数位保留,所有偶数位置0

                (与上01010101010101010101010101010101,这个数十六进制就是0x55555555)

   第三步:将偶数位保留,奇数位置0的结果向右移动一位,这样偶数位的二进制数跑到了奇数位

                 将奇数位保留,偶数位置0的结果向左移动一位,这样奇数位的二进制数跑到了偶数位

   第四步:将第三步两个移位后结果值加在一起


22.offsetof函数宏实现

写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明

考察:offsetof宏的实现

参数:

structName:结构体类型名

memberName:结构体成员们

返回:

 size_t:结构体成员的偏移量

注:

1.函数是不可能传类型的,所以offsetof函数只能用宏来实现

2.使用offsetof函数必须引用stddef.h头文件

代码:

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

struct S
{
	char c;
	int a;
	double d;
};

#define OFFSETOF(s_name, m_name)    (int)&(((s_name*)0)->m_name)

int main()
{
	printf("%d\n", OFFSETOF(struct S, c));
	printf("%d\n", OFFSETOF(struct S, a));
	printf("%d\n", OFFSETOF(struct S, d));

	return 0;
}

注:

1.结构体在内存创建空间,假如结构体的地址为0x1000,那么第一个成员变量c的地址也为0x1000,其偏移量为0;第二个成员变量a的地址为0x1004,用a的地址减去第一个成员变量地址(结构体的地址)就是第二个成员变量的偏移量,如果我们把结构体地址变为0,那么第二个成员的地址就是其偏移量,以此类推

2.(s_name*)0表示将数值0强制转换成结构体指针类型,那么0此时就代表0地址,结构体的地址就置为了0;&(((s_name*)0)->m_name)就找到了结构体成员的地址,偏移量应该是一个整型,因此再强制转换成整型即可

3.0地址处是不能访问的,我们这里面没有访问,我们只是假设0地址处放了个结构体变量,只是改变了其类型,并没有真的去放,使用的时候我们也只是去取地址,没有访问那块空间的数据,因此也不存在越界访问


23.代码运行结果

下面代码的运行结果为:

运行结果:

注:

1.循环运行7-3-3-3=-2

   -2的原码:10000000 00000000 00000000 00000010

   -2的反码:11111111 11111111 11111111 11111101

   -2的补码:11111111 11111111 11111111 11111110

i=-2,变量i里面的数据为11111110,因为i为无符号字符型,所以为254,254=84*3+2,因此此时变量i的值依次为7,4,1,254,251 ...... 5,2

2. 2-3=-1

   -1的补码:11111111 11111111 11111111 11111111

i=-1,变量i里面的数据为11111111,因为i为无符号字符型,所以为255,255=85*3,因此此时变量i的值依次为7,4,1,254,251 ...... 5,2,255,252 . ..... 6,3,0

3.从254到2总共(254-2)3=85个数字,255到0总共86个数字,因为0的时候判断为假不会进入循环体进行++j,因此要减一,总共85+86+3-1=173


24.代码运行结果

以下程序运行时,若输入1abcedf2df<回车>输出结果是?

运行结果:


25.选择题

以下哪个选项一定可以将flag的第二个bit置0

答案:A

注:

1.2的二进制序列:00000000 00000000 00000000 00000010

   ~2的二进制序列:11111111 11111111 11111111 11111101

   flag&=~2:会将flag的第二个bit置0


26.选择题

下面代码在#pragma pack(4)和#pragma pack(8)的情况下,结构体的大小分别是

 

 

答案:C


27.选择题

下面代码运行的结果为

 

答案:D


28.选择题

下列C程序执行后,c输出的结果为()(32位)

 

答案:A

注:

1.

int a=-3:

-3的原码:10000000 00000000 00000000 00000011

-3的反码:11111111 11111111 11111111 11111100

-3的补码:11111111 11111111 11111111 11111101

a变量中的数据为11111111 11111111 11111111 11111101

unsigned int b=2:

2的补码:00000000 00000000 00000000 00000010

2.long c=a+b,c中的数据为11111111 11111111 11111111 11111111,c中数据的原码为-1,因此打印出来的值为-1

3.%ld是打印长整型


29.选择题

设有定义char *p[ ]={"Shanghai","Beijing","Hongkong"};则结果为j字符的表达式是()

答案:B

注:

1.*p[1]+3:p[1]为字符串"Beijing"的地址,即首元素B的地址,*p[1]拿到了字符'B',*p[1]+3得到了字符'E'

2.*(p[1]+3):p[1]为字符串"Beijing"的地址,即首元素B的地址,p[1]+3指向了字符'j'的地址,*(p[1]+3)得到了字符'j'

3.*(p[3]+1):越界访问了

4.p[3][1]:越界访问了


30.选择题

执行下面函数后,i的值为()

答案:C


31.选择题

在int[ ][4]={{1},{3,2},{4,5,6},{0}};中,p[1][2]的值是()

答案:B


32.选择题

在下面代码中,fun(21)的运行结果是()

  

 

答案:A

注:

1.a^=(1<<5)-1:

1的二进制序列:00000000 00000000 00000000 00000001

(1<<5)-1的二进制序列:00000000 00000000 00000000 00011111

21的二进制序列:00000000 00000000 00000000 00010101

a^=(1<<5)-1的二进制序列:00000000 00000000 00000000 00001010     该二进制十进制为10


33.选择题

下列关于c/c++的宏定义,不正确的是()

 

答案:B

注:

1.在c语言中const修饰的变量是一个常变量,而在c++中const修饰的变量就是彻彻底底的常量,而宏定义的因为没有类型所以不够严谨,所以鼓励用const修饰(如下代码所示,c++中下面代码是正确的,而在c中下面代码是错误的) 


34.选择题

下面关于指针的描述不正确的是()

 

答案:A


35.选择题

由多个源文件组成的c程序,经过编辑、预处理、编译、链接等阶段会生成最终的可执行程序。下面哪个阶段可以发现被调用的函数未定义

答案:C


36.选择题

#define N 3+1

#define Y(n) ((N+1)*n)

则执行语句z=2*(N+Y(5+1))后,z的值为()

 

答案:A


37.选择题

char a;  int b;  flat c;  double d;

则表达式a*b+d-c值的类型为()

答案:D


38.Fibonacci数列

代码链接:Fibonacci数列__牛客网 (nowcoder.com)

 代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<math.h>

int main()
{
    int n = 0;
    int a = 0;
    int b = 1;
    int c = a + b;
    scanf("%d", &n);
    while (1)
    {
        if (n == b)
        {
            printf("%d\n", 0);
            break;
        }
        else if (n < b)
        {
            if (abs(a - n) < abs(b - n))
            {
                printf("%d\n", abs(a - n));
                break;
            }
            else
            {
                printf("%d\n", abs(b - n));
                break;
            }
        }
        a = b;
        b = c;
        c = a + b;
    }
    return 0;
}

39.替换空格

代码链接: 替换空格__牛客网 (nowcoder.com)

代码:(接口型试题)

class Solution {
public:
    void replaceSpace(char* str, int length) {
        int spacecnt = 0;
        char* cur = str;
        while (*cur)
        {
            if (*cur == ' ')
                spacecnt++;
            cur++;
        }
        int newlen = length + spacecnt * 2;
        int end1 = length - 1;
        int end2 = newlen - 1;

        while (end1 != end2)
        {
            if (str[end1] != ' ')
            {
                str[end2--] = str[end1--];
            }
            else
            {
                str[end2--] = '0';
                str[end2--] = '2';
                str[end2--] = '%';
                end1--;
            }
        }
    }
};

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

随风张幔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值