c 语言学习笔记

基础语法学习

1. 程序结构

c 语言程序由以下五部分组成:

  • 预处理器指令
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释

2.基本语法

基本由三大部分组成:

  1. C 的令牌: 关键字 标识符 常量 字符串值或者一个符号
  2. 分号: 英文符号的分号
  3. 注释: 单行注释 多行注释
  4. 标识符: 标识变量 函数 或者自定义项目名称
  5. 关键字: 基本 32 个关键字 之后 c99 和 c11 新增和许多关键字
1.数据类型
  1. 基本类型: 整数型和浮点数类型
  2. 枚举类型
  3. void 类型
  4. 派生类型: 指针类型、数组类型、结构类型、共同体类型和函数类型

整数类型

1
2
3
4
5
6
7
8
9
char	        1字节	    -128 到 127 或 0 到 255
unsigned char	1字节	    0 到 255
signed char	    1字节	    -128 到 127
int	            2或 4字节	-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int	2 或 4 字节	0 到 65,535 或 0 到 4,294,967,295
short	        2字节	    -32,768 到 32,767
unsigned short	2字节	    0 到 65,535
long	        4字节	    -2,147,483,648 到 2,147,483,647
unsigned long	4字节	    0 到 4,294,967,295

浮点类型

1
2
3
float	    4 字节	1.2E-38 到 3.4E+38	    6 位小数
double	    8 字节	2.3E-308 到 1.7E+308	15 位小数
long double	16 字节	3.4E-4932 到 1.1E+4932	19 位小数

void 类型

  1. 函数返回空
  2. 函数参数为空
  3. 指针指向 void
2.变量

变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储

1
2
3
4
int    i, j, k;
char   c, ch;
float  f, salary;
double d;

变量声明向编译器保证变量以指定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。

  • 1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
  • 2、另一种是不需要建立存储空间的,通过使用 extern 关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
  • 除非有 extern 关键字,否则都是变量的定义。
1
2
extern int i; //声明,不是定义
int i; //声明,也是定义
3.常量

整数常量 整数常量可以是十进制、八进制或十六进制的常量。 0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。 整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

1
2
3
4
5
6
7
85         /* 十进制 */
0213       /* 八进制 */
0x4b       /* 十六进制 */
30         /* 整数 */
30u        /* 无符号整数 */
30l        /* 长整数 */
30ul       /* 无符号长整数 */

浮点常量 浮点常量由整数部分、小数点、小数部分和指数部分组成。

1
2
3.14159       /* 合法的 */
314159E-5L    /* 合法的 */

字符常量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
\\	\ 字符
\'	' 字符
\"	" 字符
\?	? 字符
\a	警报铃声
\b	退格键
\f	换页符
\n	换行符
\r	回车
\t	水平制表符
\v	垂直制表符
\ooo	一到三位的八进制数
\xhh . . .	一个或多个数字的十六进制数

字符串常量

1
2
3
4
5
"hello, world"

"hello, \

world"

定义常量

  • 1.使用 #define 预处理器。
  • 2.使用 const 关键字。
1
2
3
4
5

#define LENGTH 10
#define WIDTH  5
const int  LENGTH = 10;
const int  WIDTH = 5;
4.存储类

存储类定义 C 程序中变量/函数的范围(可见性)和生命周期。

  • auto
  • register
  • static
  • extern auto 存储类 所有局部变量默认的存储类
1
2
3
4
{
   int mount;
   auto int month;
}

register 存储类 register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。

1
2
3
{
   register int  miles;
}

static 存储类 static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

1
static int count = 10

extern 存储类 extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int count ;
extern void write_extern();

int main()
{
   count = 5;
   write_extern();
}
1
2
3
4
5
6
7
8
#include <stdio.h>

extern int count;

void write_extern(void)
{
   printf("count is %d\n", count);
}
5.运算符
  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 杂项运算符
1
+ - * / % ++ --

关系运算符

1
== != > < >= <=

关系运算符

1
&& || !

位运算符

1
& | ^ ~ << >>

赋值运算

1
= += -= *= /= %= <<= >>= &= ^= !=

