C语言

一. 第一段C

1
2
3
4
5
6
#include <stdio.h>

int main(){
printf("hello world");
return 0;
}

解析:

  • #include : 引入头文件

  • 所有程序从main函数开始执行。

  • printf:格式化输出到屏幕,依赖stdio.h头文件。

  • stdio.h :标准输入输出头文件,使用printf()函数时,没提前引入此头文件会编译出错

  • return 0:退出程序。

二. 数据类型

  1. 基本类型:算术类型,包括

    • 整数类型
    • 浮点类型
  2. 枚举类型

    也是算术类型,用来定义在程序中只能赋予一定的离散整数值的变量

  3. void类型

    类型说明符void说明没有可用的值。

  4. 派生类型

    • 指针类型
    • 数组类型
    • 结构类型:数组类型结构类型统称为聚合类型
    • 共用体类型
    • 函数类型:指的是函数返回值的类型

1. 整数类型

标准整数类型的的存储大小和值范围

类型 存储大小(字节) 值范围
char 1 -128到127或0到255
unsigned char 1 0到255
signed char 1 -128到127
int 2或4 -32768到32767或-2147483648到2147483647
unsigned int 2或4 0到65535或0到4294967295
short 2 -32768到32767
unsigned 2 0到65535
long 4 -2147483648到2147483647
unsigned 4 0到4294967295

各种数据类型存储大小和系统位数有关,需要得到该类型在当前系统上的大小,可以使用sizeof运算符

1
2
3
4
5
#include <stdio.h>
int main(){
printf("int 的大小:%lu \n", sizeof(int));
return 0;
}

结果:

1
int 存储大小 : 4

2. 浮点型

标准浮点型的存储大小,值范围,精度:

类型 存储大小(字节) 值范围 精度
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
#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("float 精度:%d\n", FLT_DIG);
return 0;
}

ps:%E是以指数形式输出单,双精度实数

三. 变量

变量其实只是程序可操作的存储区的名称。C语言中,每个变量都有类型,类型决定变量存储的大小。

变量命名必须以字母或下划线开头,后面可以跟着数字,字母和下划线,并且字母区分大小写。

1. 变量定义

变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。定义的时候需指定变量的数据类型,并包含一个或多个变量列表。

以下为变量的声明:

1
2
3
4
int a, b, c;
char d;
float e, f;
double g;

写多个变量名表示同事创建多个变量。

变量的初始化:

1
2
3
int a = 1;
int a = 1, b = 2;
char b = 'a';

2. 变量的声明

变量的声明向编译器保证变量以指定的类型和名称存在,变量的声明有两种情况:

  1. 需要建立存储空间。

    例如:int a在声明的时候就建立了存储空间。

  2. 不需要建立存储空间。

    通过extern关键字声明的变量名而不定义他,如:extern int a,a变量可以不在这定义,而是在其他文件进行定义

    除有extern关键字的,都是变量的定义。

    1
    2
    extern int a; //变量的声明
    int a; //变量的声明,也是变量的定义

3. 左值(Lvalues)和右值(Rvalues)

C中有两种类型的表达式:

  1. 左值

    指向内存位置的表达式称为左值表达式,左值表达式可出现在“=”左边或右边。

  2. 右值

    右值指的是存储在内存中某些地址的数值。右值不能进行赋值,也就是说右值不能出现在“=”左边,只能存在右边。

    1
    2
    int a = 1;	//a为左值,指向数字1在内存中所在的地址,故a可在等号左边和右边
    int b = a;
    1
    1 = 2; // 数值型为右值型,只能在等号右边,所以这样写会报错。

四. 常量

常量是固定值,在程序执行过程不会改变,这些固定的值也叫字面量。常量的值在定义后不能进行修改。

常量可以是任何基本数据类型,如字符常量,整数常量等。

1. 整数常量

带前缀整数常量可以是:

  • 十进制:不带前缀
  • 八进制:0为前缀
  • 十六进制:0x或0X为前缀

带后缀整数常量(后缀可大写可小写):

  • 后缀 U (或u):无符号整数(unsigned)
  • 后缀 L (或l):长整数(long)
  • UL或LU:无符号长整型

实例:

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

2. 浮点常量

