C语言复习提高面试百题

个人原创整理的C语言各方面的琐碎知识点。如果这些基础你都不知道,你还好意思说自己精通C吗?

1 printf是C语言的一部分?

错误!printf是标准输入输出库的部分

2 printf中 %3.0f和%.0f的作用

%3.0f表示至少3个字符的宽度,不显示小数点和小数部分
%.0f表示无小数和小数点部分

3 符号常量是什么?有什么作用?

‘A’就是符号常量

4 外部变量注意点

1.函数要使用外部变量可以用extern显示声明,如:

int maxint main()
{
    extern int max;
    return 0;
}

2.如果外部变量在函数之前,函数使用该外部变量可以不使用extern
3.外部变量在file1定义,在file2,file3使用需要extern显示声明。用于建立之间的联系。

5 数据类型

short 实际上是short int,一般简写为short
long(int)
signedunsigned可以用于char和任何整型
long double 是高精度浮点数

6 常量

1234是int常量(数值太大int无法表示的则为long常量)
123456789L,包含了Lorl的是 long常量
u or U的后缀是 无符号常量
小数形式如3.12或者1e-2形式的,是浮点常量:后缀f or F为float常量;什么都没有则为double常量;有l or L则是long double常量

整数常量

\013是八进制,代表制表符
\xb十六进制,也表示制表符

7 字符串常量

又称为字符字面值
“hello”或者“”(空字符串)都是字符串常量

字符串常量中可以使用转义字符

“hello” “world”可以用于链接字符串

8 转义字符

\a 响铃
\n 换行
\f 换页(例如回车25行)
\b 回退
\r 回车
\t 横向制表符:跳到下一个制表符的位置(一个tab8位)
\v 纵向制表符:跳转到下一行,\v前一个字符的下一列
\030 八进制
\xb 十六进制

9 什么是枚举?enum的注意点和优点

enum color{ green = 0, red, black};

注意点:
1. 名字必须不同
2. 不同名可以同值

优点:
1. 值可以自动生成
2. 编译器会检查enum变量的值是否是其有效值
3. 可以以符号形式打印enum变量的值

10 声明

  1. 外部变量、静态变量默认初始化为0
  2. 未初始化自动变量为垃圾值
  3. 任何变量都可以使用const

11 什么是闰年?

能整除400 或者 能整除4却不能整除100 的年份是闰年
((n % 400) == 0) || ( (n % 4) == 0 && (n % 100) == 0)

12 类型转换注意点

  • 自动转换:从窄的数据类型转换为宽的数据类型,没有精度损失
  • char是较少的整型,可以自由使用。C语言没有规定char是否是signed。
  • 表达式中float不会自动转换为double
  • 有符号:二元运算符如(* +)两个操作数如果具有不同类型,低的会自动转换为高的
  • float转换为int,会舍去小数部分
  • double转换为float,根据具体实现,可能会四舍五入
  • 有符号数取决于机器,例如:int 16bit, long 32bit
    -1L < 1U—-1U unsigned int回转换为sigend long int
    -1L > 1UL—-1L signed long int会转换为 unsigned long
  • sqrt参数使用double(定义在math.h中)
    例如:sqrt((double)n); 不然数值可能不对

补充

  • 为了确保可移植性:如果char中保存的不是char的数据,需要显式地指明是signed还是unsigned
  • 即使调用函数的参数为charfloat,也把函数的声明写成int或者double

13 ++、–只能用于变量,不能用于表达式

14 编写从字符串s中删除字符c的程序

void squeeze(char *s, char c)
{
    int i, j;
    for(i = j = 0; s[i] != '\0'; i++)
    {
        if(s[i] != c)
        {
            s[j++] = s[i];
        }
    }
    s[j] = '\0';
}

15 编写strcat(将第二个字符串复制到第一个字符串尾部)

void strcat(char *s, char *t)
{
    int i, j;
    i = j = 0;
    while(s[i] != '\0') 
        i++;
    while((s[i++] = t[j++]) != '\0')
        ;
}

16 位运算符只能用于整型

17 什么是算术移位,逻辑移位

算术移位右移补符号位
算术移位左移,逻辑移位左右移均补0

18 赋值运算符(*=等)

使用方式

x *= y + 1等效于x = x * (y + 1)

优点:

  1. 不用检查两个的长表达式是否一样
  2. 有助于编译器产生高效代码

