上海科源电子有限公司笔试题(带答案)

本文涵盖数字电子技术基础知识,包括单片机与数码管电路设计、色环电阻计算、加法器实现等,并深入探讨C语言基础,涉及#error指令、内存分配方式、宏定义及链表操作等内容。


数电知识:

1.用单片机和数码管连接起来画电路图

注:该电路为一个三位7段数码管,P3.4、P3.7、P3.5为位选,P1口为段选

2.电阻的颜色,计算电阻值?写出两个色环电阻的阻值,比如红蓝黑,电阻是多大?

每种颜色代表不同的数字,如下: 

   红   橙    黄    绿    蓝     紫   灰    白  黑,            金 5%  银 10% 表示误差 

倒数第二位 金0.1  0.01

红蓝黑对应阻值:27欧

3.共阴极数码管,要你设计一个和单片机相连的电路图,考的是38译码器

4.D触发器,实现2分频

5.实现加法器(模电实现)

   如下图所示,基本上加法器是个多输入端的反相器。如果用分析反相器同样的方法来分析这个电路,结果是 Vo= -(Va*Rf/Ra+Vb*Rf/Rb+Vc*Rf/Rc) 

 在所有电阻都相同的情况下,上述公式可简化为Vo=-(Va+Vb+Vc)。 输入端并不一定是三个,可以有两个,四个或更多个输入端。 

6.链表的创建和逆序

总体思路:

创建链表插入N个元素(头插法),将链表从头元素到尾元素一次插入另外的一个链表中,即实现了元素的逆序

原理:头插后元素在链表中已经逆序存储,要实现链表的逆序只需再插一遍

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

typedef struct _linkode_{
int data;
struct _linkode_ *next;
}Linknode;
Linknode *create_linknode()  //创建节点
{
Linknode *node = NULL;
node = (Linknode *)malloc(sizeof(Linknode));
node -> next = NULL;
return node;
}
Linknode *init_linknode()  //链表初始化
{
return  create_linknode();
}
int insert_linknode(Linknode *head,int data)  //链表插入函数
{
Linknode *new = NULL;
new = create_linknode();
new->data = data;
new->next = head ->next;
head->next = new;
return 0;
}
int display_linknode(Linknode *head)  //链表元素显示函数
{
int count = 0;
while(head->next != NULL)
{
printf("%d\t",head->next->data);
count ++ ;
head = head -> next;
}
putchar('\n');
return count;
}
int copy_insert(Linknode *head,Linknode *copy ,int count) //拷贝链表数据到新建链表
{
int i;
i = count;
for(i = 0;i < count ; i++ )
{
insert_linknode(copy,head->next->data);
head = head ->next;
}
return 0;
}
int main(int argc, const char *argv[])
{
Linknode *node,*node_copy;
int i =0;
node = init_linknode();
node_copy = init_linknode();
for(i = 0;i< 10; i++)
insert_linknode(node,i);
display_linknode(node);
copy_insert(node,node_copy,10);
display_linknode(node_copy);
return 0;
}



7.卡诺图化简,根据以下表达式画出卡诺图,化简表达式

表达式:

卡诺图及其化简后的表达式:

8. TTL和CMOS能否直接互联?

不可以,常用逻辑电平:12V5V3.3VTTLCMOS不可以直接互连,由于TTL是在0.3-3.6V之间,而CMOS则是有在12V的有在5V的。CMOS输出接到TTL是可以直接互连。TTLCMOS需要在输出端口加一上拉电阻接到5V或者12V



C基础:

1.#error的作用

#error命令是C/C++语言的预处理命令之一,当预处理器预处理到#error命令时将停止编译并输出用户自定义的错误消息。


2.定义整型数组,整型指针数组,数组指针,函数指针

整型数组 :int *a[10];

整型指针数组:int *p[10];

数组指针:int  (*p)[10];

函数指针:int (*p)(int parameter);


3.内存的分配方式

(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
3)从堆上分配,亦称动态内存分配。程序在运行的时候用mallocnew申请任意多少的内存,程序员自己负责在何时用freedelete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多


4.头文件的作用

1)通过头文件来调用库功能。

2)头文件能加强类型安全检查。

3)头文件可以提高程序的可读性(清晰性)。 

5.宏函数实现两个数的最小值;

#define min(a,b)  ((a) < (b)?(a):(b))

6.简述堆栈的区别

1、管理方式不同; 2、空间大小不同; 3、能否产生碎片不同; 4、生长方向不同; 5、分配方式不同; 6、分配效率不同;  

7.二叉树的三种遍历方法

先序,中序,后序

8.用宏定义一年能有多少秒的变量SECOND
#define SECOND  (365*24*3600)UL

9.简述中断的过程

中断请求中断判优中断响应中断处理和中断返回; 中断处理过程基本上由3部分组成,第一部分为准备部分,其基本功能是保护现场,对于非向量中断方式则需要确定中断源,最后开放中断,允许更高级的中断请求打断低级的中断服务程序;第二部分为处理部分,即真正执行具体的为某个中断源服务的中断服务程序;第三部分为结尾部分,首先要关中断,以防止在恢复现场过程中被新的中断请求打断,接着恢复现场,然后开放中断,以便返回原来的程序后可响应其他的中断请求。中断服务程序的最后一条指令一定是中断返回指令