浮点数由整数部分、小数点、小数部分、指数部分组成。浮点常量表现形式:

  • 小数形式:必须包含整数部分、小数部分,或两者都有

  • 指数形式:必须包含小数点、指数,或两者都有

3. 字符常量

字符常量是在单引号中,可以是以下类型:

  • 普通字符:例如’x’
  • 转义序列:例如:’\t’
  • 通用字符:例如’\u02C0’

4. 字符串常量

跟字符常量类似,但字符串常量括在双引号中””

5. 定义常量

在C中,有两种方式定义常用:

  1. 使用#define预处理器

    1
    #define LENGTH 10
  2. 使用const关键字

    1
    const int LENGTH = 10;

注意:通常把常量定义为大写字母形式

五. 存储类

C程序中,存储类定义程序中变量、函数的范围(可见性)和生命周期,这些说明符房子啊他们所修饰的类型之前。下面是C程序中可用的存储类:

  • auto
  • register
  • static
  • extern

1. auto

auto存储类是所有局部变量默认的存储类,auto只能用在函数类,也就是说auto只能修饰局部变量。

1
2
3
4
5
{
/* 下面定义的变量其实带有相同的存储类 */
int a;
auto int a;
}

2. register

register存储类用于定义存储在寄存器的局部变量,而不是RAM中的局部变量,这意味着变量的最大尺寸等于寄存器的大小,而且不能用’&’运算符对它进行运算(因为它没有内存位置)

1
2
3
{
register int length;
}

3. static

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

使用static修饰全局变量时,会使变量的作用域限制在声明它的文件内。只要方法跟变量在同个文件内,都可调用该变量。

4. extern

extern存储类用于提供一个全局变量的引用,全局变量对所有程序文件都是可见的。

当使用extern时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当有多个文件,且定义一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用extern来得到已定义的变量或函数。extern就是用来在另一个文件中声明一个全局变量或函数。

文件一:main.c

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

int count;
extern void print_count(void); //声明printcount.c中定义的函数

int main(){
count = 10;
print_count();
return 0;
}

文件二:printcount.c

1
2
3
4
5
6
#include <stdio.h>
extern int count; //声明在main.c中定义的count

void print_count(void){
printf("count is %d\n", count);
}

结果:

1
count is 10

六. 运算符

位运算符

位运算符作用于位,并逐位执行操作,真值表如下:

p q p&q(与) p|q(或) p^q(异或)
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 1 1 1

<<二进制左移运行:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0).

>> 二进制右移运算:将一个数的二进制位全部右移若干位,正数左补0;负数左bu1,右边丢弃。

杂项运算符

  • sizeof():返回变量的大小
  • &:返回变量的地址
  • *:指向一个变量

七. 判断

C语言中“非零即为真”,就是说非零非空认定为true,或null认定为false

八. 循环

for无限循环

由于构成for循环的三个表达式中任何一个都不是必须的,所以三个条件都为空就构成一个无限循环。

1
2
3
for( ; ; ){
//无限循环执行下去
}

九. 函数

函数是一组一起执行的任务语句。

函数的声明告诉编译器函数的名称、返回类型和参数;

函数的定义提供函数的实际主体。

函数的参数

形参:如果函数要使用参数,则必须声明接受参数的变量,这些变量就称为函数的形参。形参在进入函数时被创建,退出函数时被销毁。

函数调用时的两种传递参数方式:

  • 传值调用

    该方法把参数的实际值复制给函数的形参,此时修改函数的形参并不会影响实参

  • 引用调用

    通过指针传递,形参为指向实参地址的指针,当操作形参时,相当于同时在操作实参。

默认情况下,C语言使用传值调用。

十. 枚举

第一个枚举成员的默认值为整型0,后续枚举成员的值在前一个成员加1。

枚举变量的定义,有三种方式:

  1. 先定义枚举类型,再定义枚举变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    enum DAY{
    MON = 1,
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
    };
    enum DAY day;
  2. 定义枚举类型的同时定义枚举变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum DAY{
    MON = 1,
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
    }day;
  3. 省略枚举名称,直接定义枚举变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum{
    MON = 1,
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
    }day;

十一. 指针

内存地址:

每个变量都有一个内存位置,每个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示在内存中的一个地址。

什么是指针?

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

