经典c语言笔试题

时间:2022-06-01 21:37:24

***********************************************************************************************************************************                             编程区:

                     《《《《冒泡排序,二分查找,选法,的考察》》》》


100、

有两个磁盘文件A和B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列),输出到一个新文件C中.

#include<stdio.h>

#include<stdlib.h>
int main(int argc,char* argv[])
{
  FILE* fp;
  int i,j,k,num,NUM;
  char c[50],t,ch;
 if((fp=fopen("A","r"))==NULL)
     /*can be replaced by open
      * int fd=open("A",O_RDONLY|O_CREAT);*/

      {
          printf("fileA cannot be opened\n");
          exit(0);
      }
 printf("\nA contents are:\n");
 for(i=0;(ch=fgetc(fp))!=EOF;i++)/*一个字符一个字符读*/
   {
         c[i]=ch;
         putchar(c[i]);
   }
 num=i+1;
 fclose(fp);

if((fp=fopen("B","r"))==NULL)
   {
       printf("fileB cannot be opened\n");
       exit(0);
   }
 printf("\nB contents are :\n");
for(i=0;(ch=fgetc(fp))!=EOF;i++)
  {
      c[num+i]=ch;
      putchar(c[num+i]);
   }

fclose(fp);

NUM=num+i+1;
for(k=0;k<NUM-1;k++)/*冒泡排序*/
  {
    for(j=0;j<NUM-k-1;j++)
    {
        if(c[j]>c[j+1])
              {
           t=c[j];
           c[j]=c[j+1];
           c[j+1]=t;
                }
    }
  }

printf("\nC fileis:\n");
fp=fopen("C","w");
for(i=0;i<NUM;i++)
{
    putc(c[i],fp);/*将字符一个个写入文件中*/
    putchar(c[i]);/*一个个输出字符*/
}
fclose(fp);
return 1;
}


86.有一浮点型数组A,用C语言写一函数实现对浮点数组A进行降序排序,并输出结果,要求要以数组A作为函数的入口.(建议用冒泡排序法)

#include<stdio.h>
#include<stdlib.h>
void BubbleSort(int arr[], int n)
{
  int i,j;
  int exchange = 1;//交换标志,提高算法效率;
  int temp;
  for(i=0;i<n-1;i++)
              {
       exchange=0;//本趟排序开始前,交换标志应为假
       for(j=0;j<n-i-1;j++)
                         {
            if(arr[j+1] > arr[j])
                                      {
                 temp=arr[j+1];
                 arr[j+1]=arr[j];
                 arr[j]=temp;
                 exchange=1; //发生了交换,故将交换标志置为真
                                         }
                   }
       if(!exchange) //本趟排序未发生交换,提前终止算法
        return;
          }
}
int main(int argc,char* argv[])
  {
   int arr[5]={1,4,2,6,5};
   int i;
   BubbleSort(arr, 5);
   printf("after sort,arr is :\n");
   for(i=0;i<5;i++)
            {
       printf("%3d",arr[i]);
              }
   return 1;
   }

77.写出二分查找的代码:

  Int binary_search(int* arr,int key,int size)

{

     Intmid;

     Intlow=0;

Int high=size-1;

While(low<=high)

     {

       Mid=(low+high)/2;

       If(arr[mid]>key)

           High=mid-1;

       ElseIf(arr[mid]<key)

           Low=mid+1;

     Else

       Return mid;

}

  Return -1;

}

补充1:用帅选法查找100之内的质数

#include <iostream>
using namespace std;
#define N  100
int main()
{
       /*0~100共101个数*/
   int sieve[N + 1];
   int i;
    //step 1:初始化(sieve[i] = 0 表示不在筛中,即不是质数;1表示在筛中)
    sieve[0]=sieve[1]=0;
    for(int i = 2; i <= N; i++)
                    {
            sieve[i] = 1;
                     }

    //step 2:偶数(2的倍数)肯定不是质数,所以应该先筛除
    for(i = 2; i <= N / 2; i++)
                     {
                sieve[i * 2] = 0;
                      }
    int p = 2; //第一个质数是2
    //step 3:从sieve中删去P的倍数
    while(p * p <= N)
                {
        p = p + 1;    //选下一个p
        while(sieve[p] == 0)
                              {
                       p++;
                               }
        int t = p * p;
        int s = 2 * p;/*质数与质数之和包含合数,但质数于合数之和必为质数,提高算法效率*/
        while(t <= N)
                              {
               sieve[t] = 0;  //删除
               t = t + s;
                               }
                   }
    //step 4: 输出结果
 for(i = 2; i <= N; i++)
      {
      if(sieve[i] != 0)
                        {
            cout<<i<<", ";
                        }
       }
 return 1;
}

                                  《《《《链表操作考察》》》》
