C语言面试题每日一练(二)

       C语言作为嵌入式Linux开发的必备工具,作为嵌入式Linux开发的基础语言,那么在面试嵌入式工程师时C语言定是面试中的重中之重 。作为一名开学就大三的老学长,不得不为找工作做必要准备。每天做一道C语言面试题,为面试打基础。
Alt

2020.8.11

题目描述:
       在 32 位编译器上,设有定义

       char *str1 = "Hello", str2[] = "Hello";

       则以下语句 printf("%d %d", sizeof(str1), sizeof(str2)); 的输出结果是

题目解析:
       本题主要考查了指向字符串的指针(归根结底是一个字符型指针变量)和字符数组, str1 为一个字符指针,所以 sizeof 为 4 , str2 为字符数组,其中包含 6 个字符,所以答案为 4 6 。

2020.8.12

题目描述:
       嵌入式系统总要是用户对变量或寄存器进行位操作。给定一个整形变量啊,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个位操作中,要保证其他位不变。

题目解析:
       这种题目对于学过STM32寄存器版本的应该很好理解,在对寄存器初始化运算时经常使用位操作。

#define BIT3 (0x1<<3)//1000

int a;
void set_bit3(void)
{
    
    
	a|=BIT3;//0000|1000=1000
}
void clear_bit3(void)
{
    
    
	a&=~BIT3//1000|(~1000)=0000
}

2020.8.13

题目描述:
       What is the following program print? And please explain the answer briefly.

void foo(void)
{
    
    
	unsigned int a=6;
	int b=-20;
	(a+b>6)?puts(">6"):puts("<=6");
}

题目解析:
       在C语言中,如果一个运算包含一个有符号数和一个无符号数,那么C语言会隐式地将有符号数转换为无符号数,这对于标准的算术运算没什么问题,但是对于 < 和 > 这样的关系运算符来说,它会出现非直观的结果,这种非直观的特性经常会导致程序中难以察觉的错误。

#include <stdio.h>

void main()
{
    
    
    unsigned int a=6;
	int b=-20;
	(a+b>6)?puts(">6"):puts("<=6");
    printf("%d",(a+b>6));
}

       以上代码的输出结果如下:
在这里插入图片描述
       关于这个问题感兴趣的可以看看这篇文章,写的很详细:https://blog.csdn.net/gatieme/article/details/52557546

2020.8.14

题目描述:

扫描二维码关注公众号,回复: 11648020 查看本文章

       Which of the following statements is equivalent to int *p[4] ?

        A. int p[4];
        B. int *p;
        C. int *(p[4]);
        D. int (*p)[4];

题目解析:
       题目主要考察了指针数组数组指针的相关知识,其实本质是考察运算符优先级的问题。“[]”的优先级比“*”要高。题目中的P先与“[]”结合,构成一个数组的定义,数组名为P,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,int *p[4] 这是一个数组,其包含4 个指向int 类型数据的指针,即指针数组。通过以上分析很容易得到答案就是 C 啦!

       指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。
       数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

       关于指针的问题可以参照这篇博客:C指针详解

2020.8.15

题目描述:
       1、用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

       2、写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。

题目解析:

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

#define MIN(A,B) ((A)<=(B))? (A):(B) 

       这种题目可能乍一看非常简单,但是往往是要注意很多细节的。#define 句末不能加 ; 一年的秒数,超过了16位机器上,整数所能表示的最大值。因此UL,告诉编译器这是一个长整数(有的书上直接用UL,没有定义UL为 (unsigned long)),这样的话在机器上是无法跑起来的,需要加上UL的宏定义。unsigned long int 的格式化输出符 为 %lu 。第二个宏定义可能很多同学会直接写成#define MIN(A,B) A<B?A:B,但是实际上传递的参数可能不是一个简单的变量,而可能是一个表达式,要注意陷阱。

2020.8.16

题目描述:

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

输出: [1,2,2,3,5,6]

题目解析:
方法一:

#include <stdio.h>

void merge(int* nums1, int m, int* nums2, int n){
    
    
     int i,j,temp;
     for(i=m,j=0;i<m+n;i++,j++)
     {
    
    
         nums1[i]=nums2[j];
     }
     for(i=0;i<m+n;i++)
     {
    
    
         for(j=i+1;j<m+n;j++)
         {
    
    
             if(nums1[j]<nums1[i])
             {
    
    
                 temp=nums1[i];
                 nums1[i]=nums1[j];
                 nums1[j]=temp;
             }
         }
     }
}
int main()
{
    
    
    int nums1[]={
    
    1,2,3,0,0,0};
    int nums2[]={
    
    2,5,6};
    merge(nums1,3,nums2,3);
    for(int i=0;i<6;i++){
    
    
        printf("%d",nums1[i]);
    }
    return 0;
}

