简介

C 语言是一种通用的、面向过程式的计算机程序设计语言。1972 年, 为了移植与开发 UNIX 操作系统, 丹尼斯·里奇在贝尔电话实验室设计开发了 C 语言。

  • 易于学习。
  • 结构化语言。
  • 它产生高效率的程序。
  • 它可以处理底层的活动。
  • 它可以在多种计算机平台上编译。

环境设置

UNIX/Linux

1
2
3
4
5
6
7
$ gcc -v
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr .......
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-46)

如果未安装 GCC,那么请按照 http://gcc.gnu.org/install/ 上的详细说明安装 GCC。

Mac OS

最快捷的获取 GCC 的方法是从苹果的网站上下载 Xcode 开发环境。

Xcode 目前可从 https://developer.apple.com/technologies/tools/ 上下载。

1
2
3
4
5
6
7
8
$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/incl
ude/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.38)
Target: x86_64-apple-darwin17.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Windows

安装 MinGW。MinGW 的主页 http://www.mingw.org。

至少要安装 gcc-core、gcc-g++、binutils 和 MinGW runtime。

Hello World

C 程序主要包括以下部分:

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

例子

1
2
3
4
5
6
#include <stdio.h>
int main() {
printf("Hello, World! \n");
return 0;
}
1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main() {
float f;
printf("Enter a number: ");
// %f 匹配浮点型数据
scanf("%f",&f);
printf("Value = %f", f);
return 0;
}
  • 程序的第一行 #include <stdio.h> 是预处理器指令, 告诉 C 编译器在实际编译之前要包含 stdio.h 文件。
  • 下一行 int main() 是主函数, 程序从这里开始执行。
  • 下一行 /*...*/ 将会被编译器忽略, 这里放置程序的注释内容。它们被称为程序的注释。
  • 下一行 printf(...) 是 C 中另一个可用的函数, 会在屏幕上显示消息 "Hello, World!"
  • 下一行 return 0; 终止 main() 函数, 并返回值 0

编译 & 执行 C 程序

  • 打开一个文本编辑器, 添加上述代码。
  • 保存文件为 helloword.c。
  • 打开命令提示符,进入到保存文件所在的目录。
  • 输入 gcc helloword.c,按回车, 编译代码。
  • 如果代码中没有错误, 命令提示符会跳到下一行, 并生成 a.out 可执行文件。
  • 现在, 输入 ./a.out 来执行程序。
  • 可以看到屏幕上显示 “Hello World”。

关键字

-
auto else long switch break enum register
case extern return union char float short
unsigned const for signed void continue goto
sizeof volatile default if static while do
int struct _Packed double typedef

数据类型

- 类型与描述
基本类型 整数类型和浮点类型
枚举类型 被用来定义在程序中只能赋予其一定的离散整数值的变量
void 类型 表明没有可用的值
派生类型 指针类型、数组类型、结构类型、共用体类型和函数类型

整形

类型 存储大小 值范围
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
4
5
6
7
#include <stdio.h>
#include <limits.h>
int main() {
printf("int 存储大小 : %lu \n", sizeof(int));
return 0;
}

浮点类型

类型 存储大小 值范围 精度
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 位小数
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <float.h>
int main() {
printf("float 存储最大字节数 : %lu \n", sizeof(float));
printf("float 最小值: %E\n", FLT_MIN);
printf("float 最大值: %E\n", FLT_MAX);
printf("精度值: %d\n", FLT_DIG);
return 0;
}

变量

变量定义

1
2
type variable_list;
type variable_name = value;

type 必须是一个有效的 C 数据类型, 可以是 char、w_char、int、float、double、bool 或任何用户自定义的对象。

1
2
3
4
5
6
7
8
9
int i, j, k;
char c, ch;
float f, salary;
double d;
extern int d = 3, f = 5; // d 和 f 的声明与初始化
int d = 3, f = 5; // 定义并初始化 d 和 f
byte z = 22; // 定义并初始化 z
char x = 'x'; // 变量 x 的值为 'x'

不带初始化的定义: 带有静态存储持续时间的变量会被隐式初始化为 NULL(所有字节的值都是 0), 其他所有变量的初始值是未定义的

变量声明

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

实例:

1
2
extern int i; //声明,不是定义
int i; //声明,也是定义
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
#include <stdio.h>
// 变量声明
extern int a, b;
extern int c;
extern float f;
int main () {
/* 变量定义 */
int a, b;
int c;
float f;
/* 初始化 */
a = 10;
b = 20;
c = a + b;
printf("value of c : %d \n", c);
f = 70.0/3.0;
printf("value of f : %f \n", f);
return 0;
}

常量

定义常量:

  • 使用 #define 预处理器。
  • 使用 const 关键字。

#define 预处理器

1
#define identifier value

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
int area;
area = LENGTH * WIDTH;
printf("value of area : %d", area);
printf("%c", NEWLINE);
return 0;
}

const 关键字

1
const type variable = value;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
printf("value of area : %d", area);
printf("%c", NEWLINE);
return 0;
}