杂项运算符 ↦ sizeof & 三元

1
sizeof() & * x==y?true:false
6.判断和循环
1
if if..else switch  while for do...while break continue goto
7.函数

定义函数

  • 返回类型
  • 函数名称
  • 参数
  • 函数主体
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int max(int a,int b)
{
    int res;
    if(a>b){
        res=a;
    }else{
        res=b;
    }
    return res;
}

函数声明

1
int max(int a,int b);

调用函数

1
int res = max(100,101);

作用域

  • 局部变量
  • 全局变量
8.数组

一个固定大小的相同类型元素的顺序集合。 声明数组

1
double balance[10];

初始化数组

1
2
double balance[5]={100.0,12.0,4.0,5.0,8.0};
double balance[]={100.0,12.0,4.0,5.0,8.0};

访问数据

1
double seconde=balance[1];
9.enum 枚举
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum season {spring, summer=3, autumn, winter};

// 整数转换成枚举
int a=3;
enum DAY weekend;
weekend= (enum DAY) a;
printf("weekend: %d",weekend);
10.指针

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。 声明指针

1
2
3
4
int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;     /* 一个字符型的指针 */

使用指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>

int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */

   ip = &var;  /* 在指针变量中存储 var 的地址 */

   printf("变量的地址: %p\n", &var  );
   /* 在指针变量中存储的地址 */
   printf("ip变量存储的地址: %p\n", ip );
   /* 使用指针访问值 */
   printf("*ip 变量的值: %d\n", *ip );
   return 0;
}

NULL 指针

1
2
3
4
5
6
7
#include <stdio.h>
int main ()
{
   int  *ptr = NULL;
   printf("ptr 的地址是 %p\n", ptr  ); //ptr 的地址是 0x0
   return 0;
}
11.函数指针

函数指针是指向函数的指针变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int max(int x, int y)
{
    return x > y ? x : y;
}
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略
    int a, b, c, d;

    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);

    /* 与直接调用函数等价,d = max(max(a, b), c) */
    d = p(p(a, b), c);

    printf("最大的数字是: %d\n", d);
    return 0;
}

回调函数 函数指针作为某个函数的参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdlib.h>
#include <stdio.h>

// 回调函数
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}

// 获取随机值
int getNextRandomValue(void)
{
    return rand();
}

int main(void)
{
    int myarray[10];
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}
12.字符串

字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。

1
2
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char greeting[] = "Hello";

字符串操作函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 复制字符串
strcpy(s1,s2);

# 连接字符串
strcat(s1,s2);

# 获取字符串长度
strlen(s1);

# 比较字符串 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。
strcmp(s1:s2);

# 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
strchr(s1,ch);

# 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
strstr(s1, s2);
13.结构体

用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 只声明结构体
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
};

// 声明结构体,并声明一个book变量
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;


// 也可以用typedef创建新类型
typedef struct
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Books;

struct Books b1, b2[20], *b3;

// 初始化结构体变量
struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};

// 访问成员变量
struct Books Book1;
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);

位域 “位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

1
2
3
4
5
struct bs{
    int a:8;
    int b:2;
    int c:6;
}data;

在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。

1
2
3
4
struct int16{
    int a:8;
    int b:8;
}
14.共同体

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。 您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <string.h>

union Data
{
   int i;
   float f;
   char  str[20];
};

//Data 类型的变量可以存储一个整数、一个浮点数,或者一个字符串。
//这意味着一个变量(相同的内存位置)可以存储多个多种类型的数据。


int main( )
{
   union Data data;

   printf( "Memory size occupied by data : %d\n", sizeof(data));

   data.i = 10;
   printf( "data.i : %d\n", data.i);

   data.f = 220.5;
   printf( "data.f : %f\n", data.f);

   strcpy( data.str, "C Programming");
   printf( "data.str : %s\n", data.str);
   return 0;
}

typedef typedef 为类型取一个新的名字

1
typedef unsigned char byte;
15.输入和输出
1
2
3
4
标准文件
# 标准输入	stdin	键盘
# 标准输出	stdout	屏幕
# 标准错误	stderr	您的屏幕
1
2
3
4
5
6
7

