Maison  >  Article  >  développement back-end  >  Questions d'entretien en langage C classique (référence)

Questions d'entretien en langage C classique (référence)

青灯夜游
青灯夜游avant
2020-08-03 17:07:283090parcourir

Questions d'entretien en langage C classique (référence)

1 Prétraitement

Question1 : Qu'est-ce que la précompilation ? Quand la précompilation est-elle nécessaire ?

Réponse :

La précompilation, également appelée prétraitement, est le premier travail effectué dans l'ensemble du processus de compilation, c'est-à-dire un travail de prétraitement avant l'exécution du programme. Gère principalement les instructions commençant par #. Comme copier le code du fichier inclus dans #include, remplacer les macros définies par #define, compilation conditionnelle #if, etc. .

Lorsqu'une précompilation est nécessaire :

1 Utilisez toujours des corps de code volumineux qui ne changent pas fréquemment.

2. Le programme se compose de plusieurs modules, qui utilisent tous un ensemble standard de fichiers d'inclusion et les mêmes options de compilation. Dans ce cas, tous les fichiers include peuvent être précompilés dans un en-tête précompilé.

Question2 : Écrivez une macro "Standard" qui prend deux paramètres et renvoie le plus petit

Réponse:#define MIN(x, y) ((x)5a31c26dd45d454d95daa972ea806371   }

4. 不可以将extern"C" 添加在函数内部

5. 如果函数有多个声明,可以都加extern"C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则。

6. extern"C", 还有extern "FORTRAN" 等。

问题5sizeof关键字的作用?

答:

sizeof是在编译阶段处理,且不能被编译为机器码。sizeof的结果等于对象或类型所占的内存字节数。sizeof的返回值类型为size_t

变量:int a; sizeof(a)4

指针:int *p; sizeof(p)4

数组:int b[10]; sizeof(b)为数组的大小,4*10int c[0]; sizeof(c)等于0

结构体:struct (int a; char ch;)s1; sizeof(s1)8 与结构体字节对齐有关。

注意:不能对结构体中的位域成员使用sizeof

sizeof(void)等于1

sizeof(void *)等于4

3 结构体

问题1:结构体的赋值?

答:

C语言中对结构体变量的赋值或者在初始化或者在定义后按字段赋值。

方式1:初始化

struct tag
{
 chara;
 int b;
}x = {‘A’, 1};/*初始化*/
或
struct tag
{
char a;
int b;
};
struct tag x = {‘A’,1};/*在定义变量时初始化*/

GNU C中可使用另外一种方式:

struct tag
{
char a;
int b;
}x =
{
.a = ‘A’,
.b =1;
};
或
struct tag
{
char a;
int b;
};
struct tag x =
{
 .a= ‘A’,
 .b=1,
};

方式2:定义变量后按字段赋值

struct tag
{
char a;
int b;
};
struct tag x;/*定义变量*/
x.a = ‘A’;/*按字段赋值*/
x.b = 1; /*按字段赋值*/

而当你使用初始化的方式来赋值时,如x = {‘A’,1};则出错。

方式3:结构变量间的赋值

struct tag
{
 chara;
 int b;
};
struct tag x,y;
x.a=’A’;
x.b=1;
y = x;/*结构变量间直接赋值*/

问题2:结构体变量如何比较?

答:虽然结构体变量之间可以通过=直接赋值,但不同通过比较符如==来比较,因为比较符只作用于基本数据类型。这个时候,只能通过int memcmp(const void *s1, const void *s2, size_t n);来进行内存上的比较。

问题3:结构体位域

答:

位域是一个或多个位的字段,不同长度的字段(如声明为unsigned int类型)存储于一个或多个其所声明类型的变量中(如整型变量中)。

位域的类型:可以是charshortint,多数使用int,使用时最好带上signedunsigned

位域的特点:字段可以不命名,如unsignedint :1;可用来填充;unsigned int :0; 0宽度用来强制在下一个整型(因此处是unsigned int类型)边界上对齐。

位域的定义:

struct st1
{
unsigned chara:7;/*字段a占用了一个字节的7个bit*/
unsigned charb:2;/*字段b占用了2个bit*/
unsigned charc:7;/*字段c占用了7个bit*/
}s1;