x *= y + 1的类型是什么,值是什么

类型是左侧操作数的类型
值是赋值完成后的数值

18 条件表达式,三元运算符?: 优点

紧凑

19 运算符优先级

C语言复习提高面试百题_第1张图片
第二类:+ -表示数值的正负

20 求值顺序:C语言没有规定函数各个参数的求值顺序

所有的对函数参数的副作用都必须在调用函数之前完成

21 程序块

  • 任何程序块中都可以声明变量
  • 代码块内声明的变量可以隐藏程序块之外与之同名的变量,它们之间没有任何关系。如代码:
if(n > 0)
{
    int i;//这里的i与if外面的i没有任何关系
}
  • 然而,应该避免出现隐藏外部作用域中相同名字的情况,否则容易引起混乱,不是好的代码风格。

22 switch退出方式

break, return

23 shell排序

24 逗号运算符

逗号运算符是从左到右的顺序计算的。
但是如:for(int i = 0, j = 10; i < j; i++)中的逗号, 不是逗号运算符,是平等关系

25 goto label中标号的作用域是整个函数

26 程序是什么的集合

函数定义和变量定义

27 函数间通信方式

参数、返回值、外部变量

28 return; 不返回值

29 函数声明:有参数就要声明,没有参数需要使用void

30 internal是定义在函数内部的变量和函数参数

不允许在函数内部定义函数,因为函数本身是外部的

31 外部链接是什么?

对同一名字的外部变量的多次引用实际上是引用同一个变量

32 外部变量的特点

作用域更广,生命周期更长
外部变量只能在定义时初始化

33 什么时候必须使用extern?

  • 在外部变量定义前使用外部变量
  • 外部变量定义和使用不在同一文件

34 extern不一定要指明数组的数量

extern int array[];

35 静态变量(static)用于外部变量,内部变量和函数

static用于:
外部变量: 其他文件不可见,不会与其他文件同名变量冲突
函数:仅在所在文件可见
内部变量:仅能在所在函数中使用,但一直占据空间

36 寄存器变量(register)

特点

  1. 程序更小、执行更快
  2. 寄存器变量只适用于自动变量、函数形参
  3. 对寄存器变量使用的限制

实际机器上
1. 只有较少变量可以放入寄存器
2. 只有部分特定类型的变量才能存入

注意

  1. 过量寄存器声明没有什么害处(编译器会合理安排或者忽略)
  2. 寄存器变量地址不能引用

37 外部变量和静态变量初始化

在没有显式的情况下,初始化为0
初始化表达式必须是常量表达式,且只初始化一次

38 自动变量和寄存器变量初始化

在没有显式的情况下,初始化为垃圾值
初始化表达式可以不是常量表达式:表达式中可以包含任意之前定义的值,包括函数调用。

39 数组初始化

*数组初始化可以跟初始化表达式列表:int a[10] = {1, 3, 7, 2, 9};
* 如果初始化列表的值比需要的少,对于自动变量,静态变量,外部变量没有初始化元素值为0

40 字符数组初始化

有两种方法:

char string[40] = "hello feather"
char string[40] = {'h','e','l','l','o',' ','f','e','a','t','h','e','r','\0'};//这里需要加上结束标志\0

41 #include

根据相应规则在库目录里查找
"my.h" 先在源文件所在位置查找该文件,如果没找到,则根据相应规则在库目录里查找

42 宏替换使用反斜杠\换行

43 “”里的宏不会替换

例如:

#define YES hello
printf("YES");//不会用hello 替换YES

44 宏的妙用:宏调用能不考虑数据类型

比如max(a, b)是函数,对于a,b不同的数据类型会有不同的max版本

而使用宏

#define max(A, B) ((A)>(B)?(A):(B)) 
//记得宏调用中每个部分都要用上(),防止产生副作用

45 宏替换文本中参数以#开头

如:

#define dprintf(expr) printf(#expr "= %g\n", expr)`
/*调用*/
dprintf(x/y)
/*等效于*/
printf("x/y" " = %g\n", x/y);
/*因为“”有链接字符串功能,所以等效为*/
printf("x/y = %g\n", x/y);

46 宏替换中##的作用

如果替换文本中的参数与##相邻,则该参数将被实际参数替换,且##前后空白删除

#define paste(front, back) front##back
paste(name,1);
//结果将建立记号name1

