C考题

目录:

(1)位操作

(2)静态分配和动态分配内存的区别

(3)宏定义的嵌套

(4)宏定义的修改

(5)define宏定义和const常变量区别:

(6)利用宏定义计算结构体中元素偏移量

(7)调用运算符(下面三种等价)

(8)定义函数指针数组,并给数组赋值。

(9)字符指针与字符数组:

(10)C函数之memcpy()与strcpy()函数用法

(11)嵌入式计算机系统的最小组成

(12)引用与指针的区别

正文:


(1)位操作

1.统计一个数的二进制数中1的个数。利用x=x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1会将该位变为0.

 int Count(int x)
{ int sum=0;
    while(x)
    { sum++;
        x=x&(x-1);
    }
    return sum;

}

或者:

int Countone(int i)

{

int count=0;

while(i)

{

if(i&0x01)

count++;

i>>1;

}

return count;

}


(2)静态分配和动态分配内存的区别

要弄懂这个问题,首先你得知道静态和动态指的是什么。个人觉得卡耐基上的解释很经典:

     “The word static refers to things that happen at compile time and link time when the program is constructed—as opposed to load time or run time when the program is actually started.”

     “The term dynamic refers to things that take place when a program is loaded and executed. ”

      说白了,内存的静态分配和动态分配的区别主要是两个:

        一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。

      二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。    

       对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。

        一般,用static修饰的变量,全局变量位于静态数据区。函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。

 

 

其它:

所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

例如我们定义一个float型数组:float score[100];   

但是,在使用数组的时候,总有一个问题困扰着我们:数组应该有多大?在很多的情况下,你并不能确定要使用多大的数组,比如上例,你可能并不知道我们要定义的这个数组到底有多大,那么你就要把数组定义得足够大。这样,你的程序在运行时就申请了固定大小的你认为足够大的内存空间。即使你知道你想利用的空间大小,但是如果因为某种特殊原因空间利用的大小有增加或者减少,你又必须重新去修改程序,扩大数组的存储范围。这种分配固定大小的内存分配方法称之为静态内存分配。但是这种内存分配的方法存在比较严重的缺陷,特别是处理某些问题时:在大多数情况下会浪费大量的内存空间,在少数情况下,当你定义的数组不够大时,可能引起下标越界错误,甚至导致严重后果。

我们用动态内存分配就可以解决上面的问题. 所谓动态内存分配就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。从以上动、静态内存分配比较可以知道动态内存分配相对于景泰内存分配的特点:

   1、不需要预先分配存储空间;

   2、分配的空间可以根据程序的需要扩大或缩小。

要实现根据程序的需要动态分配存储空间,就必须用到malloc函数.

malloc函数的原型为:void *malloc (unsigned int size) 其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。

 

原文地址:http://hi.baidu.com/wu_yuzhi/blog/item/8bafc18a4922c2759f2fb4cb.html


(3)宏定义的嵌套

 嵌套的宏定义,就是用定义过的宏名去定义另一个宏名。例如:

        #define WIDTH 80

        #define LENGTH (WIDTH+40)

在第二个宏定义中,使用了前面定义过的宏名WIDTH。在编译预处理时,程序中所有的WIDTH都被80所替换,所有的LENGTH又被(80+40)替换。如果程序中出现了如下语句:

        var=LENGTH*20;

经过替换以后变为:

        var=(80+40)*20;

但是如按以下方式定义:

        #define WIDTH 80

        #define LENGTH WIDTH+40

        var=LENGTH*20;

则经过编译预处理后变成

        var=80+40*20;  

    就是说,宏替换只是简单地用定义的宏体去替换宏名而不进行任何计算。因此,宏定义中若出现表达式时,园括号的有无,效果明显不同。为了保证定义在置换后仍保持正确的运算顺序,经常在定义中使用必要的圆括号将字符串括起来。


(4)宏定义的修改

1.宏本身的值可以通过#undef和#define重新定义,或者用条件编译也可以。(用#undef命令终止宏定义的作用域 )

    宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。-------不一定吧/??????
如:
#include <stdio.h>
int  main(  void  )
{
#define MAX 200
     printf ( "MAX= %d\n" ,MAX);
#undef MAX
#define MAX 300
     printf ( "MAX= %d\n" ,MAX);
     return  0;
}

2.#define   max(a,b)   ((a)   >   (b)   ?   (a)   :   (b))  

参考: #undef :http://baike.baidu.com/link?url=JCwcBUrDsVROi7J8ESW08ieJUcKRkG5nGbJmrDUZFkeC5P_1o6Prja5VPzZBsi1w0MeU-BhjlFNfyMWEEq4Op_

3 .宏定义冲突

C语言编程中,对一个比较大型的项目,用到的库会比较多,如果设计的稍有疏忽,便可能产生宏定义的冲突。项目中有一个例子就是发生在两个头文件都定义了“ISSPACE(ch)”这个宏,并且两个宏定义不太一致,从而导致了整个工程无法编译。

解决该问题的方法是将该宏undefine,具体的做法是,在紧邻include语句之后对有冲突的宏进行undefine。例如库liba.h和头文件 app_pub.h就有对ISSPACE (ch)的不同定义,如果同时使用这两个文件,就会产生宏定义的冲突,这时,可以将其一个undefine(一个前提是,不是两个头文件定义的宏都会用到):

#include "app_pub.h"
#ifdef ISSPACE 
#undef ISSPACE
#endif

这样的方法是解决宏定义冲突的一个无奈之举。事实上,宏定义的作用域早就被人诟病,特别放在头文件中,被数次包含之后,在不经意间又被覆盖,然后又被重新使用,也许此时已经不是原来的意思了,而我们在使用时还不知道。

