C语言面试知识点总结

#ウルトラマンコスモス(高斯)

一、前言

C 语言,这门古老而强大的编程语言,始终是计算机领域的基石。无论是操作系统的开发,还是嵌入式系统的编程,C 语言都发挥着至关重要的作用。

当我们踏入编程世界的大门,C 语言就像一位忠实的引路人,陪伴着我们走过无数的代码之旅。而在求职的道路上,C 语言的面试知识点更是我们必须跨越的重要关卡。

二、开始

2.1 简介

  1. 通用、面向过程的语言,1972 年,为了移植与开发 UNIX 操作系统,丹尼斯·里奇在贝尔电话实验室设计开发了 C 语言。
  2. 最新标准为 C11,之前标准为 C99。
# include <stdio.h>//头文件
//main 函数
int main(){
  printf("Hello World");
  return 0; //退出程序
}

2.1.1 特点

  1. 易于学习
  2. 结构化语言
  3. 产生高效率的程序
  4. 可以处理底层活动
  5. 多种计算机平台上编译

2.1.2 关于 C

  1. 为编写 UNIX 操作系统而发明
  2. 以 B 语言为基础
  3. 标准有 ANSI 制定
  4. 截止 1973年,UNIX 操作系统完全使用 C
  5. C 语言是最广泛的使用语言
  6. 大多数先进的软件都是 C 编写
  7. Linux 和关系型数据库(MySql) 等由 C 编写

2.1.3 为什么要使用 C

   所产生的代码运行速度与汇编语言编写代码速度几乎一样。

三、语法

3.1 C 程序结构

        组成 1.预处理器指令 2.函数 3.变量 4.语句、表达式 5.注释

3.2 C 的令牌(Tokens)

        C 由各种令牌组成,可以是关键字、标识符、常量、字符串常量、或标识符用来标识变量、函数等,由 A-Z、a-2、_下划线开始,不允许出现标点字符串(@、$、%),C 区分大小写

3.5 关键字

        常量名、变量名、或其他标识符名称

3.6 空格

        只包含空格的行,称为空白行。

3.7 数据类型

类型说明
基本类型整数类型、浮点类型
枚举类型也是算术类型,用来定义程序中只能赋予一定的离散整数值的变量
void 类型说明符,没有可用值
派生类型指针类型、数组类型、结构类型、共用体类型、函数类型

 数组类型和结构类型称为聚合类型

3.8 整数类型

类型字节大小
char1字节-128-127、0-255
unsigned char1字节0-255
signed char1字节-128-127
int2或4字节
unsigned int2或4字节
short2字节
unsigned int2字节
long4字节
unsigned long4字节

 获取某个类型、某个变量在特定平台上的准确大小,sizeof(type) 得到对象或类型的存储字节大小

3.9 浮点类型

类型存储大小值范围精度
float41.2E-38 3.4E+386
double82.3E-308 1.7E+30815
long double163.4E-1932 1.1E+493219

3.10 void 类型

  1. 函数返回为空
  2. 函数参数为空
  3. 指针指向 void (void * 的指针代表对象的地址)

3.11 变量

3.11.1 变量定义

type variable_list
int a;

3.11.2 变量声明

  1. 需要建立存储空间
  2. 不需要建立存储空间,通常使用 extern 关键字声明变量名而不去定义它
extern int i;//声明,不是定义,i 可以在别的文件中定义
int i;//声明,也是定义
//在一个源文件中引用另外一个源文件中定义的变量,加 extern
//addtwonum.c
extern int x;
extern int y;
int addtwonum(){
  return x+y;
}

//test.c
# include <stdio.h>
int x = 1;
int y = 2;
int addtwonum();
int main(void) {
 int result;
 result = addtwonum();
 return 0;
}

3.11.3 左值和右值

  1. 左值:可以出现在表达式左边右边
  2. 右值:只能出现在右边