47 #if对其中常量整型表达式求值,判断

常量整型表达式不能包含:
1. sizeof
2. 类型转换运算符
3. enum常量

48 ANSIC C采用类型void 代替char 作为通用指针类型

49什么是指针?

是能够存在一个地址的一组存储单元(通常是2个或者4个字节)

50 &取地址符不能用于什么?

不能用于表达式,常量和register变量

51 *ip++是什么意思?

会先将ip的值加一,然后再取出其中的值。*和++这类一元运算符符合从右到左的结合顺序。
这和(*ip)++结果不一样。

52 指针和数组注意点

  1. 数组下标能够完成的任何操作都可以通过指针来实现
  2. 一般指针编写的程序执行更快,但难理解
  3. 数组元素a[i],C语言会将其转换为*(a+i)
  4. p[-2],p[-1]语义上是合法的,但是这样做是非法的

不同之处

指针是变量:可以pa++或者pa=a
但是对于数组a++和a=pa都是非法的

53 alloc,afree作用

以栈的形式进行管理,此外afree的调用顺序必须和alloc相反!
malloc和free没有这样的限制

54 指针和整数之间不能相互转换,但是0是惟一的例外

0一般为NULL

55 指针之间的比较

  1. 任何指针和0进行相等和不等的比较都有意义(但是必须是同一个数组的元素)
  2. 指针可以和最后的元素之后的元素地址进行比较

56 指针的减法运算,能得到两个指针之间元素的数目

58 strcpy的实现

void strcpy(char *s, char *t)
{
    while( *s++ = *t++ )
        ;
}

59 strcmp的实现

void strcmp(char *s, char *t)
{
    int i;
    for(i = 0; s[i] == t[i]; i++)
    {
        if(s[i] == '\0')
            return 0;
    }
    return s[i] - t[i];
}

请再次用指针形式来实现。

60 二位数组为参数,必须指明数组的列数

func(int a[3][10]);
func(int a[][10]);
func(int *a[10]);

除数组一维可以不指定大小,其余各维必须制定大小

61 指针数组初始化

char *name[]={"wang", "zhang"};

62 指针数组最频繁的用处:保存不同长度的字符串

63 指针和多维数组的区别

int a[10][20]; //a分配了200个int类型数据的大小
int *b[10];    //b仅仅分配了10个int指针的大小

64 命令行参数

int main(int argc, char * argv[]);
argv[argc]必须为NULL

65 函数指针

int (*comp)(void *, void *); //函数指针
int * comp(void *, void *); //函数

66 char (*(*x())[])() 是什么意思

67 指针之间的加是非法的,减是合法的

68 结构体可以有哪些赋值操作?

  1. 复制
  2. 赋值
  3. 作为函数操作传入
  4. 作为函数返回值

69 什么是结构标记?

struct point{
    int x;
    int y;
};

70 结构标记、结构成员、普通变量可以同名

71 结构体变量声明和初始化

声明变量:struct {...} x, y, z;
初始化: struct point maxpt = {320, 220};//必须是常量

72 结构体间不可以比较

73 指针:下面表达式如何解释?

*p->str
*p->str++
*p++->str
(*p->str)++

74 sizeof用法和返回值是什么?

  1. sizeof 对象 sizeof a
  2. sizeof(类型名) sizeof(int)
    返回值:
    size_t 一般是无符号整型,定义在 stddef.h

75 宏中用sizeof得到数组元素个数

#define NKEYS (sizeof keytab/ sizeof(struct key))
#define NKEYS (sizeof keytab/ sizeof keytab[0])

第二种方法最好,能够无视数组元素的类型

76 #if中不能使用sizeof,但是#define可以

因为#define并不需要求值

77 &取址注意点

  1. &table[-1] 绝对是非法的
  2. &table[n] 是合法的,但是对该地址进行取值是非法的
    对数组最后的元素的下一个元素取址是合法的

78 结构体的自引用

struct node{
    char name[MAXSIZE];
    struct node * left; //仅仅使用了自身的指针,是可以的
};

但是如果使用了struct node tnode;这是非法的,不能使用自身的实例

79 typedef类型定义作用和优点

作用

并没有创建新的类型,但是创建了新的类型名

优点

  1. 表达方式更加简洁
  2. 为程序提供了更好的说明性,Tree比复杂的结构体指针要更容易理解
  3. 使程序参数化,提高可移植性