为此,有一个简单的原则可以减少宏定义的冲突,那就是尽量不要将宏定义置于头文件当中,除非有一个非将其置于头文件不可的理由。除此之外,还有一个策略 是,如果一个文件/库的某个函数不会被其他地方所使用,那么就不要将其置于头文件当中(因为宏的作用域是当前文件,不管是在函数还是头部定义,都是作用于当前文件)。头文件应该是接口,而不是大杂烩。

对于c++开发者尽量不要使用宏,因为const定义常量和inline定义函数都优于宏定义,所以在<<effective c++>>中都推荐使用其他c++特性去代替宏。

对于c开发者而言,宏是一个不错的选择,特别是宏里面的#ifdef #undef #else等,还有就是可以自定义变量#define MERGR(x,y) class##x##y能使用MERGR(First,Val)合成标识符classFirstVal。这些特性有助于开发大型项目,但是有些功能谨慎使用。

(5)define宏定义和const常变量区别:
1.define是宏定义,程序在预处理阶段将用define定义的内容进行了替换。因此程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。const定义的常量,在程序运行时在常量表中,系统为它分配内存。
2.define定义的常量,预处理时只是直接进行了替换。所以编译时不能进行数据类型检验。const定义的常量,在编译时进行严格的类型检验,可以避免出错。3.define定义表达式时要注意“边缘效应”,例如如下定义:
#define N 2+3 //我们预想的N值是5,我们这样使用N,int a = N/2; //我们预想的a的值是2.5,可实际上a的值是3.5原因在于在预处理阶段,编译器将 a = N/2处理成了 a = 2+3/2;这就是宏定义的字符串替换的“边缘效应”因此要如下定义:#define N (2+3)。const定义表达式没有上述问题。const定义的常量叫做常变量原因有二:const定义常量像变量一样检查类型;const可以在任何地方定义常量,编译器对它的处理过程与变量相似,只是分配内存的地方不同。

参考:http://www.cnblogs.com/kevinGaoblog/archive/2012/04/16/2452668.html


(6)利用宏定义计算结构体中元素偏移量

//OFFSETOF(s, m)的宏定义,s是结构类型,m是s的成员,求m在s中的偏移量。
#define OFFSET(s, m) ((size_t) &((s *)0)->m)

转自:
http://hi.baidu.com/tian_20032242/blog/item/77fd7afa5ffcc29d59ee90ba.html

#define OFFSETOF(type, field) ((size_t)&(((type *)0)->field))

(type *)0:把0地址当成type类型的指针。

((type *)0)->field:对应域的变量。

&((type *)0)->field:取该变量的地址,其实就等于该域相对于0地址的偏移量。

(size_t)&(((type *)0)->field):将该地址(偏移量)转化为size_t型数据。

ANSI C标准允许任何值为0的常量被强制转换成任何一种类型的指针,并且转换结果是一个NULL指针,因此((s*)0)的结果就是一个类型为s*的NULL指 针。如果利用这个NULL指针来访问s的成员当然是非法的,但&(((s*)0)->m)的意图并非想存取s字段内容,而仅仅是计算当结构 体实例的首址为((s*)0)时m字段的地址。聪明的编译器根本就不生成访问m的代码,而仅仅是根据s的内存布局和结构体实例首址在编译期计算这个(常 量)地址,这样就完全避免了通过NULL指针访问内存的问题。

参考:http://blog.csdn.net/wu936754331/article/details/49799471


(7)调用运算符(下面三种等价)

     指针->成员

     对象.成员

   (*指针).成员


(8)定义函数指针数组,并给数组赋值。
     int  (*fun[10]) (int);     定义储存10个函数指针的数组,且函数返回值和参数都是整型数。   
     fun[0] = function0;

(9)字符指针与字符数组:

参考:http://blog.csdn.net/qiumm/article/details/5657120

      http://c.biancheng.net/cpp/html/80.html


(10)C函数之memcpy()与strcpy()函数用法

参考:http://blog.csdn.net/tigerjibo/article/details/6841531


(11)嵌入式计算机系统的最小组成 

嵌入式计算机系统的最小组成进行介绍:

  一、  硬件层  
  硬件层中包含嵌入式微处理器、存储器(SDRAM、ROM、Flash等)、通用设备接口和I/O接口(A/D、D/A、I/O等)。在一嵌入式处理器基础上添加电源电路时钟电路和存储器电路,就构成了一个嵌入式核心控制模块。其中操作系统和应用程序都可以固化在ROM中;
  二、  中间层  
  硬件层与软件层之间为中间层,也称为硬件抽象层(Hardware Abstract Layer,HAL)或者板级支持包(Board Support Package,BSP),它半系统上层软件与底层硬件分离开来,使系统的底层驱动程序与硬件无关,上层软件开发人员无需关心底层硬件的具体情况,根据BSP层提供的接口即可进行开发。该层一般包含相关底层硬件的初始化、数据的输入/输出操作和硬件设备的配置功能。  实际上,BSP是一个介于操作系统和底层硬件之间的软件层次,包括了系统中大部分与硬件联系紧密的软件模块。设计一个完整的BSP需要完成两部分工作:嵌入工系统的硬件初始化的BSP功能,设计硬件相关的设备驱动;
  三、  系统软件层  
  系统软件层由实时多任务操作系统(Real-time Operation System,RTOS)、 文件系统、图形用户接口(Graphic User Interface,GUI)、网络系统及通用组件模块组成。RTOS是嵌入式应用软件的基础和开发平台
(12)引用与指针的区别
参考:http://blog.csdn.net/thisispan/article/details/7456169
 
 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值