C常见笔试题(二)

第一章C语言常见问题

1.1关作键字static的用

这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。

2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3)在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

大多数应试者能正确回答第一部分,一部分能正确回答第二部分,但是很少的人能懂得第三部分。这是一个应试者的严重的缺点,

因为他显然不懂得本地化数据和代码范围的好处和重要性。

考点:在嵌入式系统中,要时刻懂得移植的重要性,程序可能是很多程序员共同协作同时完成,在定义变量及函数的过程,可能会重名,这给系统的集成带来麻烦,因此保证不冲突的办法是显示的表示此变量或者函数是本地的,static即可。

在Linux的模块编程中,这一条很明显,所有的函数和全局变量都要用static关键字声明,将其作用域限制在本模块内部,与其他模块共享的函数或者变量要EXPORT到内核中。

1)设置变量的存储域,函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

(2)限制变量的作用域,在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

(3)限制函数的作用域,在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

(4)在类中的static成员变量意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见;

(5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

 

1.2程序运行时的内存分配

一个由C/C++编译的程序占用的内存分为以下几个部分

1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式类似于链表。

3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。

4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放

5、程序代码区—存放函数体的二进制代码。

二、例子程序

这是一个前辈写的,非常详细

//main.cpp

int a = 0;全局初始化区

char *p1;全局未初始化区

main()

{

int b;栈

char s[] ="abc";栈

char *p2;栈

char *p3 ="123456"; 123456\0在常量区,p3在栈上。

static int c =0;全局(静态)初始化区

p1 = (char*)malloc(10);

p2 = (char*)malloc(20);分配得来得10和20字节的区域就在堆区。

strcpy(p1,"123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。

}

 

1.3堆和栈的区别

1)申请方式

stack:

由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间

heap:

需要程序员自己申请,并指明大小,在c中malloc函数

如p1 = (char *)malloc(10);

在C++中用new运算符

如p2 = (char *)malloc(10);

但是注意p1、p2本身是在栈中的。

堆是向上增长而栈是向下增长

2)堆栈的进一步讨论

http://blog.csdn.net/kkxgx/article/details/7520902

在学习《深入理解计算机系统》中链接这一章中,数据讲一个可执行文件包含多个段。在Linux系统中代码段总是从0x08048000处开始,数据段在接下来的4KB对齐的地址处,运行时堆在接下来的读写段之后的第一个4KB对齐的地址处,

并通过调用malloc库网上增长,开始于地址0x40000000处的段是为共享库保留的,用户栈总是从地址0xbfffffff处开始,并向下增长,从栈的上部开始于地址0xc0000000处的段是为操作系统驻留存储器部分的代码和数据保留的。如下图:

 

 

 

 

下面通过代码来测试:

#include<stdio.h>

int a1=0;

staticintsa=0;

int a2=0;

int b;

int main()

{

     int c1=1;

     int c2;

     int *d=(int *)malloc(sizeof(int)*10);

     int *e=(int *)malloc(sizeof(int)*10);

     staticint f=0;

     if(d==NULL)

     {

            printf("malloc error");

            return -1;

     }

     printf("a1:%p\n",&a1);

     printf("sa:%p\n",&sa);

     printf("a2:%p\n",&a2);

     printf("b:%p\n",&b);

     printf("c1:%p\n",&c1);

     printf("c2:%p\n",&c2);

     printf("d:%p\n",&d);

     printf("*d:%p\n",d);

     printf("e:%p\n",&e);

     printf("*e:%p\n",e);

     printf("f:%p\n",&f);

     printf("%p\n",&"123");

     free(d);

     return 0;

}

分析如下:

a1,a2,sa,f是按序存放,且全局共有非静态变量和静态变量是分开存放的,紧接着存放的是全局非初始化变量b,由于是int型,故地址相差4.

c1,c2,d,e这几个变量存储在用户栈空间。主要区分d、e和d、e申请内存之间的区别。