80 联合:union

    union type{
        int a;
        float b;
        char *c;
    }u;

作用:一个变量可以合法保存多种数据类型中的某一类型的对象
要求:需要能装下最大的成员变量,具体长度与实现有关。

81 位字段(bit-field)

提供了和宏一样操作位的方法

struct{
    unsigned int is_keyword :1;
    unsigned int is_extern  :1;
    unsigned int is_static  :1;
}flags;

冒号后为字段的宽度(二进制)
特殊宽度0可以用来强制在下一个字边界上对齐
字段仅仅能申明为int
字段不是数组,并且没有地址。

82 scanf忽视格式串中的空格和制表符

83 system(char *s)作用?

执行s中的命令,然后继续执行当前程序

84 math.h中有20多个数学函数,常用的如下

sin(x),cos(x),exp(x),log(x),log10(x),pow(x,y),sqrt(x),fabs(x)

85 随机数生成

使用rand()生成随机数,但是这是伪随机数
使用srand(unsigned int)用于装载随机数的种子。可以使用time(0)获取当前时间来装载种子

下面是例子:

srand(time(0));
while(1)
{
    printf("%d\n", rand());
}

86 getchar的实现

一次read读入一组数据,然后每次调用getchar返回一个字符

int getchar(void)
{
    static char buf[BUFSIZE];
    static char *bufp = buf;
    static int n = 0;
    if( n == 0) //缓冲区为空
    {
        n = read(0, buf, sizeof buf);
        bufp = buf;
    }
    return (--n >= 0) ? (unsigned char)*bufp++ : EOF;
}

87 布尔等关键字

_Bool 布尔,只有真假

C99

_Complex 复数
_Imaginary 虚数

头文件 stdbool.h
使用bool代替_Bool

88 iso646.h中 && || !分别用 and or not 替换

89 scanf(“%*d%*d%d”, &n);含义?

输入12,13,14 将14给n,前面2个舍去

90 printf(“%*d”, 10, a);实际格式为%10d

91 循环和递归优缺点?如何用递归求数的二进制?

循环:更快且节省内存
递归:慢且消耗内存
代码如下:

void f(int n)
{
    int r = n % 2;
    if(n > 1)
        f(n / 2);
    printf("%d", r);
}

92 C99指定初始化项目

int arr[6] = {[5] = 223};其余自动为0

93 指针问题

int **pt;
int a[3][2];
int (*pa)[3];

如下赋值:
pt = a; //错误!一个是指针的指针,一个是数组的指针
*pt = a[0]; //可以,两个都是是int的指针
pa = a; //错误!,pa是3个int的数组指针,a是两个int的数组指针

94 C99变长数组,其使用方式和限制

int a = 4;
int b = 5;
int array[a][b]; //是可以的

限制:

  1. 必须为自动存储类,必须在函数内部或者作为函数参数声明
  2. 声明时不能初始化
  3. 作为函数参数:int sum(int r, int c, int a[r][c])
    r,c必须在数组之前定义

95 C99 复合文字

/*普通数组声明*/
int a[2] = {10, 20};
/*复合文字*/int [2]){10, 20}; //or
(int []){30, 40};

通常使用指针来保存

int *p = (int [2]){10, 20};

96 *”hello”的含义是字符h

97 string.h中实用的函数

  1. char * strchr(char *s, int c) 返回s中存在的第一个c的位置,没找到返回null
  2. char * strrchr(char *s1, int c) 返回s中存在的最后一个c的位置,没找到返回null
  3. char * strpbrk(char *s1, char *s2); s1中存放s2中任何一个字符的第一个位置
  4. char * strstr(char * s1, char *s2); //s1字符串中第一次出现s2字符串的位置

98 字符串和数字间的相互转换

atoi(s) ,以字符串为参数,返回相应整数
atol(s), 字符串转换为long
atof(s), 字符串转换为double

99 对函数参数不能使用static

int w(static int a);错误!!!

100 register何时使用和注意点

应用于裸机开发,使变量尽可能保存在寄存器中
适合:CPU支持的数据类型,自动变量,函数形参,
不适合:全局变量,函数

注意点:不能给register变量取&

101 static作用

声明为静态变量,在程序结束后才释放(保存在全局数据区,默认初始化为0)

作用