# int scanf(const char *format, ...) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
# int printf(const char *format, ...) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
# int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。
# int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。
# char *gets(char *s) 函数从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF。
int puts(const char *s) 函数把字符串 s 和一个尾随的换行符写入到 stdout
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
int main( ) {

   char str[100];
   char str2[100];

   printf( "scanf:Enter a value :");
   scanf("%s ", str);

   printf( "\nscanf:You entered: %s ", str);
   printf("\n");
   scanf("");

   printf( "gets:Enter a value :");
   gets( str2 );
    //直到一个终止符或 EOF
   printf( "\ngets:You entered: ");
   puts( str2 );


   int c;

   printf( "getchar:Enter a value :");
   c = getchar();
   //只存一个字符
   printf( "\ngetchar:You entered: ");
   putchar( c );
   printf( "\n");


   return 0;
}
16.文件读写
1
2
3
4
5
6
r	打开一个已有的文本文件,允许读取文件。
w	打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a	打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+	打开一个文本文件,允许读写文件。
w+	打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+	打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

打开文件

1
FILE *fopen( const char * filename, const char * mode );

关闭文件

1
int fclose( FILE *fp );

写入文件

1
2
3
4
5
# 写入单个字符
int fputc( int c, FILE *fp );

# 写入多个字符
int fputs( const char *s, FILE *fp );

读取文件

1
2
3
4
5
6
7
8
# 读取单个字符
int fgetc( FILE * fp );

# 读取多个字符
char *fgets( char *buf, int n, FILE *fp );

# 从文件中读取字符串 读到空格字符时会停止读取
int fscanf(FILE *fp, const char *format,char *buf, ...)

二进制 I/O 函数

1
2
3
4
5
6
# 读取文件
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);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main()
{
   FILE *fp = NULL;
   fp = fopen("/tmp/test.txt", "w+"); // 打开文件
   fprintf(fp, "This is testing for fprintf...\n"); //字符串输入到文件中
   fputs("This is testing for fputs...\n", fp); //直接输入字符串到 文件中
   fclose(fp);

   char buff[255];

   fp = fopen("/tmp/test.txt", "r");
   fscanf(fp, "%s", buff); //读取字符 遇到空格会退出
   printf("1: %s\n", buff );

   fgets(buff, 255, (FILE*)fp); //获取一行数据
   printf("2: %s\n", buff );

   fgetc((FILE*)fp); //获取一个字符数据

   fclose(fp);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#define ROWS 100
#define LEN 225
// 定义一个文本
char text[ROWS][LEN];

char buff[LEN];
/**
 * 输入字符串到文件
 * 再从文件中获取字符串并打印
 *
*/
int main(int argc, char *argv[])
{
    // 获取命令行参数,第一个参数是程序名,第二个参数是才是用户输入参数.
   if (argc<2)
   {
      printf("read file and input values ");
      printf("usage: %s <filename>",argv[0]);
      return 0;
   }
   FILE *fp = NULL;


   fp = fopen(argv[1], "w+"); // 打开文件
   int t, i,j;
   printf("enter empty to quit:\n");

    //用户输入多行字符串  当一行字符串为空时退出输入
   for (t = 0; t < ROWS; t++)
   {
        printf("%d:", (t+1));
        gets(text[t]);
        if (!*text[t])     //当输入的该行为空时   if条件成立,执行break语句跳出循环。
        {
            break;
        }
   }
   for (i = 0; i < t; i++)
   {
        //当text[i][j]==0时退出该行的打印
        // for (j = 0; text[i][j]; j++)
        // {
        //     // putchar(text[i][j]);   //逐个字符进行打印
        //     fputc(text[i][j], fp); // 逐个字符写入文件中
        // }

        fprintf(fp, "%s\n",text[i]);
        // fputs(text[i],fp);
        // fputc('\n', fp);
    }

   fclose(fp);

   printf("output:\n");
   fp=fopen(argv[1],"r");
   int bi=0;
   // 打印每行字符
   while((fgets(buff, LEN, (FILE*)fp))!=NULL){
        printf("%d: %s",++bi, buff );
   }
   fclose(fp);
   return 0;
}
17.预处理器

编译器在实际编译之前完成所需的预处理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#define	定义宏
#include	包含一个源代码文件
#undef	取消已定义的宏
#ifdef	如果宏已经定义,则返回真
#ifndef	如果宏没有定义,则返回真
#if	    如果给定条件为真,则编译下面代码
#else	#if 的替代方案
#elif	如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif	结束一个 #if……#else 条件编译块
#error	当遇到标准错误时,输出错误消息
#pragma	使用标准化方法,向编译器发布特殊的命令到编译器中
1
2
3
4
5
6
#undef  FILE_SIZE
#define FILE_SIZE 42

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

预定义宏

1
2
3
4
5
__DATE__	当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
__TIME__	当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
__FILE__	这会包含当前文件名,一个字符串常量。
__LINE__	这会包含当前行号,一个十进制常量。
__STDC__	当编译器以 ANSI 标准编译时,则定义为 1。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#include <stdio.h>
#define  message_for(a, b)  \
    printf(#a " and " #b ": We love you!\n")

#define tokenpaster(n) printf ("token" #n " = %d", token##n)

// 预处理运算器
#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

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

int main()
{
    //预定义宏
   printf("File :%s\n", __FILE__ );
   printf("Date :%s\n", __DATE__ );
   printf("Time :%s\n", __TIME__ );
   printf("Line :%d\n", __LINE__ );
   printf("ANSI :%d\n", __STDC__ );
   message_for("tom "," jerry");
   int token34=100;
   tokenpaster(34);
   puts("\n");
   printf("%s\n",MESSAGE);
   printf("Max between 20 and 10 is %d\n", MAX(10, 20));
}
18.头文件

头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享 有两种类型的头文件:

  • 程序员编写的头文件
  • 编译器自带的头文件。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <math.h>

// 引用自定义头文件
#include "base_func.h"

// 只引用一次头文件
#ifndef _GLOBAL_H
#define _GLOBAL_H "base_func.h"

// 有条件的引用头文件
#if LINUX
   # include "linux.h"
#elif WINDOWS
   # include "windows.h"
#elif IOS
   # include "ios.h"
#endif
19.强制类型转换

强制类型转换是把变量从一种类型转换为另一种数据类型。

1
char -> int->unsigned int -> float -> long -> unsigned long -> long long -> unsigned long long -> float -> double -> long double
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main()
{
    char a = 'a'; // a ascii值是97
    int b = 5;
    int c = a + b;
    printf("char[a=%c] + int[b=%d] => int[%d]\n", a, b, c);

    float f;
    f = b + c;
    printf("int[b=%d] + int[c=%d]=> float[%f]\n", b, c, f);

    double d;
    d = b + c;
    printf("int[b=%d] + int[c=%d]=> double[%f]\n", b, c, d);

    printf("double[d=%f] + 0.123 => case(int) => %d", d, (int)(d + 0.1234));
}
21.错误处理

C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据。 errno、perror() 和 strerror()

  • perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
  • strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

int main()
{
    FILE *pf;
    int errnum;
    pf = fopen("unexist.txt", "rb");
    if (pf == NULL)
    {
        errnum = errno;
        fprintf(stderr, "error no: %d\n", errno);
        perror("use perror output error");                          // 使用perro函数打印错误
        fprintf(stderr, "open file error: %s\n", strerror(errnum)); //使用strerror获取错误字符串
    }
    else
    {
        fclose(pf);
    }
    // 被零除错误
    int dividend = 20;
    srand((unsigned)time(NULL));
    int divisor = rand() % 2;
    int quotient;
    if (divisor == 0)
    {
        fprintf(stderr, "diversor is  0  exit...\n");
        exit(EXIT_FAILURE); // exit(-1)
    }
    quotient = dividend / divisor;
    fprintf(stderr, "quotient is  %d\n", quotient);
    exit(EXIT_SUCCESS); // exit(0)
    printf("this code never run");
    return 0;
}
22.递归
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <stdio.h>
int fac(int n);
int factorial(int n);
int fibo(int n);
int fibonaci(int n);
// 递归函数栗子
int main()
{
    int n = 10;

    // 测试阶乘
    for (int i = 0; i < n; i++)
    {
        int a = fac(i);
        int b = factorial(i);
        printf("[fac(%d)=%d] %s [factorial(%d)=%d] \n", i, a, (a == b ? "==" : "!="), i, b);
    }

    // 测试斐波那契
    for (int i = 0; i < n; i++)
    {
        int d = fibo(i);
        int c = fibonaci(i);
        printf("[fibo(%d)=%d] %s [fibonaci(%d)=%d] \n", i, d, (d == c ? "==" : "!="), i, c);
    }
}
// 正常无递归,求n的阶乘
int fac(int n)
{
    int s = 1;
    for (int i = n; i > 0; i--)
    {
        s *= i;
    }
    return s;
}
// 递归,求n的阶乘
int factorial(int n)
{
    if (n <= 1)
    {
        return 1;
    }
    return n * factorial(n - 1);
}
// 正常无递归, 斐波那契数列
int fibo(int n)
{
    int a = 0;
    int b = 1;
    for (int i = 0; i < n; i++)
    {
        int t = a + b;
        a = b;
        b = t;
    }
    return a;
}

// 递归,斐波那契数列
int fibonaci(int n)
{
    if (n == 0)
    {
        return 0;
    }
    if (n == 1)
    {
        return 1;
    }
    return fibonaci(n - 1) + fibonaci(n - 2);
}
23. 可变参数
1
2
3
4
5
// 语法 num 参数个数
double func(int num, ...)
{

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdarg.h> //引入动态参数的头文件
#include <stdio.h>

// num 表示参数个数
double average(int num, ...)
{

    va_list valist;
    double sum = 0.0;
    int i;

    /* 为 num 个参数初始化 valist */
    va_start(valist, num);
    /* 访问所有赋给 valist 的参数 */
    for (i = 0; i < num; i++)
    {
        int v = va_arg(valist, int); // 调用一次,就会拿到指向的参数,当调用num次就拿完了所有参数.
        printf("i = %d, arg = %d\n", i, v);
        sum += v;
    }
    /* 清理为 valist 保留的内存 */
    va_end(valist);

    return sum / num;
}

int main()
{
    printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2, 3, 4, 5));
    printf("Average of 5, 10, 15 = %f\n", average(3, 5, 10, 15));
}
24.内存管理

**<stdlib.h>**头文件中提供了内存分类和管理的函数.

1
2
3
4
5
6
7
void *calloc(int num, int size);  //在内存中动态地分配 num 个长度为 size 的连续空间,并将每一个字节都初始化为 0。所以它的结果是分配了 num*size 个字节长度的内存空间,并且每个字节的值都是0。

void free(void *address); //该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。

void *malloc(int num);  // 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。

void *realloc(void *address, int newsize);  // 该函数重新分配内存,把内存扩展到 newsize。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    char name[100];
    char *description;
    strcpy(name, "Tom Baby");
    char desc[] = "Tom Baby is a Boy and Very Handsome!";
    char rdesc[] = "Lily Baby is a Girl and Very Beautify, All of us very like it!";
    /* 动态分配内存 */
    // description = (char *)malloc(200 * sizeof(char));
    description = (char *)calloc(strlen(desc), sizeof(char));
    if (description == NULL)
    {
        fprintf(stderr, "Error - unable to allocate required memory\n");
    }
    else
    {
        strcpy(description, desc);
    }

    printf("Name = %s\n", name);
    printf("Description: %s\n", description);
    printf("Description length: %d\n", strlen(description));
    description = (char *)realloc(description, strlen(rdesc) * sizeof(char)); //从新分配内存
    if (description == NULL)
    {
        fprintf(stderr, "Error - unable to allocate required memory\n");
    }
    else
    {
        strcat(description, rdesc);
    }

    printf("new Description: %s\n", description);
    printf("new Description length: %d\n", strlen(description));
    free(description);
}