*d,*e代表申请的内存,地址是增大方向,即地址是向上增长的,为堆中申请的数据。

“123”为常量,地址0x80486da应该在只读段的范围内,属于rodate数据。

故可知:C程序中,全局变量和静态变量存储在读写段,常量存储在制度段,动态申请的内容是在堆上,局部变量在运行时栈中。

 

1.4关键字const的含义

只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)

 

如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?

 

Const只是一个修饰符,不管怎么样a仍然是一个int型的变量

constint a;

int const a;

constint *a;

int * const a;

int const * aconst;

本质:const在谁后面谁就不可修改,const在最前面则将其后移一位即可,二者等效

 

前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,指向的整型数是不可修改的,但指针可以,此最常见于函数的参数,当你只引用传进来指针所指向的值时应该加上const修饰符,程序中修改编译就不通过,可以减少程序的bug)。

第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

 

const关键字至少有下列n个作用:

 

(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;

(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;

(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;

(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:

constclassAoperator*(const classA& a1,const classA& a2);

operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:

classA a, b, c;

(a * b) = c; //对a*b的结果赋值

  操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。

 

1.5关键字volatile含义

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

 

1.6什么是野指针?产生的原因?

“野指针”不是NULL指针,是指向“垃圾”内存(不可用内存)的指针。“野指针”是很危险的,if无法判断一个指针是正常指针还是“野指针”。有个良好的编程习惯是避免“野指针”的唯一方法。

野指针的成因主要有三种:

一、指针变量没有被初始化。指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。

三、指针操作超越了变量的作用范围。比如不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。比如说某个地址的生命期,使用一个没有生命期的指针是非常危险的。

 

1.7 sizeof和strlen区别

一、sizeof

sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。

二、strlen

strlen(...)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。

它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。

三、举例:

eg1、char arr[10] = "What?";

intlen_one =strlen(arr);

intlen_two =sizeof(arr);

cout<<len_one<<" and " <<len_two<<endl;

输出结果为:5 and 10

点评:sizeof返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据。strlen只关心存储的数据内容,不关心空间的大小和类型。其中\0不计入strlen计数中去。

 

第二章 C语言的常见笔试题

2.1 在某工程中,要求设置一绝对地址为

嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

int*ptr;

ptr =(int *)0x67a9;

*ptr =0xaa55;

 

2.2 写出strcpy,strcmp,strlen,memcpy函数源码的实现。

char * strcpy( char*strDest, const char *strSrc )

{

assert( (strDest !=NULL) && (strSrc != NULL) );

char *address =strDest;

while( (*strDest++= * strSrc++) !=‘\0’ );

return address;

}

 

intstrlen( constchar *str ) //输入参数const

{

assert(strt != NULL); //断言字符串地址非0

intlen;

while( (*str++) !='\0' )

{

len++;

}

returnlen;

}

 

 

 

2.3 请写一个C函数,判断大小端。

所谓的大端模式,是指数据的高位,保存在内存的低地址中,而数据的低位,保存在内存的高地址中

所谓的小端模式,是指数据的高位保存在内存的高地址中,而数据的低位保存在内存的低地址中

intcheckCPU()

{

{

union w

{

int a;

char b;

} c;

c.a = 1;

return (c.b == 1);

}

}

2.4用变量a 给出下面的定义

a) 一个整型数(An integer)

b)一个指向整型数的指针(A pointer to an integer)

c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to anintege)r

d)一个有10 个整型数的数组(An array of 10 integers)

e) 一个有10 个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers tointegers)

f) 一个指向有10 个整型数数组的指针(A pointer to an array of 10integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer asan argument

and returns an integer)

h)一个有10 个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers tofunctions t

hat take an integer argument and return aninteger )

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to aninteger

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers to integers

f) int (*a)[10]; // A pointer to an array of 10integers

g) int (*a)(int); // A pointer to a function athat takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointersto functions that take an integer argument and return an integer

2.5 位操作

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