87、实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。

//删除操作

StatusListDelete_DuL(DuLinkList&  L,int i,ElemType&  e)

{

if(!(p=GetElemP_DuL(L,i)))

return   ERROR;//容错判断;

e=p->data;

p->prior->next=p->next;

p->next->prior=p->pror;

free(p);

p=NULL;//勿忘,否则内存泄露

return OK;

}

//插入操作

StatusListInsert_DuL(DuLinkList&L,inti,ElemType&e)

{

if(!(p=GetElemP_DuL(L,i)))

return   ERROR;

if(!(s=(DuLinkList)malloc(sizeof(DuLNode))))

 return  ERROR;

               /*assert((s=(DuLinkList)malloc(sizeof(DuLNode)))!=NULL)*/

s->data=e;

s->prior=p;

p-> next -> prior =s;

p->next=s;

s->next=p->next->next;

return  OK;}

88、把一个链表反向。//链表头插法;

intre_Link(Linklist H)

{

       Linklist p=H->next,q;

       H->next=NULL;

       while(p!=NULL)

       {

              q=p;

              p=p->next;

             q->next=H->next;

              H->next=q;

       }

       return 0;

}

                                                      《《《《strcpy 和memcpy》》》》

76.已知strcpy函数的原型是char *strcpy(char*strDest, const char *strSrc);其中strDest是目的字符串,strSrc是源字符串。

(1)不调用C++/C 的字符串库函数,请编写函数strcpy。

char* stringcpy(char* Des,const char* Src)

{

assert((Des!=NULL) && (Src!=NULL));

char* address=Des;

while((*Des++=*Src++)!='\0');

return address;
}

断言assert是一个宏,该宏在<assert>中,

当使用assert时候,给他个参数,即一个判读为真的表达式。

预处理器产生测试该断言的代码,如果断言不为真,则发出一个错误信息告诉断言是什么以及它失败一会,程序会终止。我们一般可以用在判断某件操作是否成功上。

详见《高质量c&c++编程,林锐,6.5章》