3.12 常量

  常量是固定值,执行期间不会改变,又叫字面量

3.12.1 整数常量

221、215u、0xFeel

3.12.2 浮点常量

3.142

3.12.3 字符常量

'x'、'\t'

3.12.4 字符串常量

"hello"

3.12.5 定义常量

1.#define
  #define indentifier value
  #define MAX 100;

2.const
  const type variable = value
  const int MAX = 100;

3.13 存储类

   定义C 程序中变量/函数的范围(可见性)和生命周期,说明符放置在修饰类型之前

3.13.1 auto

   是所有局部变量默认的存储类,只能修饰局部变量

{
  int mount;
  auto int month;
}

3.13.2 register

  定义存储在寄存器而不是RAM中变量,最大尺寸等于寄存器大小,不能应用一元 & 运算符

{
  register int miles;
}

3.13.3 static

   编译器在程序生命周期内保持局部变量存在

3.13.4 extern

    提供一个全局变量引用,全局变量对所有程序文件可见,当年使用 extern,无法初始化变量,会把变量名指向一个之前定义过存储位置,修饰两个或过个文件共享相同的全局变量或函数时候。

//main.c
#include <stdio.h>
int count ;
extern void write_extern();
 
int main()
{
   count = 5;
   write_extern();
}

//support.c
#include <stdio.h>
extern int count;
 
void write_extern(void)
{
   printf("count is %d\n", count);
}

3.14 运算符

运算符
算术+、-、*、/、%、++、--
关系==、!=、>、<、>=、<=
逻辑&&、!、||
&、^ 、|
赋值=
杂项sizeof()、&(变量实际地址)、*(指向一个变量)、?:

3.15 判断

符号
if
if...else
switch
嵌套switch
三元 ? :

3.16 循环

3.16.1 循环类型

符号
while
for
do..while
嵌套

3.16.2 循环控制

符号简介
break
continue
goto将控制转移到标记的语句,不建议
#include <stdio.h>
int main ()
{
   int a = 10;
   LOOP:do
   {
      if( a == 15)
      {
         a = a + 1;
         goto LOOP;
      }
      printf("a 的值: %d\n", a);
      a++; 
   } while( a < 20 );
   return 0;
}

3.17 函数

   每个 C 语言都有一个函数,主函数 main()

# include<stdio.h>
int max(int num1,int num2);
int main() {
  int result = max(1,6);
}

int max(int num1,int num2){
  if(num1 > num2) {
    return num1;
  } else {
     return num2;
  }
}

3.17.1 函数参数

  1. 传值调用
  2. 引用调用(指针)

3.18 作用域规则

  1. 局部变量
  2. 全局变量
  3. 形式参数

3.19 数组

//声明数组
type arrayName[size];
double balance[10];
//初始化数组
double balance[2] = {100.0,2.0};
double balance[] = {100.0,2.0}
//访问数组
double i = balance[0];
//传递数组给函数
int *param
int param[10]
int param[]

3.20 枚举

enum DAY {
  //第一个默认为0,后面+1
  MON=1, TUE, WED, THU, FRI, SAT, SUN
}
//1.向定义枚举类型,再定义枚举变量
enum DAY {
  MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;

//2.定义枚举类型的同时定义枚举变量
enum DAY {
  MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

//3.省略枚举名称,直接定义枚举变量
enum  {
  MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

//遍历枚举
#include<stdio.h> 
enum 
{
    MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
    // 遍历枚举元素
    for (day = MON; day <= SUN; day++) {
        printf("枚举元素:%d \n", day);
    }
}

3.21 指针