#define BIT3 (0x1 << 3)

staticint a;

void set_bit3(void) {

a |= BIT3;

}

void clear_bit3(void) {

a&= ~BIT3;

}#define BIT3 (0x1 << 3)

staticint a;

void set_bit3(void) {

a |= BIT3;

}

void clear_bit3(void) {

a&= ~BIT3;

}

 

2.6 与“零值”比较

分别给出BOOL,int,float,指针变量与“零值”比较的 if语句(假设变量名为var)

解答:

BOOL 型变量:if(!var)

int型变量:if(var==0)

float 型变量:

const floatEPSINON = 0.00001;

if ((x >= -EPSINON) && (x <= EPSINON)

指针变量:if(var==NULL)

 

2.7 对齐方式

·使用伪指令#pragma pack (n),编译器将按照n 个字节对齐;

·使用伪指令#pragma pack (),取消自定义字节对齐方式。

注意:如果#pragma pack (n)中指定的n 大于结构体中最大成员的size,则其不起作用,结构体仍然按照size 最大的成员进行对界。

另外,通过__attribute((aligned (n)))也可以让所作用的结构体成员对齐在n 字节边界上。

 

2.8 malloc分配的是物理地址还是虚拟地址

物理地址

 

2.9 不是使用变量,调换两个变量的值

int a=10,b=20;a=a+b;b=a-b;a=a-b;结果:a=20;b=10;

 

第三章 C++常见问题

1.      memcpy源码

void *  memcpy (

        void * dst,

        const void * src,

        size_t count ){

        void * ret = dst;

             if((dst == NULL)||(src == NULL)||(count== 0))

                    return NULL;

        while (count--) {

                *(char *)dst = *(char *)src;

                dst = (char *)dst + 1;

                src = (char *)src + 1;

        }

        return(ret);

}

 

也许这并不是最佳答案,可是我也懒不想写太多,至少应该转换成int这样可以提供效率。

 

2.    冒泡排序

#define LEN 8

int a[LEN]={5,4,3,2,1,7,6,0};

intbubble_new_sort(void)

{

      int i=0; int j = 0;     int k = 8;      int flage = 1; int tmp  =0;

    for(i=1;(i<k)&&(flage == 1);i++){

             flage = 0;

             for(j=0;j<k-i;j++){

                 if(a[j]>a[j+1]){

                      tmp =a[j];

                      a[j] = a[j+1];

                      a[j+1] = tmp;

                      flage=1 ;

                    }

             }

       printf("i = %d : %d,%d,%d,%d,%d,%d,%d,%d\n",i,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);

}

      printf("%d,%d,%d,%d,%d,%d,%d,%d\n",a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]);

    return 0;

}

 

3.    进程和线程的区别

进程是表示资源分配的基本单位,又是调度运行的基本单位。

线程是进程中执行运算的最小单位,亦即执行处理机调度的基本单位。

 

4.    const char * char * const

 

5.  请问C++的类和C 里面的struct 有什么区别?

c++中的类具有成员保护功能,并且具有继承,多态这类oo特点,而c 里的struct 没有

 

6.  交换两个数,不用第三块儿内存:

int a =  2;

int b = 3

a = a + b;

b = a - b;

a = a - b;

或者:

a = a ^ b;

b = a ^ b;

a = a ^ b;

 

7.    二分查找思想

int binary_search(int* a, int len, int goal)

{

    int low = 0;

    int high = len - 1;

    while(low <= high)

    {

        int middle = (low +high)/2;

        if(a[middle] == goal)

            return middle;  //在左半边

        else if(a[middle] >goal)

            high = middle - 1;//在右半边

        else

            low = middle + 1;

    }

    return -1; //没找到

}

 

8.    free

一个指针被free 后 内存被释放,但并不是空的,指向的位置不确定。

 

第五章 C&C++

5.1 C和C++做程序的区别?

1.C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。

2.C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“设计这个概念已经被融入到C++之中”。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值