(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值?

 为了实现链式表达式:

        int len= strlen(stringcpy(Des,"hello"));

内存复制:

void* memcpy(void* pvTo, constvoid* pvFrom, size_tsize)

{

assert((pvTo!= NULL) &&(pvFrom!= NULL));

byte* pbTo= pvTo;

byte* pbFrom= pbFrom;

while (size--> 0)

{

*pbTo++ = *pbFrom++;

}

return pvTo;

}

注意:内存拷贝时要避免内存空间重叠的问题,(即pvfrom与pvto所指向的内存不能重叠)

为了防止内存空间重叠,若是目标地址高于源地址,从后往前复制;

若是源地址高于目标地址,从前往后复制;

                                                   《《《《查找字符串中的子串》》》》

84、请编写一个C 函数,该函数在一个字符串中找到可能的最长的子字符串,该字符串是由同一字符组成的。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int  ChildString(char*p)
{
   char* q=p;
   int stringlen=0, i=0,j=1,len=0,maxlen=1;
   //stringlen=strlen(p);
   while(*q!='\0') //不能用strlen,求得长stringlen
        {
     stringlen++;
     q++;
        }
while( i< stringlen)
 {
   if(*(p+i)==*(p+j)&&j< stringlen)
         {
     len++; //统计子串长度
     i++;
     j++;
         }
  else
      {
     if(len>=maxlen) //统计最大子串长度
                  {
             maxlen=len+1;
             len=0;
                }
      else
         len=0;
    i++;
    j++;
      }
}
return maxlen;
}

int main(int argc,char* argv[])
{
   char arr[11];
   int len;
   printf("please input chararr(10):\n");
   scanf("%s",arr);
   len=ChildString(arr);
   printf("the len of childarr is:%d\n",len);
   return 1;
}

99. 计算字符串中子串出现的次数

方法1;
int main(int argc,char* argv[])
{
   char str1[20],str2[20],*p1,*p2;
   int sum=0;
   printf("pleaseinput two strings\n");
   scanf("%s%s",str1,str2);
   p1=str1;
   p2=str2;
   while(*p1!='\0')
       {
    if(*p1==*p2)
             {
                 while((*p1++==*p2++) &&  *p2!='\0');   /*不断比较字符串1与2,至字符串2到达‘\0’*/
              }
    else
          p1++;         /*如果,字符串2一次匹配已结束,或者 此刻*p1与*p2不等;*/
     if(*p2=='\0')       /*如果是字符串2结束,则成功找到一次,sum++*/
             sum++;
   p2=str2;          /*p2始终指向str2;*/
      }
   printf("%d",sum);
   return 1;
}

方法2:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//判断两字符串是否相等,相等返回1,不等返回0
int Judge(char *movePt,char *tempPt)
#if 1
{
    int ret=0 ;
    while( !(*movePt-*tempPt) && *tempPt)
    {
        movePt++;
        tempPt++;
    }
    if(*tempPt=='\0')
    {
        ret=1;
    }
  return ret;
}
#endif
#if 0
{
    int i;
    for(i=0; i<strlen(tempPt); i++,movePt++)
        {
        if(*movePt != tempPt[i])
            return 0;
    return 1;
       }
 }
#endif
//计算子串出现的次数,str为原字符串,sub为子串
int StrCount(char *str,char *sub)
{
    int count = 0;
    char *move = str;
    if( strlen(str) < strlen(sub) )
              {
        return 0;
               }
    else
            {
        while( strlen(move) >= strlen(sub) )
                         {
                    printf("%s\n",move);
            if(Judge(move,sub))
                                    {
                count++;
                printf("count++");
                                     }
            move++;
        }
    }
    return count;
}
int main(int argc,char* argv[])
{
    char arr1[20];
    char arr2[20];
    int num;
    printf("please input two arrs:");
    scanf("%s%s",arr1,arr2);
    num=StrCount(arr1,arr2);
    printf("the num is :%d\n",num);
    return 1;
}

90、输入一行字符,统计其中有多少个单词。

int main(int argc,char* argv[])
{
    char string[81];
    int i,num=0;//word=0;
       char c;
       gets(string);      /*不能用scanf,视空格为终结*/
  for(i=0;(c=string[i])!='\0';i++)
        {
         if(c==' ')
         num++;
        }
  num++;

printf("Thereare %d words in theline\n",num);
return 1;
}


83、请编写一个C 函数,该函数在给定的内存区域搜索给定的字符,并返回该字符所在位置索引值。
intsearch(char* cpSource, int n, char ch) //起始地址,搜索长度,目标字符
{
   int i;
   for(i=0; i<n && *(cpSource+i) != ch; ++i);
  return i;
}



                                            《《《《数字问题,水仙花数,/和%的用法》》》》

98某个公司采用公用电话传递数据,数据是四位的整数,在传递过程中是加密的,加密规则如下:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。

#include<stdio.h>
#include<stdlib.h>
int main(int argc,char* argv[])
{
    int a,i,aa[4],t;
 scanf("%d",&a);
 aa[0]=a%10;
 aa[1]=a%100/10;
 aa[2]=a%1000/100;
 aa[3]=a/1000;
for(i=0;i<=3;i++)
   {
   aa[i]+=5;
   aa[i]%=10;
    }
for(i=0;i<=3/2;i++)
   {
    t=aa[i];
    aa[i]=aa[3-i];
    aa[3-i]=t;
   }
for(i=3;i>=0;i--)
printf("%d",aa[i]);
return 1;
}

97、809*??=800*??+9*??+1其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809*??后的结果。

output(longb,long i)

{

    printf("\n%ld/%ld=809*%ld+%ld",b,i,i,b%i);

}

int main()

{

   long  int  a,b,i;

   a=809;

   for(i=10;i<100;i++)

   {

     b=i*a+1;

   if(b>=1000&&b<=10000&&8*i<100&&9*i>=100)

   output(b,i);

  }

}    

92、有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

 #include "stdio.h"

Int main()

{

inti,j,k;

printf("\n");

for(i=1;i<5;i++) /*以下为三重循环*/

   for(j=1;j<5;j++)

       for (k=1;k<5;k++) 

        {

             if (i!=k&&i!=j&&j!=k) /*确保i、j、k三位互不相同*/

            printf("%d,%d,%d\n",i,j,k);

         }

   }

水仙花束问题:

#include <stdio.h>

int main()

{

  int i;

   int num=0;

for(i=100;i<=999;i++)

{

int H,T,G,A;

           H =i/100;

            T=i/10%10;

            G  =i%10;

       A =H*H*H +T*T*T + G*G*G;

            if(A==i )

           {

             printf("%5d",i);

             num++;

           }

        }

       printf("thenum is %d\n",num);

       return 1;

       }

                                                    《《《《有关位操作的问题》》》》

93.取一个整数a从右端开始的4~7位。

Int main()

{

unsigned a,b,c,d;

scanf("%o",&a);  /*scanf("%x",&a);16进制*/

b=a>>4;

c=~(~0<<4);// ~的优先级大于<<;

/*~0,11111111->11110000->括号外面00001111,保证低4位为1111*/

d=b&c;

printf("%o\n%o\n",a,d);

}

运行结果:输入:1234

输出:

1234

11(8进制)


78、请编写一个C 函数,该函数给出一个字节中被置1 的位的个数

#include<stdio.h>
#include<stdlib.h>
unsigned char CheckSetBitNum(unsigned char ucNumber)
{
    unsigned char i;
    unsigned char iResult=0;
    for(i=0;i<8;i++)
              {
        iResult+= (ucNumber>>i) & 0x01;    //第i位是1则加1,否则加0,位移动操作不改变原值
        printf("ucNumber>>%d=%d\n",i,ucNumber>>i);
        printf("iResult=%d\n",iResult);
              }
    return iResult;
}
int main(int argc,char* argv[])
{
    unsigned char a;
    int num;
    scanf("%c",&a);
    num=CheckSetBitNum(a);
    printf("%d",num);
    return 1;
}
方法2:
 int count(int x)
{
  int i,y,sum=0;
  for (i=0;i<8;i++)
   {
        y=x%2; /*这是移出去的值*/
       x=x/2;   /*对于整数右移一次后x的值相当于右移前的值除以2*/
       if (y==1) sum+=1;
}
return sum;
}

int main(int argc,char* argv[])
{
   int x;
  scanf("%d",&x);
  printf("%d",count(x));
  return 0;
}



                                                 《《《《字符串与整数互换》》》》
79、请编写一个C 函数,该函数将给定的一个字符串转换成整数。
int main(int argc,char* argv[])
{
    char arr[20];
    char* str=arr;
    int num=0;
    int digital;
    printf("please input a string");
    scanf("%s",arr);
 while(*str!='\0')
            {
       digital=*str-48;
       num=num*10+digital;
      str=str+1;
 }
  printf("the result is %d",num);
  return 1;

 }
字符串倒置
int main(int argc,char*argv[])
{
         char* str="hello world";
         char* des=NULL;
         int len=strlen(str);
         des=(char*)malloc(len+1);//结尾封口添0;
         char* d=des;
         char* s=&str[len-1];//指向最后一个字符;
         while(len--!=0)
                 *d++=*s--;
         *d='\0';//封口
         printf("%s\n",des);
         free(des);
return 1;
                                                             《《《《数组》》》》 

94. 打印出杨辉三角形

int main()

{

  int i,j,arr[11][11];

       for(i=1;i<=10;i++)

       for(j=1;j<=i;j++) 

       {

           if(j==1||i==j) 

              arr[i][j]=1;      

               else

                  arr[i][j]=arr[i-1][j-1]+arr[i-1][j]; 

      }

       for(i=1;i<=10;i++)

       for(j=1;j<=i;j++) 

       {

         printf("%5d",arr[i][j]);

         if(i==j)

         printf("\n");      

         }    return 1;           

}

71.一语句实现x是否为2的若干次幂的判断。

void main()

{

int a;

scanf(“%d”,&a); printf(“%c”,(a)&(a-1)?’n’:’y’);

// 若是打印y,否则n

****************************************

2的n次幂用2进制表示一定是10,100,1000,10000......

对应的i-1就是1,11,111,1111.... i &(i-1)为false(也就是0)时就是返回true

 

******************************************************************************************************* ****************************

                                                                             程序分析题

class A
{
public:
   A(int a)
 {
       printf("%d ",a);
    }
};
A a(1);
int main(void)
{
    printf("main ");
    A c(2);
    static A b(3);
    return 0;
}
答案:、1 main 2
3
【函数体外】
能存在声明语句或定义语句(实际上函数体外的声明语句都是定义语句,如果没有初始化,会隐式的初始化,对于基本类型初始化为零,对于类类型则调用相应的构造函数),
不能存在表达式语句,包括函数调用语句。


2.
struct Test
{
    unsigned short int a:5;
    unsigned short int b:5;
    unsigned short int c:6;
};
int main(intargc,char* argv[])
{
    struct Test test;
    test.a=16;
    test.b=4;
    test.c=0;
    int j=sizeof(test);
    int i=*(short*)&test;
    printf("%d\n",i);
    printf("sizeof %d\n",j);
    return 0;
}
0000 0000 1001 0000
小端机器结果将是:16+128=144,选B


60.main()
{
Int a[5]={1,2,3,4,5};
int*ptr=(int*)(&a+1);
int* ptr2=(int*)((int*)a+1);
printf(“%d,%d,%d”,*(a+1),*(ptr-1),*ptr2);  结果:2,5,2
}

地址  0-3   4-7   8-11  12-15  16-19   20-23
数值   1     2     3      4      5
&a+1 就是地址为20的地方
*ptr1[-1]就是20-4=16这个地方  一个Int 占用4个地址
(int)a+1 跟(int*)a+1不一样  前者地址为1 后者为4,
所以,int *ptr2=(int*)((int)a+1);*ptr2表示的是指向地址为1的指针地

要点:指针进行运算,加数与指针类型相关,一般(char*),一个字节;(int*),4个字节;
          若是指向结构体,或者是数组的指针,由具体(sizeof)长度决定;
详见:点击打开链接

#include <stdio.h>
#include <stdlib.h>
int main()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}

小端字节:*ptr2=0x2000000;

大端字节:*ptr2=0x100;



  62
#define SQUARE(a)((a)*(a))
int a=5;
int b;
b=SQUARE(a++);

在同一个式子中有两次以上修改变量内容的副作用时,是未定义行为。
C语言规定a++的自增副作用应该发生在下一个序列点之前,但是乘法、括号和赋值都不是序列点,只有整个表达式结束时才是。在此之前a自增副作用发生的时机是未定义的。  每个编译器的结果不同,结果是25或者36(不提倡在一个表达式中对变量进行两次后自增操作)


63、#define Max_CB500
void LmiQueryCSmd(StructMSgCB* pmsg)
{
unsigned char ucCmdNum;
......
for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdNum++)
{
......;
}

}这段代码执行有什么问题?
【标准答案】死循环
unsigned char //无符号字符型表示范围0~255
char //有符号字符型表示范围-128~127


67.
#include<stdio.h>
#include<stdlib.h>
int modifyvalue()
{
  int x;
  return(x+=10);
}
int changevalue(int x)
{
   x+=1;
   return(x);
}

int main(int argc,char*argv[])
{
int x=10;
x++;
x=changevalue(x);
printf("changevalue:%d\n",x);//12
x++;
modifyvalue();
printf("Firstoutput:%d\n",x);//13
x++;
x=changevalue(x);//15
printf("Secondoutput:%d\n",x);
modifyvalue();
printf("Thirdoutput:%d\n",x);//15
return 1;
}

int modifyvalue()
{
int x;
  return(x+=10);
}
int changevalue(int x)
{
   x+=1;
   return(x);
}
int main(int argc,char*argv[])
{
int x=10;
x++;
changevalue(x);//变量没有接受返回值
printf("changevalue:%d\n",x);//11
x++;
modifyvalue();      //无形参
printf("Firstoutput:%d\n",x);//12
x++;
changevalue(x);//15
printf("Secondoutput:%d\n",x);//13
modifyvalue();
printf("Thirdoutput:%d\n",x);//13
return 1;
}
考查临时变量(返回值为栈中变量)的生存期:仅仅在于 紧跟着的一条语句;

73、下面的代码输出是什么,为什么?
void foo(void)
{
unsigned inta = 6;
int b = -20;
(a+b> 6)? puts("> 6") : puts("<= 6");
}

【参考答案】这个问题测试你是否懂得C语言中的整数自动转换原则,
我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是“>6”。
原因是当表达式中存在有符号类型和无符号类型时所有的数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的



74、评价下面的代码片断:

unsigned intzero = 0;

unsigned intcompzero= 0xFFFF;

/*1‘s complement of zero */

【参考答案】对于一个int型不是16位的处理器为说,上面的代码是不正

确的。应编写如下:

unsigned intcompzero= ~0;

这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经

验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而

PC机程序往往把硬件作为一个无法避免的烦恼。


75.

char *ptr;

if ((ptr= (char *)malloc(0)) ==NULL)

puts("Gota nullpointer");

else

puts("Gota validpointer");

如果所请求的空间大小为0,其行为由库的实现者定义:可以返回空指针,也可以让效果跟申某个非0大小的空间一样,所不同的是返回的指针不可以被用来访问一个对象。

为什么 new T[0] 和 malloc(0) 不返回空指针

首先需要说明的是,按照C++标准,成功的 new T[0] 是不能返回空指针的。而 malloc(0),C 语言标准则指出,成功的时候可以返回空指针,也可以返回非空指针,多数库一般也选择了返回非空指针这种行为。


76.

int main(int argc,char* argv[])
{
    char a='a',b='b';
    int p,c,d;
    p=a;
    p=(p<<8)|b;
    d=p&0xff;
    c=(p&0xff00)>>8;
    printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);
    return 1;
}

