Chinaunix首页 | 论坛 | 博客
  • 博客访问: 181899
  • 博文数量: 22
  • 博客积分: 1069
  • 博客等级: 准尉
  • 技术积分: 240
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-01 13:40
文章分类

全部博文(22)

文章存档

2015年(4)

2011年(2)

2010年(12)

2009年(4)

我的朋友

分类: C/C++

2010-01-10 18:33:49

==============================================
函数只定义了一次, 调用了一次, 但编译器提示非法重定义了。
在范围内没有声明就调用(可能是第一次调用在函数的定义之前)的函数被认
为返回整型(int)(且没有任何参数类型信息),如果函数在后边声明或定义成其它
类型就会导致矛盾。所有函数(非整型函数一定要)必须在调用之前声明。
另一个可能的原因是该函数与某个头文件中声明的另一个函数同名。
==============================================
定义一个包含N个指向返回指向字符的指针的函数的指针的数组?
这个问题至少有以下答案:
1.  char *(*(*a[N])())();
2.  用typedef 逐步完成声明:
typedef  char  *pc;         /*  字符指针         */
typedef  pc  fpc();         /*  返回字符指针的函数 */
typedef  fpc  *pfpc;        /*  上面函数的指针    */
typedef  pfpc  fpfpc();     /*  返回函数指针的函数 */
typedef  fpfpc  *pfpfpc;    /*  上面函数的指针    */
pfpfpc  a[N];               /*  上面指针的数组    */
==============================================

局部变量能否和全局变量重名吗?

能,局部会屏蔽全局。要用全局变量,需要使用"::",局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

 

如何引用一个已经定义过的全局变量?

Extern,可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个函数名写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

 

全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错

 

C中什么数据分配在栈或堆中,malloc分配数据是在近堆还是远堆中?

存放局部变量,函数调用参数函数返回值,函数返回地址。

由系统管理堆程序运行时动态申请的空间,malloc申请的内存就在堆上

 

结构与联合有和区别?

1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 

2. 对于联合的不同成员赋值将会对其它成员重写,  原来成员的值就不存在了而对于结构的不同成员赋值是互不影响的。

 

程序的局部变量存在于()中,全局变量存在于()中,动态申请数据存在于()中?

栈、静态区、堆。

 

队列和栈有什么区别?

队列先进先出,栈后进先出

 

描述内存分配方式以及它们的区别?

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

 

为什么标准头文件都有类似的结构? (#ifndef ... #define ... #endif)

头文件中的编译宏的作用是防止被重复引用。

==============================================

定义空指针:

#define NULL ((void *)0)

用来定义无效的指针

(void *)0 就是将0强制转化为(void *)类型的指针

char *ch = (void *)0;//ch指向地址

==============================================
volatile的作用说明(一):

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 
    1). 并行设备的硬件寄存器(如:状态寄存器) 
    2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 
    3). 多线程应用中被几个任务共享的变量 
    回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
    假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 
    1). 一个参数既可以是const还可以是volatile吗?解释为什么。 
    2). 一个指针可以是volatile 吗?解释为什么。 
    3). 下面的函数有什么错误: 
         int square(volatile int *ptr) 
         { 
              return *ptr * *ptr; 
         } 
    下面是答案: 
    1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 
    2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 
    3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: 
    int square(volatile int *ptr)  
    { 
         int a,b; 
         a = *ptr; 
         b = *ptr; 
         return a * b; 
     } 
    由于*ptr的值可能被意想不到地该变,因此ab可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: 
     long square(volatile int *ptr)  
     { 
            int a; 
            a = *ptr; 
            return a * a; 
     } 

==============================================

volatile的作用说明(二):

volatile的本意是“易变的”  
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如: 

static int i=0; 

int main(void) 

    ... 
    while (1) 
   { 
       if (i) dosomething(); 
   } 


/* Interrupt service routine. */ 
void ISR_2(void) 

   i=1; 


程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导dosomething永远也不会被调用。如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。 

一般说来,volatile用在如下的几个地方: 

1、中断服务程序中修改的供其它程序检测的变量需要加volatile; 

2、多任务环境下各任务间共享的标志应该加volatile; 

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义; 

另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

=============================================

volatile的作用说明(三):

volatile 的含义
volatile总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优化不是程序所需要的,这时可以用volatile关键字禁止做这些优化,volatile的字面含义是易变的,它有下面的作用: 

不会在两个作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下,变量可能被其他的程序改变,编译器自己无法知道,volatile就是告诉编译器这种情况。 
不做常量合并、常量传播等优化,所以像下面的代码: 
    volatile int i = 1; 
    if (i > 0) ...   //if的条件不会当作无条件真。 
volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO的处理是不能这样优化的。 

前面有人说volatile可以保证对内存操作的原子性,这种说法不大准确,其一,x86需要LOCK前缀才能在SMP下保证原子性,其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc 

