碰到的一些笔试题C语言方面

1.编写strcpy函数

已知strcpy函数的原型是

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

       其中strDest是目的字符串,strSrc是源字符串。

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

char *strcpy(char *strDest, const char *strSrc);
{
     assert((strDest!=NULL) && (strSrc !=NULL)); // 对源地址和目的的非零断言 2分
     char *address = strDest;                   // 2分
     while( (*strDest++ = * strSrc++) != ‘/0’ )    // 2分
          NULL ;
     return address ;                          //为了实现链式操作,实现目的地址的返回  2分
}

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

答:为了实现链式表达式。                                              // 2分

例如       int length = strlen( strcpy( strDest, “hello world”) );

另:原型:extern char *strcpy(char *dest,char *src);
用法:#include <string.h>
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
给一个很经典版本的strcpy函数源码:
char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL));//对源地址和目的的非零断言
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘/0’ )
return address ; //为了实现链式操作,实现目的地址的返回

  }

在拷贝时,要考虑到"/0"字节,这个是字符串结尾字符。

返回值:what‘s the reason?

char sz0[100] = "liu_feng_fly";
char sz1[100];
char sz2[100];
strcpy(sz2,strcpy(sz1,sz0));
这样连接起来用的时候就需要了
---------------------------------------------------------------

这个是为了方便你是用函数的执行结果作为其他函数的参数或者表达式中的一项。
---------------------------------------------------------------

有很多的返回值是用来判断这个函数执行成功于否


2.atoi函数实现方式

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

int atoi(const char *str)
{
     int total=0;                
     int sign=1; 
    
     assert(str);                   //如果并未输入任何字符串或一个无效字符串则退出运行

     if(*str=='-')
     {                             //如果第一个字负号
          sign=-1;                  //输出为负数
          str++;                    //移至下一个字符
     }
     else
          if(*str=='+')             //如果第一个字符是正号
          {
               str++;                //无改动,移至下一个字符
          }
     while(*str)                   //当字符串还为结尾
     {
          unsigned int ch=*str-'0'; //当下字符所代表的数字
          assert(ch<=9&&ch>=0);     //如果该数字不再0至9的范围内退出运行
          total=total*10+ch;        //原数字增加一位
          str++;                    //移动指针到下一个字符
     }
     return total*sign;            //返还所得到的整数,加入符号
}

void main()                    //测试用例
{
     int i;
     char *str="123";
     i=atoi(str);
     printf("%d/n",i);
}

3.什么是进程?什么是线程?
进程:描述计算机程序的执行过程和作为资源分配的基本单位用来反映操作系统的执行并发、资源共享及用户随机的特点。 进程的定义:并发执行的程序在执行过程中分配和管理资源的基本单位。
进程控制块包含了有关进程的描述信息、控制信息以及资源信息、是进程动态特征的集中反映。系统根据PCB感知进程的存在和通过PCB中所包含的各项变量的变化,掌握进程所处的状态以达到控制进程活动的目的。
线程:轻权级进程或轻量级进程。
线程和进程的区别:
虽然进程和线程都是处理机调度的基本单位,但是,线程的改变只代表了CPU执行过程的改变,而没有发生进程所拥有的资源变化。或者说,除了CPU之外,计算机内的软硬件资源的分配与线程无关,线程只能共享它所属进程的资源。
线程是进程的一部分,它没有自己的地址空间,它和进程内的其他进程一起共享分配该进程的所有资源。
4.sizeof问题
见《程序员面试宝典》


5.Linux进程间通信有几种方式?各自优缺点?
管道和FIFO(命名管道) 信号量 消息 共享内存区 套接字

Linux平台下进程间通信主要几种方法

(1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

(3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本 身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于 BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。


(4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

(5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

(6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。

(7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

(8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

6.8位按位倒序。



7.static在C/C++中的作用。

1.先来介绍它的第一条也是最重要的一条:隐藏。

当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c.

下面是a.c的内容:

 char a = 'A'; // global variable
void msg()
{
printf("Hello/n");
}


下面是main.c的内容:

 int main(void)
{
extern char a; // extern variable must be declared before use
printf("%c ", a);
void msg();
return 0;
}

程序的运行结果是:

 Hello

你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。

如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的 文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变 量,static还有下面两个作用。

2.static的第二个作用是保持变量内容的持久。

存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。

 #include <stdio.h>
int fun(void)
{
static int count = 10; // 事实上此赋值语句从来没有执行过
return count--;
}
int count = 1;
int main(void)
{
printf("global/t/tlocal static/n");
for(; count <= 10; ++count)
printf("%d/t/t%d/n", count, fun());
return 0;
}

程序的运行结果是:

global local static
1  10
2   9
3   8
4   7
5   6
6   5
7   4
8   3
9   2
10  1

3.static的第三个作用是默认初始化为0.其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。

在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有 元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末 尾加‘/0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是‘/0’。不妨做个小实验验证一下。

#include <stdio.h>
int a;
int main(void)
{
int i;
static char str[10];
printf("integer: %d; string: (begin)%s(end)", a, str);
return 0;
}

程序的运行结果如下integer: 0; string: (begin)(end)

最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0.





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值