能修饰变量和函数
1. 全局静态变量 :文件作用域(在当前文件可访问,其他文件不可见)具有静态存储时期
2. 局部静态变量: 代码块作用域, 具有静态存储时期
3. 静态函数:文件作用域,函数名对其他文件不可见

何时使用static?

  1. 使局部变量在整个程序期间可用,能防止其他代码块修改
  2. 使全局变量 防止其他文件修改该变量

102 const修饰的变量是常量?

错误!!!!
声明为只读变量,而非常量,可以间接修改其值。

 const int a = 10;
int * p = &a;
printf("%d\n", a);
*p = 11;
printf("%d\n", a);

103 宏定义特点

是符号常量
优点: 提高程序的自注释性
缺点:不安全,不语法检查

104 宏函数特点

适合简短、频繁的函数
优点:高效
不适合复杂的函数

105 枚举的特点

不同名可以相同值
优点:
1. 自动生成常量值
2. 提高自注释性
3. 编译时进行语法检查

106 数组和指针的区别

数组 指针
数组名为指针常量,不可以自加 指针可以自加
数组给所有元素分配空间 指针仅仅分配指针的空间
直接访问 间接访问
要注意防止越界 要注意防止内存泄露

107 C语言的优点和缺点

缺点:
1. 维护性差
2. 复用性差
3. 扩展性差
优点:
1. 适合底层开发
2. 能做界面,绘图等工具
3. 适合做服务器
4. 驱动开发

108 typedef禁止声明中使用一个以上存储类说明符,也就是其他存储类说明符不可以作为typedef的一部分。

109 volatile是什么?如何使用?

volatile是表示该变量容易改变。使用语法和const一致

volatile int local;//local是易变的值
volatile int *p;   //p指向易变的值

解释

一般的程序会暂时使用寄存器中的值,有时候就会出错。volatile修饰的值直接从变量地址获取值,保证数据的一致性和正确。

restrict 作用(C99)

只能用于指针,表示指针是访问一个数据对象唯一且初始的方法

110 memcpy、memmove作用和注意点

将一个位置的字节复制到另一个位置

void *memcpy(void *restrict des, void *restrict src, size_t n);
void *memmove(void *restrict des, void *restrict src, size_t n);

memcpy要求两个位置间不能重叠
memmove允许重叠(但是更危险)
* 确保没有重叠时,使用memmove

111 fgetpos、fsetpos作用

fpos_t 类型能处理较大数的定位函数(20亿字节以上)

int fsetpos(FILE * stream, const fpos_t * pos);
        //*pos 使得文件指向该数值所示的位置
int fgetpos(FILE * stream, fpos_t * pos);
        //Return:0 if OK, 值放于pos

112 结构指定初始化(C99)

struct book library = {.number = 2, .title = “The”, 10};

注意,对特定成员最后的赋值才是其实际值

113 与数组不同,结构的名字不是该结构的地址,必须使用&

114 符合文字和结构(C99)

struct book read;
read = (struct book){22, “hello”, 6.9};

115 伸缩型数组成员(C99)

使用

struct flex{
    int count; 
    double scores[];
};
//如果要使得`scores`表示含5个double
struct flex * p = (struc flex *)malloc(sizeof(struct flex) + 5 * sizeof(double));

p->scores[2] = 10; //访问数组成员

声明规则

  • 必须是最后一个数组成员
  • 结构体中至少含有一个其他成员

116 内联函数(C99)

使用

函数说明符 inline

#include 
inline void func(void)
{
    ;
}
int main()
{
    func();
    return 0;
}

作用:可能会简化函数的调用机制,但也可能不起作用

117 qsort(),stdlib.h提供的快速排序

void qsort(void *base, //要排序的数组的首地址(数组名)
        size_t nmemb, //排序的数量
        size_t size,  //数组每个元素的大小
        int(* compare)(const void *, const void * ) //自定义比较数组的元素
        );

使用方法:

int double_compare(const void * p1, const void * p2)
{
    double ac = 0.001;
    const double * a = (const double * )p1;
    const double * b = (const double * )p2;
    if(fabs(*a - *b) < ac) return 0;
    else if(*a - *b > ac) return 1;
    else
        return -1;
}
qsort(array, 10, sizeof(double), double_compare);
//能比较结构体
qsort(array, 10, sizeof(struct node), mycompare);

你可能感兴趣的:(C)