对于jiffies,它已经声明为volatile变量,我认为直接用jiffies++就可以了,没必要用那种复杂的形式,因为那样也不能保证原子性。 

你可能不知道在Pentium及后续CPU中,下面两组指令 

inc jiffies 
;; 
mov jiffies, %eax 
inc %eax 
mov %eax, jiffies 

作用相同,但一条指令反而不如三条指令快。 

==============================================
volatile的作用说明(四):

补充: volatile应该解释为直接存取原始内存地址比较合适,易变的这种解释简直有点误导人;  

易变是因为外在因素引起的,象多线程,中断等,并不是因为用volatile修饰了的变量就是易变了,假如没有外因,即使用volatile定义,它也不会变化; 

而用volatile定义之后,其实这个变量就不会因外因而变化了,可以放心使用了; 大家看看前面那种解释(易变的)是不是在误导人 

 

volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。 
使用该关键字的例子如下: 
int volatile nVint; 
>>>>当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 
例如: 
volatile int i=10; 
int a = i; 
... 
//其他代码,并未明确告诉编译器,对i进行过操作 
int b = i; 
>>>>volatile 指出 i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。 
>>>>注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响: 
>>>>首先,用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码: 
>> 
#include  
void main() 

      int i=10; 
      int a = i; 
      printf("i= %d",a); 
     //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 
      __asm

      { 
             mov dword ptr [ebp-4], 20h 
      } 
      int b = i; 
      printf("i= %d",b); 
}       
然后,在调试版本模式运行程序,输出结果如下: 
i = 10 
i = 32 
然后,在release版本模式运行程序,输出结果如下: 
i = 10 
i = 10 
输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。下面,我们把 i的声明加上volatile关键字,看看有什么变化: 
#include  
void main() 

         volatile int i=10; 
         int a = i; 
         printf("i= %d",a); 
         __asm 

         { 
                mov dword ptr [ebp-4], 20h 
         } 
         int b = i; 
         printf("i= %d",b); 
}       
分别在调试版本和release版本运行程序,输出都是: 
i = 10 
i = 32 
这说明这个关键字发挥了它的作用!

 

典型的例子 
       for ( int i=0; i<100000; i++); 
这个语句用来测试空循环的速度的 
但是编译器肯定要把它优化掉,根本就不执行 
如果你写成  
       for ( volatile int i=0; i<100000; i++); 
它就会执行了

==============================================

static关键字至少有下列n个作用:
  (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  (2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  (3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

     static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

     static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

     static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
=============================
const关键字至少有下列n个作用:
  (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
  (2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const
  (3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

==============================================
用变量a给出下面的定义
a) 一个整型数(An integer) int a;
b) 一个指向整型数的指针(A pointer to an integer) int *a;
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer) int **a;
d) 一个有10个整型数的数组(An array of 10 integers) int a[10];
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers) int *a[10];
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) int (*a)[10];
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数 int (*a)(int);
  (A pointer to a function that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 int(*a[10])(int);
  (An array of ten pointers to functions that take an integer argument and return an integer )
==============================================
Heap与stack的差别。
  Heap是堆,stack是栈。
  Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
  Stack空间有限,Heap是很大的自由存储区
  C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。
  程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
==============================================
linux系统:
1.列举几种进程的同步机制,并比较其优缺点。
  原子操作
  信号量机制
  自旋锁
  管程,会合,分布式系统
2.进程之间通信的途径
  共享存储系统
  消息传递系统
  管道:以文件系统为基础
3.进程死锁的原因
  资源竞争及进程推进顺序非法
4.死锁的4个必要条件
  互斥、请求保持、不可剥夺、环路
5.死锁的处理
  鸵鸟策略、预防策略、避免策略、检测与解除死锁
6.操作系统中进程调度策略有哪几种?
  FCFS(先来先服务),优先级,时间片轮转,多级反馈
7.数组和链表的区别
  数组:数据顺序存储,固定大小
  链表:数据可以随机存储,大小可动态改变
===========================================

进程对应的内存空间中所包含的5种不同的数据区都是干什么的

  1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像。代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作——它是不可写的。 

  2)数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。 

3BSS段:BSS段包含了程序中未初始化的全局变量,在内存中 bss段全部置零。

(BSS“Block Started by Symbol”的缩写,意为以符号开始的块

  4)堆(heap):堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) 

  它的物理内存空间是由程序申请的,并由程序负责释放。 

  5)栈:栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。 

  它是由操作系统分配的,内存的申请与回收都由OS管理。

==============================================

进程和线程的差别

线程是指进程内的一个执行单元,也是进程内的可调度实体.

与进程的区别:

(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位

(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行

(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于

进程的资源.

(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销

明显大于创建或撤消线程时的开销。

阅读(2411) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~