sizeof(s1)等于3。因为一个位域字段必须存储在其位域类型的一个单元所占空间中,不能横跨两个该位域类型的单元。也就是说,当某个位域字段正处于两个该位域类型的单元中间时,只使用第二个单元,第一个单元剩余的bit位置补(pad0

于是可知Sizeof(s2)等于3*sizeof(int)12

struct st2
{
unsigned inta:31;
unsigned intb:2;/*前一个整型变量只剩下1个bit,容不下2个bit,所以只能存放在下一个整型变量*/
unsigned int c:31;
}s2;

位域的好处:

      1.有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有01 两种状态,用一位二进位即可。这样节省存储空间,而且处理简便。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
       2.可以很方便的利用位域把一个变量给按位分解。比如只需要4个大小在03的随即数,就可以只rand()一次,然后每个位域取2个二进制位即可,省时省空间。

位域的缺点:

不同系统对位域的处理可能有不同的结果,如位段成员在内存中是从左向右分配的还是从右向左分配的,所以位域的使用不利于程序的可移植性。

问题4:结构体成员数组大小为0

结构体数组成员的大小为0GNU C的一个特性。好处是可以在结构体中分配不定长的大小。如

typedef struct st
{
 inta;
int b;
char c[0];
}st_t;
sizeof(st_t)等于8,即char c[0]的大小为0.
#define SIZE 100
st_t *s = (st_t *)malloc(sizeof(st_t) + SIZE);

4 函数

问题1:函数参数入栈顺序

答:

C语言函数参数入栈顺序是从右向左的,这是由编译器决定的,更具体的说是函数调用约定决定了参数的入栈顺序。C语言采用是函数调用约定是__cdecl的,所以对于函数的声明,完整的形式是:int __cdecl func(int a, int b);

问题2inline内联函数

答:

inline关键字仅仅是建议编译器做内联展开处理,即是将函数直接嵌入调用程序的主体,省去了调用/返回指令。

5 内存分配回收

问题1 malloc/freenew/delete的区别

答:

1) mallocfreeC/C++语言的标准库函数,new/deleteC++的运算符。它们都可用于申请动态内存和释放内存。

2) 对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的对象没有构造与析构的过程,对它们而言malloc/freenew/delete是等价的。

3) 既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

问题2malloc(0)返回值

答:如果请求的长度为0,则标准C语言函数malloc返回一个null指针或不能用于访问对象的非null指针,该指针能被free安全使用。

6 可变参数列表

可变参数列表是通过宏来实现的,这些宏定义在stdarg.h头文件,它是标准库的一部分。这个头文件声明了一个类型va_list和三个宏:va_startva_argva_end

typedef char *va_list;
#define va_start(ap, A)  (void)((ap) = (char *)&(A) + _Bnd(A, _AUPBND))
#define va_arg(ap, T) (*(T )((ap) += _Bnd(T, _AUPBND)) - _Bnd(T, _ADNBND)))
#define va_end(ap) (void)0
int print(char *format, …)

va_start的第一个参数是va_list类型的变量,第二个参数是省略号前最后一个有名字的参数,功能是初始化va_list类型的变量,将其值设置为可变参数的第一个变量。

va_arg的第一个参数是va_list类型的变量,第二个参数是参数列表的下一个参数的类型。va_arg返回va_list变量的值,并使该变量指向下一个可变参数。

va_end是在va_arg访问完最后一个可变参数之后调用的。

问题1:实现printf函数

/*(转载)
 * A simple printf function. Only support the following format:
 * Code Format
 * %c character
 * %d signed integers
 * %i signed integers
 * %s a string of characters
 * %o octal
 * %x unsigned hexadecimal
 */