[运行结果]:97,98,97,98

77.

int main(int argc,char* argv[])
{
    unsigned a,b;
    printf("please input a number:");
    scanf("%d",&a);
    b=a>>5;
    b=b&15;
    printf("a=%d\tb=%d\n",a,b);
    return 1;
}
【运行结果】:输入 64,输出2


***********************************************************************************************************************************

                                                                             概念区别

指针数组:存放指针的数组;   

Int* ptr[4];  (等同于二级指针 int** ptr)

          一级指针是指向定义类型的内存地址,二级指针就是指向定义类型的内存地址所指向的新的内存地址

应用:指向若干字符串,整形数据等,使得元素处理更加方便;其中元素存放各对应地址

          此时,一级指针只能寻址到字符串所在的位置,并不能将其输出,因为没有其首地址,而**p则完成二级寻址,找到了位置,也找到了它的首地址,所以能输出

 

数组指针:指向一个数组的指针;

          Int (*ptr)[4]

          应用:可以应用在二维数组中;

指针函数:返回指针值得函数

          Int* search(int num);

函数指针:指向函数的指针

          Int (*ptr)(int num);/*申明一个函数指针*/

          Ptr=fun();/*将fun函数地址付给指针ptr*/

                Int a=fun(6).等价于,int a=(*ptr)(6);

         函数指针数组:    int(*s[10])(int) 函数指针数组