10.一题链表的插入,编程题

#include<stdio.h>
#include<stdlib.h>
typedef struct _linkode_{
int data;
struct _linkode_ *next;
}Linknode;      // 定义链表结构体
Linknode *create_linknode()   //创建链表
{
Linknode *node = NULL;
node = (Linknode *)malloc(sizeof(Linknode));
node -> next = NULL;
return node;
}
Linknode *init_linknode()   //链表初始化,创建空节点
{
return  create_linknode();
}
int insert_linknode(Linknode *head,int data) //链表插入采用头插法
{
Linknode *new = NULL;
new = create_linknode();
new->data = data;
new->next = head ->next;
head->next = new;
return 0;
}
int display_linknode(Linknode *head)  //显示链表中所有元素
{
int count = 0;
while(head->next != NULL)
{
printf("%d\t",head->next->data);
count ++ ;
head = head -> next;
}
putchar('\n');
return count;
}
int main(int argc, const char *argv[])
{
Linknode *node,*node_copy;
int i =0;
node = init_linknode();
for(i = 0;i< 10; i++)
insert_linknode(node,i); 
display_linknode(node);
return 0;
}

11.冒泡法排序(编程题)

升序:

#include<stdio.h>
int main()
{
int a[10] = {10,8,9,7,6,4,5,3,1,2};
int i,j;
int temp;
for(i = 0;i <sizeof(a) - 1 ; i++)
{
for(j = 0;j < sizeof(a) -i -1;j ++ )
{
if(a[j] > a[j+1])
{
temp = a[j];
a[j] = a[j +1];
a[j +1] = temp ;
}
}
}
}


12. 整型变量val的第三位置零或置一

置零:val = val & (~ 0x0001<<3) | 0x0000

置一:val = val &  ( ~ 0x0001 << 3)  |  0x0008

13. 简述关键字static volatile const 的作用并举例

static作用:

1)隐藏。所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问,加了static,就会对其它源文件隐藏

2static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。

3static的第三个作用是默认初始化为0static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。

如main.c中

static int display()

{

printf("welcome to csdn\n");

}


同一个工程文件中其他源文件将不能访问display函数,应为函数前被static修饰

 volatile的作用:

 作为指令 关键字,确保本条指令不会因 编译器的优化而省略,且要求每次直接读值. 简单地说就是防止 编译器 对代码进行优化
i nt square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
因为函数传参是volatile类型的,故 它可能被意想不到地改变,由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值
const 的作用:
(1)可以定义const常量,具有不可变性。 
  例如:const int Max=100; int Array[Max]; 
  (2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
  例如: void f(const int i) { .........} 编译器就会知道i是一个常量,不允许修改; 
  (3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!
  如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可! 
  (4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在函数体内修改了i,编译器就会报错; 
  例如: void f(const int i) { i=10;//error! } 
  (5) 为函数重载提供了一个参考。 
  class A { ...... 
  void f(int i) {......} //一个函数 
  void f(int i) const {......} //上一个函数的重载 ...... 
  }; 
  (6) 可以节省空间,避免不必要的内存分配。 例如: 
  #define PI 3.14159 //常量宏 
  const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ...... 
  double i=Pi; //此时为Pi分配内存,以后不再分配! 
  double I=PI; //编译期间进行宏替换,分配内存 
  double j=Pi; //没有内存分配 
  double J=PI; //再进行宏替换,又一次分配内存! 
  const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。 
  (7) 提高了效率。 
  编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。


14.简化c = a+++b运算

c = (a ++) + b;

c = a + b;

a = a +1;

15.usigned int a = 10;int b = -20;计算(a + b> 10 ?puts(“*”):puts(“**”) ;

答案:*        //运算过程中int将强转为unsigned int

操作系统:

1. ucos_II在进入代码临界区之前是否要关中断?为什么?

UCOS_II在进入系统临界代码区之前要关闭中断,等到退出临界区后再打开 关中断可以避免其他任务或中断进入临界区代码。uC/OS-II定义了这两个宏来实现,但注意一条:调用uC/OS-II功能函数时,中断应该总是开着的。

 

2. ucos_II抢占式任务调度的特点

  ucos-II是基于任务优先级抢占式任务调度法的,就是内核在管理调度时,调用任务切换函数(一般为SSched()),在该函数中将此时已处于就绪状态(条件一)并且为最高优先级(条件二)的任务的保存于其栈中的相应信息压入cpu寄存器中(软中断完成),然后cpu开始运行该任务的代码。 

3. ucos_II进程运行的几种状态

就绪态、运行态、可中断的等待态、不可中断的等待态、僵尸态、终止态

4. ucos_II进程间通信

     1:通过全局变量 

    2:信号量、邮箱、消息队列(邮箱数组) 

备注:以上题目解答为个人处理,不能保证其绝对正确性,如有错误之处还望指正

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值