方法二:

#include <stdio.h>

void merge(int* nums1, int m, int* nums2, int n){
    
    
     for(int i=0;i<n;i++){
    
    
		int j=m-1;
		for(;j>=0;j--){
    
    
			if(nums2[i]<nums1[j]){
    
    
				nums1[j+1]=nums1[j];
			}else{
    
    
                break;
            }
		}
		nums1[j+1]=nums2[i];
		m++;
	}
}
int main()
{
    
    
    int nums1[]={
    
    1,2,3,0,0,0};
    int nums2[]={
    
    2,5,6};
    merge(nums1,3,nums2,3);
    for(int i=0;i<6;i++){
    
    
        printf("%d",nums1[i]);
    }
    return 0;
}

       方法一采用了传统的方法,先将两个数组进行合并,然后再进行排序。优点是思想简单,几乎每个人都可以想到。缺点也很明显,因为题目中给的是两个已经排序了的数组,我们通过冒泡排序显然时间复杂度很高,那么针对数组已经排序的特点我们可以使用插入法进行排序就可以大大减少耗时和内存占用。

2020.8.17

题目描述:

       分析以下程序:

void test2()
{
    
    
 char string[10], str1[10];
 int i;
 for(i=0; i<10; i++)
 {
    
    
  str1[i] = 'a';
 }
 strcpy( string, str1 );
}

题目解析:

       定义的是两个字符数组,在循环对str1进行赋值时使得str1的10个元素都为‘a’,这样就使得str1数组没有结束。strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性。

       str1不能在数组内结束。因为str1的存储为{a,a,a,a,a,a,a,a,a,a},没有’\0’(字符串结束符),所以不能结束strcpy( char *s1,char *s2)。他的工作原理是,扫描s2指向的内存,逐个字符赋值到s1所指向的内存,直到碰到’\0’,因为str1结尾没有’\0’,所以具有不确定性。

       正确程序如下:

void test2()
{
    
    
    char string[10], str1[10];
    int i;
    for(i=0; i<9; i++)
    {
    
    
        str1[i] = 'a'; //把abcdefghi赋值给字符数组
    }
    str[i]='\0';//加上结束符
    strcpy( string, str1 );
}

2020.8.18

题目描述:

       分析以下程序:

void test3(char* str1)
{
    
    
 char string[10];
 if( strlen( str1 ) <= 10 )
 {
    
    
  strcpy( string, str1 );
 }
}

题目解析:

  • strlen(str1)—— C风格字符串函数,返回p的长度,但不计\0在内。
  • strcpy是从源地址开始拷贝,直到遇到\0为止。
  • 存在问题:
    要将str1拷贝到string,string为10,str1长度最多为10,而strlen不计\0,所以str1长度最多为9,不能等于10。
  • 解决问题:
    if( strlen( str1 ) <= 10 )应改为<10。
  • strlen与sizeof的区别
    sizeof:看名字就知道求的是字符串实际占用空间,包括’\0’
    strlen:当然这个就是求字符串的长度,不包括’\0’

2020.8.19

题目描述:
       求以下函数的返回值

int func(x)
{
    
    
	int countx = 0;
	while(x)
	{
    
    
		countx ++;
		x = x&(x-1);
	}
	return countx;
}

题目解析:
       x = 9999 时,x-1=9998,用二进制表示
       10011100001111
       10011100001110

       当他们执行&运算并赋值给x,结果x为:10011100001110

       此时x-1为:10011100001101

       当他们执行&运算并赋值给x,结果x为:10011100001100

       如此类推到结果为00000000000000,应该发现x = x&(x-1)就是将x最右边的二进制位1变为0。x为9999时二进制为1的位有8个,所以结果为8。

       通过以上程序可以判断一个数的二进制对应有多少个1

2020.8.20

题目描述:
       已知strcpy函数的原型是:char * strcpy(char * strDest,const char * strSrc);不调用库函数,实现strcpy函数,同时解释为什么要返回char *。

题目解析:
       strcpy函数原型如下:

char *strcpy(char *strDest,const char *strSrc)
{
    
    
    assert((strDest!=NULL)&&(strSrc!=NULL));//条件返回错误,则终止执行
    char *adress=strDest;
    while(((*strDest++)=(*strSrc++))!='\0');
    return adress;
}

之所以有返回值并不是多此一举,而是为了支持链接表达,增加灵活性

char str[20];
int length = strlen( strcpy(str, “Hello World”) );

       不积小流无以成江河,不积跬步无以至千里。而我想要成为万里羊,就必须坚持学习来获取更多知识,用知识来改变命运,用博客见证成长,用行动证明我在努力。
       如果我的博客对你有帮助、如果你喜欢我的博客内容,记得“点赞” “评论” “收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44895651/article/details/107806173