27、关键字volatile有什么含意?并给出三个不同的例子。

       它是用来修饰被不同线程访问和修改的变量

如果没有volatile,基本上会导致:要么无法编写多线程程序,要么编译器失去大量优化的机会。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

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

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

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

3). 多线程应用中被几个任务共享的变量

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

3个关联的问题:
  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的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
      long square(volatile int *ptr)
      {
             int a;
             a = *ptr;
             return a * a;
      }


37、Heap与stack的差别。

【标准答案】Heap是堆,stack是栈。

Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/放。

Stack空间有限,Heap是很大的*存储区

C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操符。

程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行

40、带参宏与带参函数的区别(至少说出5点)?

                  带参宏                带参函数

处理时间:    编译时                运行时

参数类型:    无                    定义类型

程序长度:    变长                  不变

占用存储空间:否                    是

运行时间:    不占用                调用和返回占用时间

 

38.用宏定义写出swap(x,y),即交换两数

#define  swap(x, y)  (x)=(x)+(y);(y)=(x)–(y);(x)=(x)–(y);

经典c语言笔试题


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

   #define Min(X, Y)  ((X)>(Y)?(Y):(X))               //结尾没有;

 

43、已知一个数组table,用一个宏定义,求出数据的元素个数。

【标准答案】#define NTBL(table)   (sizeof(table)/sizeof(table[0]))

 

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

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


总结:有关宏定义#define 的基本语法

1#define的概念
#define命令是C语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。
该命令有两种格式:一种是简单的宏定义,另一种是带参数的宏定义。
(1) 简单的宏定义:
#define <宏名>  <字符串>
  例: #define PI 3.1415926
(2) 带参数的宏定义
  #define <宏名>(<参数表>) <宏体>
  例: #defineA(x) x
一个标识符被宏定义后,该标识符便是一个宏名。

这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。

2.宏定义的缺点

在简单宏定义的使用中,当替换文本所表示的字符串为一个表达式时,容易引起误解和误用。如下例:
例1 #define N 2+2
void main()
{
  int a=N*N;
  printf(“%d”,a);
}
(1) 出现问题:在此程序中存在着宏定义命令,宏N代表的字符串是2+2,该题的结果为8,
(2) 问题解析:宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方只是简单地使用串2+2来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+2*2+2,计算后=8,这就是宏替换的实质,如何写程序才 能完成结果为16的运算呢?
(3)解决办法:将宏定义写成如下形式
#define N (2+2)
这样就可替换成(2+2)*(2+2)=16
在带参数的宏定义的使用中,极易引起误解。例如我们需要做个宏替换能求任何数的平方,这就需要使用参数,以便在程序中用实际参数来替换宏定义中的参数。一般学生容易写成如下形式:
#define area(x) x*x
这在使用中是很容易出现问题的,看如下的程序
void main()
{
int y=area(2+2);
printf(“%d”,y);
  }