实际数据类型,不管是整形,浮点型或者字符型等,对应的指针的值类型都是一样的,都是一个代表内存地址的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

使用指针:

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

int main(){
int var = 20;
int *ip;

ip = &var;
printf("var 的地址:%p\n", &var);
printf("指针变量的值:%p\n", ip);
printf("指针所指的值:%d\n", *ip);

return 0;
}

C中的Null指针

声明变量时,如果没有确定可以赋值的地址,通常给指针变量赋NULL值,此时的指针称为空指针

NULL指针是定义在标准库中值为零的常量,

十二. 函数指针

通常指针变量是指向例如整形,字符型等变量,而函数指针是指向函数的指针变量。

函数指针可用于调用函数,传递参数。

回调函数

函数指针作为某个函数的参数:

函数指针可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数,也就是说回调函数是由别人的函数执行时调用你实现的函数。

十三. 结构体

C数组允许存储相同类型的数据,结构是C语言中可由用户自定义的数据类型,它允许存储不同类型的数据项。

定义结构

使用struct语句定义一个包含多个成员的新数据类型:

1
2
3
4
5
struct tag{
member1;
member2;
...
}variable-list;
  • tag:结构体标签
  • member:标准的变量定义,比如int a。
  • variable-list:结构变量,定义在结构的末尾,可以指定一个或多个结构变量。

例子:

1
2
3
4
5
struct Books{
int id;
char title[50];
char author[50];
}book;

结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,从而实现一些更高级的数据结构,比如结构或者链表等。

结构体变量的初始化

结构体变量可以在定义时指定初始值:

1
2
3
4
5
struct Books{
int id;
char title[50];
char author[50];
}book = {123456, "C 语言", "hehe"};

访问结构成员

可以使用成员访问运算符(.)来访问结构成员。

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

struct Books{
int id;
char tilte[50];
char author[50];
};

int main(){
struct Books Book1;
strcpy(Book1.tilte, "C Programming");
strcpy(Book1.author, "hehe");
Book1.id = 123;

printf("Book1 title: %s\n", Book1.tilte);
printf("Book1 author: %s\n", Book1.author);
printf("Book1 id: %d\n", Book1.id);
return 0;
}

结果:

1
2
3
Book1 title: C Programming
Book1 author: hehe
Book1 id: 123

指向结构的指针

定义指向结构的指针方式跟定义指向其他类型变量的指针类似:

1
struct Books *struct_pointer;

给结构指针变量赋值:

1
strcut_pointer = &Book1;

为了使用指向该结构的指针访问结构成员,需使用->运算符:

1
struct_pointer->title;

上面例子使用结构的指针来访问成员变量来实现:

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

struct Books{
int id;
char tilte[50];
char author[50];
};

int main(){
struct Books Book1;
struct Books *struct_pointer;

struct_pointer = &Book1;

strcpy(Book1.tilte, "C Programming");
strcpy(Book1.author, "hehe");
Book1.id = 123;

printf("Book1 title: %s\n", struct_pointer->tilte);
printf("Book1 author: %s\n", struct_pointer->author);
printf("Book1 id: %d\n", struct_pointer->id);
return 0;
}

十四. 共用体

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

定义共用体

定义共用体,需使用union语句,方式跟结构体相似。

1
2
3
4
5
union Data{
int i;
float f;
char str[10];
}data;

共用体占用的内存为成员变量中最大的那个,例如上面例子中,Data占用10个字节。

访问共用体成员

访问共用体成员,可以使用成员访问符(.)。共用体变量的定义可以使用union,例如定义一个共用体类型的变量:

1
unit Data data;

注意:同一时间只能使用一个变量。

十五. typedef

C语言中typedef关键字,可以用来为已有类型或者自定义的数据类型取一个新的名字。例如

1
typedef unsigned char BYTE;

这样重命名后,以后就可以用BYTE来定义unsigned char了,这好比给自己起了一个昵称之后,以后朋友可以用你原来的名字叫你,也可用昵称来叫你了。

typedef 与#define的比较

  • typedef仅限为类型定义符号名称,#define不仅为类型定义别名,也能为数值定义别名,例如我们能将数字1定义为ONE
  • typedef是由编译器执行解释的,#define语句是预编译器进行处理的