PS: 把常量定义为大写字母形式。

存储类

  • auto
    • auto 只能用在函数内,即 auto 只能修饰局部变量。
  • register
    • 用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置。
  • static
    • 指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。
  • extern
    • 用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。

运算符

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 位运算符
  • 赋值运算符
  • 杂项运算符

判断

判断语句

  • if 语句
  • if…else 语句
  • switch 语句

三元运算符

1 : 0 ? 0;

循环

  • while 循环
  • for 循环
  • do…while 循环

循环控制语句

  • break 语句
  • continue 语句
  • goto 语句

函数

每个 C 程序都至少有一个函数,即主函数 main()。

定义函数

1
2
3
return_type function_name(parameter list) {
return 0;
}

函数声明

函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。

1
return_type function_name( parameter list );

实例:

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 max(int num1, int num2);
int main () {
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
/* 调用函数来获取最大值 */
ret = max(a, b);
printf( "Max value is : %d\n", ret );
return 0;
}
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) {
/* 局部变量声明 */
int result;
if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
  • 内部函数 static int max(int a,intb)
  • 外部函数 extern int max (int a,intb)
  • 内联函数 inline void swap(int *a, int *b)
    • 递归函数不能定义为内联函数
    • 内联函数一般适合于不存在while和switch等复杂的结构且只有1~5条语句的小函数上,否则编译系统将该函数视为普通函数。
    • 内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。
    • 对内联函数不能进行异常的接口声明。

作用域

局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
int main () {
/* 局部变量声明 */
int a, b;
int c;
/* 实际初始化 */
a = 10;
b = 20;
c = a + b;
printf ("value of a = %d, b = %d and c = %d\n", a, b, c);
return 0;
}

全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
/* 全局变量声明 */
int g;
int main () {
/* 局部变量声明 */
int a, b;
/* 实际初始化 */
a = 10;
b = 20;
g = a + b;
printf ("value of a = %d, b = %d and g = %d\n", a, b, g);
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
27
#include <stdio.h>
/* 全局变量声明 */
int a = 20;
int main () {
/* 在主函数中的局部变量声明 */
int a = 10;
int b = 20;
int c = 0;
int sum(int, int);
printf ("value of a in main() = %d\n", a);
c = sum( a, b);
printf ("value of c in main() = %d\n", c);
return 0;
}
/* 添加两个整数的函数 */
int sum(int a, int b)
{
printf ("value of a in sum() = %d\n", a);
printf ("value of b in sum() = %d\n", b);
return a + b;
}

数组

所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。

声明数组

1
2
3
type arrayName [arraySize];
//例子
double balance[10];

初始化数组

1
2
3
4
//声明大小
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
//不声明大小
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};

访问数组中数据:

1
double salary = balance[9];

指向数组的指针

1
double balance[50];

balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。

1
2
3
4
double *p;
double balance[10];
p = balance;

*(balance + 4) 是一种访问 balance[4] 数据的合法方式。

指针

什么是指针?

指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。

1
2
3
4
5
6
type *var-name;
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
int main () {
int var1;
char var2[10];
printf("var1 变量的地址: %p\n", &var1 );
printf("var2 变量的地址: %p\n", &var2 );
return 0;
}

使用指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main () {
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("Address of var variable: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %p\n", ip );
/* 使用指针访问值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}

字符串

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
struct [structure tag]
{
member definition;
member definition;
...
member definition;
} [one or more structure variables];
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;

访问结构体

1
2
3
4
5
6
7
8
//访问结构体
struct Books Book1;
Book1.title
//指向结构的指针
struct Books *struct_pointer;
struct_pointer = &Book1;
struct_pointer->title;

typedef

声明

1
2
typedef unsigned char BYTE;
BYTE b1, b2;
1
2
3
4
5
6
7
8
9
10
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
Book book;
book.title;

typedef vs #define

  • typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
  • typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。

内存管理

函数 描述
calloc 在内存中动态地分配 num 个长度为 size 的连续空间。
free 该函数释放 address 所指向的内存块,释放的是动态分配的内存空间。
malloc 在堆区分配一块指定大小的内存空间,用来存放数据。
realloc 该函数重新分配内存,把内存扩展到 newsize。

堆空间和栈空间

一个由C/C++编译的程序占用的内存分为以下几个部分:

  • 栈区(stack):又编译器自动分配释放,存放函数的参数值,局部变量的值等,其操作方式类似于数据结构的栈。

  • 堆区(heap):一般是由程序员分配释放,若程序员不释放的话,程序结束时可能由OS回收,值得注意的是他与数据结构的堆是两回事,分配方式倒是类似于数据结构的链表。

  • 全局区(static):也叫静态数据内存空间,存储全局变量和静态变量,全局变量和静态变量的存储是放一块的,初始化的全局变量和静态变量放一块区域,没有初始化的在相邻的另一块区域,程序结束后由系统释放。
  • 文字常量区:常量字符串就是放在这里,程序结束后由系统释放。
  • 程序代码区:存放函数体的二进制代码。