int my_printf( const char* format, ...)
{
    va_list arg;
    int done = 0;

    va_start (arg, format); 

    while( *format != '\0')
    {
        if( *format == '%')
        {
            if( *(format+1) == 'c' )
            {
                char c = (char)va_arg(arg, int);
                putc(c, stdout);
            } else if( *(format+1) == 'd' || *(format+1) == 'i')
            {
                char store[20];
                int i = va_arg(arg, int);
                char* str = store;
                itoa(i, store, 10);
                while( *str != '\0') putc(*str++, stdout); 
            } else if( *(format+1) == 'o')
            {
                char store[20];
                int i = va_arg(arg, int);
                char* str = store;
                itoa(i, store, 8);
                while( *str != '\0') putc(*str++, stdout); 
            } else if( *(format+1) == 'x')
            {
                char store[20];
                int i = va_arg(arg, int);
                char* str = store;
                itoa(i, store, 16);
                while( *str != '\0') putc(*str++, stdout); 
            } else if( *(format+1) == 's' )
            {
                char* str = va_arg(arg, char*);
                while( *str != '\0') putc(*str++, stdout);
            }

            // Skip this two characters.

            format += 2;
        } else {
            putc(*format++, stdout);
        }
    }

    va_end (arg);

    return done;
}

7 其他

问题1:ASSERT()的作用

:ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,你可以这样写程序:

......

ASSERT( n != 0);

k = 10/ n;

.....

ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。

assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。

问题2:system("pause");的作用

:系统的暂停程序,按任意键继续,屏幕会打印,"按任意键继续。。。。。"省去了使用getchar();

问题3:请问C++的类和C里面的struct有什么区别?

:c++中的类具有成员保护功能,并且具有继承,多态这类oo特点,而c里的struct没有。c里面的struct没有成员函数,不能继承,派生等等.

8 找错题

试题1:

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

解答:字符串str1有11个字节(包括末尾的结束符'\0'),而string只有10个字节,故而strcpy会导致数组string越界。

试题2:

void test2()
{
  char string[10], str1[10];
  int i;
  for(i=0; ibaf103fa9dc8da6e7bf9789e55ccb6b9

size_t strlen(const char *s)

{

const char *sc;

for (sc = s; *sc != '\0'; ++sc)/*不包含最后的结束符'\0'*/

;

return (sc - s);

}

试题4:

void GetMemory(char *p)
{
  p = (char *)malloc( 100 );
}
void Test( void )
{
  char *str = NULL;
  GetMemory(str);
  strcpy(str,"hello world");
  printf(str);
}


解答:C语言中的函数参数为传值参数,在函数内对形参的修改并不能改变对应实参的值。故而调用GetMemory后,str仍为NULL。

试题5:

char *GetMemory( void )
{
  char p[] = "hello world";
  return p;
}
void Test( void )
{
  char *str = NULL;
  str = GetMemory();
  printf(str);
}

解答:GetMemory中,p为局部变量,在函数返回后,该局部变量被回收。故而str仍为NULL

试题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);
}

解答:试题6避免了试题4的问题,但在GetMemory内,未对*p为NULL情况的判断。当*p不为NULL时,在printf后,也未对malloc的空间进行free

试题7:

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

解答:未对str为NULL的情况的判断,在free(str)后,str未设置为NULL,可能变成一个野指针(后面对str的操作可能会导致踩内存)。

试题8:

swap(int* p1,int* p2)
{
  int *p;
  *p = *p1;
  *p1 = *p2;
  *p2 = *p;
}

解答:在swap函数中,p是个野指针,可能指向系统区,导致程序运行的崩溃。故而,程序应改为:

swap(int* p1,int* p2)
{
  int p;
  p = *p1;
  *p1 = *p2;
  *p2 = p;
}

9 编程题

1:判断字符串str2是否在字符串str1里。

#include <stdio.h>



#define OK 1

#define ERROR 0



int str_str(const char *str1, const char *str2)

{

const char *s1 = NULL;

const char *s2 = NULL;



if (str1 == NULL)

{

return (str2 == NULL) ? OK : ERROR;

}



if (str2 == NULL)

{

return OK;

}



for (; *str1 != &#39;\0&#39;; str1++)

{

if (*str1 == *str2)

{

for (s1 = str1, s2 = str2; ; )

{

if (*++s2 == &#39;\0&#39;)

{

return OK;

}

else if (*++s1 != *s2)

{

break;

}

}

}

}



return ERROR;

}

怎么样,赶紧复制粘贴运行一下吧!

推荐:《c语言教程

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer