JobPlus知识库 IT 其它 文章
c语言基础

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




2. 写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
#define MIN(A,B) ((A) <= (B) (A) : (B)) 
注意大小写的问题、括号的问题
4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是: 
while(1) 


一些程序员更喜欢如下方案: 
for(;;) 


第三个方案是用 goto 
Loop: 
... 
goto Loop;


5. 用变量a给出下面的定义 
a) 一个整型数(An integer) int a
b) 一个指向整型数的指针(A pointer to an integer) int* ptr
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer) int** ptr
d) 一个有10个整型数的数组(An array of 10 integers) int a[10]
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)int* p[10] 
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) in (*p)[10]
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) int (*p)(int)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )int (*p[10])(int)


答案是: 
a) int a; // An integer 
b) int *a; // A pointer to an integer 
c) int **a; // A pointer to a pointer to an integer 
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 10 integers 
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer 
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer 


6. 关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 


修饰变量:在函数内,表示此变量在调用的过程中它的值维持不变;
          在函数外,表示静态变量,可以被本文件使用,但不能被其他文件使用。
修饰函数:表示是静态函数,只能被该文件中其他函数调用;


7.关键字const是什么含意? 
const int a; 表示a是一个常整数
int const a; 表示a是一个常整数
const int *a; 表示a是一个指向常整数的指针
int * const a; 表示a是一个指向整数类型的常指针
int const * a const;表示a是一个指向常整数类型的常指针


前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。


8. 关键字volatile有什么含意 并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 
1). 并行设备的硬件寄存器(如:状态寄存器) 
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 
3). 多线程应用中被几个任务共享的变量 


表示该变量是易变的,编译器不应该去优化该值,即编译器不会去假设这个变量的值。它必须去内存中重新获取该值,而不是用寄存器中保存的值。
(1)并行设备的硬件寄存器
(2)一个服务子程序中访问到的非自动变量。
(3)多线程应用中被几个任务共享的变量。




1). 一个参数既可以是const还可以是volatile吗?解释为什么。 
可以,比如寄存器变量,我们希望它是只读的,但是仍可能是易变的。
2). 一个指针可以是volatile 吗?解释为什么。 
buffer指向一个地址被A线程使用,B线程修改了buffer所指的地址,同时希望A线程使用新地址,设置volatile。
3). 下面的函数有什么错误: 
int square(volatile int *ptr) 

return *ptr * *ptr; 

Ptr由于是volatile类型,是易变的,两次取得的值可能不一样。因此此函数可能无法完成平方的功能。
Ptr内容可能被修改,无法保证两次取得同一个值,应该先取出值放入一个变量中,然后通过这个变量来计算
9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
#define BIT3 (0x1<<3) 
static int a; 
void set_bit3(void) 

a |= BIT3; 

void clear_bit3(void) 

a &= ~BIT3; 



#define BIT3 (0x1<<3) //注意括号
void set_bit3(void)

  a | =BIT3
}
Void clear_bit3
{
  A&=~BIT3
}


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


Int *p=(int *)0x67a9;
*p=0xaa6;




11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius) 

double area = PI * radius * radius; 
printf(" Area = %f", area); 
return area; 
}
(1)中断不应该有返回值
(2)中断子程序中应该尽量少使用浮点类型,因为不可重入,以及printf也是不可重入的,在不同平台移植会出错。
(3)中断不应该传参


12 . 下面的代码输出是什么,为什么?
void foo(void) 

unsigned int a = 6; 
int b = -20; 
(a+b > 6) puts("> 6") : puts("<= 6"); 
}
>6  
无符号整形和符号整形数字相加时,后者自动转化为无符号整形,因此应该是>6
13. 评价下面的代码片断:


unsigned int zero = 0; 
unsigned int compzero = 0xFFFF; 
/*1's complement of zero */
因为不知道该平台是几位的,因此这个写法是不对的,正确写法应该是
unsigned int compzero=~0;


14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么?
主要有三种类型:
内存泄露、
内存碎片、
内存崩溃  
内存崩溃是内存使用最严重的结果,主要原因有数组访问越界、写已经释放的内存、指针计算错误、访问堆栈地址越界等等。碎片收集的问题,变量的持行时间等等
下面的代码片段的输出是什么,为什么?


char *ptr; 
if ((ptr = (char *)malloc(0)) == NULL) 
puts("Got a null pointer"); 
else 
puts("Got a valid pointer"); 
该代码的输出是“Got a valid pointer”。




15. Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子: 
#define dPS struct s * 
typedef struct s * tPS; 


答案是:typedef更好。思考下面的例子: 
dPS p1,p2; 
tPS p3,p4;
第一个扩展为 
struct s * p1, p2;


上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。


16. C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么? 
int a = 5, b = 7, c; 
c = a+++b;
上面的代码被处理成: 
c = a++ + b; 
因此, 这段代码持行后a = 6, b = 7, c = 12。 


17.找错题


  试题1:


void test1()
{
 char string[10];
 char* str1 = "0123456789";
 strcpy( string, str1 );



  试题2:
















void test2()
{
 char string[10], str1[10];
 int i;
 for(i=0; i<10; i++)
 {
  str1[i] = 'a';
 }
 strcpy( string, str1 );



  试题3:


void test3(char* str1)
{
 char string[10];
 if( strlen( str1 ) <= 10 )
 {//假如str1=10,那么它的长度就是11了,拷贝不下了
  strcpy( string, str1 );
 }



  解答:


  试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;


  对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分;


  对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。


18.写出字符串strcpy的函数实现过程式
void strcpy( char *strdest, char *strsrc )
{
  while( (*strdest++ = * strsrc++) != ‘\0’ );



  4分


void strcpy( char *strdest, const char *strsrc ) 
//将源字符串加const,表明其为输入参数,加2分
{
  while( (*strdest++ = * strsrc++) != ‘\0’ );



  7分


void strcpy(char *strdest, const char *strsrc) 
{
 //对源地址和目的地址加非0断言,加3分
 assert( (strdest != null) && (strsrc != null) );
 while( (*strdest++ = * strsrc++) != ‘\0’ );

  10分


//为了实现链式操作,将目的地址返回,加3分!


char * strcpy( char *strdest, const char *strsrc ) 
{
 assert( (strdest != null) && (strsrc != null) );
 char *address = strdest; 
 while( (*strdest++ = * strsrc++) != ‘\0’ ); 
  return address;







 


 20.经典getmemory问题讨论
试题4:


void getmemory( char *p )
{
 p = (char *) malloc( 100 );
}


void test( void ) 
{
 char *str = null;
 getmemory( str ); 
 strcpy( str, "hello world" );
 printf( str );



  试题5:


char *getmemory( void )

 char p[] = "hello world"; 
 return p; 
}


void test( void )

 char *str = null; 
 str = getmemory(); 
 printf( str ); 



  试题6:


void getmemory( char **p, int num )
{
 *p = (char *) malloc( num );
}


void test( void )
{
 char *str = null;
 getmemory( &str, 100 );
 strcpy( str, "hello" ); 
 printf( str ); 



  试题7:


void test( void )
{
 char *str = (char *) malloc( 100 );
 strcpy( str, "hello" );
 free( str ); 
 ... //省略的其它语句



  解答:


  试题4传入中getmemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完


char *str = null;
getmemory( str ); 


  后的str仍然为null;


  试题5中


char p[] = "hello world"; 
return p; 


  的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。


  试题6的getmemory避免了试题4的问题,传入getmemory的参数为字符串指针的指针,但是在getmemory中执行申请内存及赋值语句


*p = (char *) malloc( num ); 


  后未判断内存是否申请成功,应加上:


if ( *p == null )
{
 ...//进行申请内存失败处理
} //申请内存后一定要加上内存申请成功与否的语句,而且最后进行释放


  试题7存在与试题6同样的问题,在执行


char *str = (char *) malloc(100); 


  后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:


str = null; 


  试题6的test函数中也未对malloc的内存进行释放。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持
287人赞 举报
分享到
用户评价(0)

暂无评价,你也可以发布评价哦:)

扫码APP

扫描使用APP

扫码使用

扫描使用小程序