  1. & 访问内存地址
  2. 指针是一个变量,其值为另一个变量的地址,内存位置的直接地址,就像其他变量或常量一样,必须先声明。
//1.声明
type *var-name
int *ip;
//2.使用
int var = 20;
int *ip = NULL; //空指针
ip = &var 
//2.1.在指针变量中存储的地址
printf("%p\n", ip) 
//2.2.使用指针访问值
printf("%d\n", *ip) 
//3.算术运算
//3.1.递增
ptr++ //加入 ptr 指向地址 1000 整型指针 ptr++ 为 1004
//3.2
int var[] = {100,200,300};
int i,*ptr;
//指向数组地址
ptr = var
for(i= 0;i < 3;i++){
   printf("值:%d\n", *ptr );//100 200 300
   ptr++;
}
//3.3.递减一个指针
ptr = &var[2];
for ( i = 3; i > 0; i--)
    {
      printf("%d\n", *ptr );
      /* 移动到下一个位置 */
      ptr--;
   }
 //3.4.指针比较
 ptr = var;
 i = 0;
 while ( ptr <= &var[MAX - 1] )
 {
     printf("Value of var[%d] = %d\n", i, *ptr );
     /* 指向上一个位置 */
     ptr++;
     i++;
 }  

3.21.1 指针数组

//1.
int *ptr[3];
ptr[i] = &var[i]; /* 赋值为整数的地址 */
printf("Value of var[%d] = %d\n", i, *ptr[i] );
//2.
const char *name[]={"zz","aa"};
printf("Value of names[%d] = %s\n", i, names[i] );

3.22.2 指向指针的指针

int var = 300;
int *ptr;
int *pptr
ptr = &var;
pptr = &ptr;
printf("%d",*ptr);
printf("%d",**pptr);

3.22.3 传递指针给函数

void getSeconds(unsigne long *par){
   *par = time(NULL);
}

3.22.4 函数中返回指针

int * getRandom(){
   static int  r[10];
   int i 
   /* 设置种子 */
   srand( (unsigned)time( NULL ) );
   for ( i = 0; i < 10; ++i)
   {
      r[i] = rand();
      printf("%d\n", r[i] );
   }
   return r;
}

3.23 函数指针

     指向函数的指针变量

// 声明一个指向同样参数、返回值的函数指针类型
typedef int (*fun_ptr)(int ,int)

int max(int x, int y)
{
    return x > y ? x : y;
}
// p 是函数指针 
int (* p)(int, int) = & max; // &可以省略
// 与直接调用函数等价,d = max(max(a, b), c)
d = p(p(a, b), c); 

3.24 回调函数

   回调函数是由别人的函数执行时调用你实现的函数

// 回调函数
void test(int (*getValue)(void))
{
    getValue();
}
int getValue(void)
{
    return 8;
}
test(getValue)

3.25 字符串