按 理说给的参数是2+2,所得的结果应该为4*4=16,但是错了,因为该程序的实际结果为8,仍然是没能遵循纯粹的简单替换的规则,又是先计算再替换了,在这道程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。那如果遵循(1)中的解决办法,把2+2 括起来,即把宏体中的x括起来,是否可以呢?#define area(x) (x)*(x),对于area(2+2),替换为(2+2)*(2+2)=16,可以解决,但是对于area(2+2)/area(2+2)又会怎么样 呢,这道题替换后会变为 (2+2)*(2+2)/(2+2)*(2+2)即4*4/4*4按照乘除运算规则,结果为16/4*4=4*4=16,那应该怎么呢?解决方法是在整个宏体上再加一个括号,即

#define area(x) ((x)*(x)),不要觉得这没必要,没有它,是不行的。
要想能够真正使用好宏 定义,

一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展开后再进行相应的计算,就不会写错运行结果。

如果是自己编程使用宏替换,则在使用简单宏定义时,当字符串中不只一个符号时,加上括号表现出优先级,如果是 带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。

宏定义的优点:

(1) 方便程序的修改
使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时, 我们可以用较短的有意义的标识符来写程序,这样更方便一些。
(2) 提高程序的运行效率
使用带参数的宏定义可完成函数调用的功能,又能 减少系统开 销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。

 

49、什么是预编译,何时需要预编译:

【标准答案】1、总是使用不经常改动的大型代码体

2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

 

53、Typedef在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。

如,思考一下下面的例子:

#define dPSstructs *

typedefstructs * tPS;

以上两种情况的意图都是要定义dPS和tPS作为一个指向结构s指针。哪种方法更好呢?

答案是:typedef更好。思考下面的例子:

dPSp1,p2;

tPSp3,p4;

第一个扩展为

structs * p1, p2;

上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许

不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。

 

54.在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”?
     重点就是C++里面支持函数的重载,因此编译出来的.obj或者库文件里面,函数名会被加上和参数有关的签名,用来区分同样函数名参数不同的函数。然而C语言不支持重载,所以函数名里面没有这样的签名。
     这样,当C语言的程序调用C++写成的库的时候,就会找不到函数。比如,一个函数叫 void foo(int bar)之类的,可能会在c++编译成叫 foo_i之类的名字,而在c语言里面就会编译成foo,这样c语言的程序去找foo就会找不到,这样连接的时候会出错。
      为了解决这个问题,引入了extrn "c"{},在这个的作用域之内,c++的函数名不会加上参数签名,和c语言的标准保持统一,就兼容c语言的程序了。

2、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area(double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
【参考答案】这个函数有太多的错误了,以至让人不知从何说起了:
1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第
一项。
3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编
译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做
浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明
智的。
4). 与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉
了第三和第四点,我不会太为难你的。

35、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
【标准答案】可以

                                       更新至11.30