    字符串实际使用 null 字符 "\0" 终止的一维字符数组

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char greeting[] = "Hello";
操作简介
strcpy(s1, s2);复制字符串 s2 到字符串 s1。
strcat(s1, s2);连接字符串 s2 到字符串 s1 的末尾。
strlen(s1);返回字符串 s1 的长度。
strcmp(s1, s2);如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
strchr(s1, ch);返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
strstr(s1, s2);返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

3.26 结构体

1.C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,存储不同类型的数据项。

struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list ;

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

2.一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个

1.
struct 
{
    int a;
} s1;
2.
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
    int a;
    char b;
    double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
3.
//也可以用typedef创建新类型
typedef struct
{
    int a;
    char b;
    double c; 
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;

3.结构体可以包含其他结构体

//其他的结构体
struct COMPLEX
{
    char string[100];
    struct SIMPLE a;
};
 
//此包含了指向自己类型的指针
struct NODE
{
    char string[100];
    struct NODE *next_node;
};

4.如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明

//对结构体B进行不完整声明
struct B;   
//结构体A中包含指向结构体B的指针
struct A
{
    struct B *partner;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
    struct A *partner;
};

5.结构体变量的初始化

struct Books
{
   char  title[50];
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
booke.title;

6.访问结构成员

struct Books
{
   char  title[50];
};

struct Book book1;
//赋值
strcpy( Book1.title, "C"); 

7.结构作为函数参数

#include <stdio.h>
struct Book {
   char title[50];
};
void printBook(struct Book book);
int main()
{
   struct Book book1;
   strcpy(book1.title, "C");
   printBook(book1);
   return 0;
}

void printBook(struct Book book){
   printf( "Book title : %s\n", book.title);
}

8.指向结构的指针

//定义指针
struct Books *struct_pointer;
//存储地址
struct_pointer = &Book1;
//使用
struct_pointer->title;

#include <stdio.h>
struct Book {
   char title[50];
};
void printBook(struct Book *book);
int main()
{
   struct Book book1;
   strcpy(book1.title, "C");
   printBook(&book1);
   return 0;
}

void printBook(struct Book *book){
   printf( "Book title : %s\n", book->title);
}

3.27 位域

  1. 把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
  2. 例子 1.用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。 2.读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。
//定义
struct 位域结构名 
{
 位域列表
};
//data 为 bs 变量,共占2个字节,
struct bs{
   int a:8; //8位
   int b:2; //2位
   int c:6; //6位
}data;
//1.一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。
struct bs{
    unsigned a:4;
    unsigned  :4;   // 空域 
    unsigned b:4;   // 从下一单元开始存放
    unsigned c:4
}
//2.位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。如果最大长度大于计算机的整数字长,一些编译器可能会允许域的内存重叠,另外一些编译器可能会把大于一个域的部分存储在下一个字中。
//3.位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。
struct bs{
    int a:1;
    int  :2;    // 该 2 位不能使用
    int b:3;
    int c:2;
};
//4.位域使用 与 结构体一样
//位域变量名.位域名
//位域变量名->位域名

3.28 共用体

   特殊的数据类型,允许在相同的内存位置存储不同的数据类型。

//1.定义
union [union tag]
{
   member definition;
   member definition;
   ...
   member definition;
} [one or more union variables];

union Data
{
   int i;
   float f;
   char str[20]; //占用20个字节
} data;
//2.访问
union Data
{
   int i;
   float f;
   char  str[20];
};
union Data data; 
data.i = 10; 
data.f = 220.5; 
strcpy( data.str, "C"); //c
printf( "data.i : %d\n", data.i); // 1917853763
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
...
data.i = 10; 
printf( "data.i : %d\n", data.i); // 10

3.29 typedef

    使用它来为类型取一个新的名字

//1.给 unsigned char 取名 BYTE(大写)
typedef unsigned char BYTE;
BYTE  b1, b2;
//2.结构体取名字
typedef struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Book;
//使用
Book book;

3.29.1 typedef vs #define

  1. #define 是 C 指令,用于为各种数据类型定义别名。
  2. typedef 仅为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如定义 1 为 ONE。
  3. typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

3.30 输入、输出

标准文件文件指针设备
标准输入stdin键盘
标准输出stdout屏幕
标准错误stderr您的屏幕
  1. scanf() 从标准输入(键盘)读取并格式化
  2. printf() 格式化输出到标准输出(屏幕)

3.30.1 getchar() & putchar()

  1. getchar() :函数从屏幕读取下一个可用的字符,并把它返回为一个整数
  2. putchar():把字符输出到屏幕上,并返回相同的字符

3.30.2 gets() & puts()

  1. gets(): stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF
  2. puts():把字符串 s 和一个尾随的换行符写入到 stdout

3.31 文件读写

3.31.1 打开文件

FILE *fopen( const char * filename, const char * mode );
模式描述
r打开一个已有的文本文件,允许读取文件。
w打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+打开一个文本文件,允许读写文件。
w+打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

3.31.2 关闭文件

int fclose( FILE *fp );

3.31.2 写入文件

//字符写入到流中
int fputc( int c, FILE *fp );
//字符串 s 写入到 fp 所指向的输出流中
int fputs( const char *s, FILE *fp );

3.31.3 读取文件

// fp 所指向的输入文件中读取一个字符
int fgetc( FILE * fp );
//从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buf,并在最后追加一个 null 字符来终止字符串。
char *fgets( char *buf, int n, FILE *fp );

3.31.4 二进制 I/O 函数

//用于存储块的读写 - 通常是数组或结构体。
size_t fread(void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);
              
size_t fwrite(const void *ptr, size_t size_of_elements, 
             size_t number_of_elements, FILE *a_file);

3.32 预处理器

 不是编译器组成部分,编译过程中有个单独步骤,文本替换工具而已。

指令描述
#define
#include源文件代码
#undef取消宏
#ifdef宏已经定义,则返回真
#ifndef宏没有定义,则返回真
#if给定条件为真,则编译下面代码
#elseif 的替代方案
#elif前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个 #if……#else 条件编译块
#error当遇到标准错误时,输出错误消息
#pragma使用标准化方法,向编译器发布特殊的命令到编译器中

3.32.1 预定义宏

描述
_DATE_当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量
_TIME_当前时间,一个以 "HH:MM:SS" 格式表示的字符常量
_FILE_包含当前文件名,一个字符串常量
_LINE_包含当前行号,一个十进制常量
_STDC_当编译器以 ANSI 标准编译时,则定义为 1。

3.32.2 预处理器运算符

//1.宏延续运算符(\) 换行
#define  message_for(a, b)  \
    printf(#a " and " #b ": We love you!\n")

//2.字符串常量化运算符(#) 参数转换为字符串常量
#define  message_for(a, b)  \
    printf(#a " and " #b ": We love you!\n")
int main(void)
{
   message_for(Carole, Debra);
   return 0;
}

//3.标记粘贴运算符(##) 合并两个参数
#define tokenpaster(n) \
     printf ("token" #n " = %d", token##n)
int main(void)
{
   int token34 = 40;
   tokenpaster(34);//token34 = 40
   return 0;
}

//4.defined() 运算符 常量表达式中 确定一个标识符是否已经使用 #define 定义过
#include <stdio.h>
#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void)
{
   printf("Here is the message: %s\n", MESSAGE);  
   return 0;
}

//5.参数化的宏
#define MAX(x,y) ((x) > (y) ? (x) : (y))

头文件

//1.
#include <file>
#include "file"

3.33 强制类型转换

//1.类型转换
mean = (double)sum;
//2.整数提升
//把小于 int 或 unsigned int 的整数类型转换为 int 或 unsigned int

3.34 错误处理

errno、perror()、strerror()
perror():显示传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式
strerror():返回一个指针,指针指向当前 errno 值的文本表示形式。

程序退出状态
 exit(EXIT_FAILURE); -1
 exit(EXIT_SUCCESS);  0

3.35 递归

   函数自己调用自己

3.36 可变参数

//1.引入
#include <stdarg.h>

double average(int num,...){
   //2.定义va_list 类型变量
   va_list valist;
   //3.为 num 个参数初始化 valist 
   va_start(valist, num);
   //4.访问所有赋给 valist 的参数 
   for (i = 0; i < num; i++)
   {
       sum += va_arg(valist, int);
    }
   //5.清理为 valist 保留的内存 
   va_end(valist);
   return sum/num;
}

3.37 内存管理

//内存中动态地分配 num 个长度为 size 的连续空间
void *calloc(int num, int size);
//释放动态分配的内存空间
void free(void *address); 
//堆区分配一块指定大小的内存空间,用来存放数据
void *malloc(int num);
//重新分配内存,把内存扩展到 newsize
void *realloc(void *address, int newsize); 	

//动态分配内存
char name[100];

//重新调整内存的大小和释放内存
//在不需要内存时,都应该调用函数 free() 来释放内存

3.38 命令行参数

int main( int argc, char *argv[] )  
  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值