C语言基础知识

1 C 语言简介

C 语言是一种通用的高级语言,是用来与机器交流的一种语言,可移植性和执行效率都较高。

2 C 程序结构

Hello World 程序为例,C程序结构主要包含以下部分:

  • 预处理指令
  • 函数
  • 变量
  • 语句/表达式
  • 注释
#include 	// 系统路径下去找
#include "xxx.h"	// 当前路径下去找
int main()			// main实际上是一个地址0x0013161 别名 标识符
{
   /* Hello World 注释 */
   printf("Hello, World! \n");
   if (a)
   {
       return -1;
   }
   return 0;		
}
  1. #include 是预处理指令,告诉编译器在编译前要包含 stdio.h 文件。
  2. int main() 是主函数,也是程序的入口,程序从这里开始执行。
  3. /* Hello World 注释 */会被编译器忽略,因为注释是写给人看的。
  4. printf("Hello, World! \n")是一个函数,可以在屏幕上打印Hello World
  5. return 0表示程序运行结束并返回0, 表示程序执行成功(成功只有返回0这一种情况,而错误则有很多种情况)。

3 C 基本语法

3.1 分号

分号是语句结束符,相当于语文中的句号,表示一条语句结束,这样可以区分语句。

3.2 注释

// 这是单行注释
/*
   这是多行注释
*/

注释是写给人看的,编译器看到 // 或者 /**/ 这样的,会自动忽略。

3.3 标识符

标识符就是用来标识变量、函数的,相当于为变量或者函数起一个名字。

命名规则是:以字母A-Z或a-z或下划线_开始,后面跟字母或下划线_或数字(0-9)。(命名不能跟关键字冲突!)

注意,命名中不能出现标点字符,比如@$%,而且C语言命名是区分大小写的!(大小写敏感)

常用的命名法则——驼峰命名法,采取大小写字母混用的命名方式。// 自说明变量

  • 小驼峰命名法:一般用于变量,比如int myStudentsCount;。// count 计数
  • 大驼峰命名法:一般用于类名(C++/Java),比如public class DataBaseUser;

3.4 关键字

关键字是C语言保留字(表示已被占用了),不能作为常量名、变量名或其他标识符名称。

关键字 说明
auto 声明自动变量(不常用)
goto 无条件跳转语句(不常用)
volatile 说明变量在程序执行中可被隐含地改变(不常用)
register 声明寄存器变量(不常用)
union 声明共用体类型(不常用)
enum 声明枚举类型
signed 声明有符号类型变量或函数
unsigned 声明无符号类型变量或函数
break 跳出当前循环
case 开关(switch)语句分支
char 声明字符型变量或函数返回值类型
const 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变
continue 结束当前循环,开始下一轮循环
default 开关语句中的"其它"分支
do 循环语句的循环体
double 声明双精度浮点型变量或函数返回值类型
else 条件语句否定分支(与 if 连用)
extern 声明变量或函数是在其它文件或本文件的其他位置定义
float 声明浮点型变量或函数返回值类型
for 一种循环语句
if 条件语句
int 声明整型变量或函数
long 声明长整型变量或函数返回值类型
return 子程序返回语句(可以带参数,也可不带参数)
short 声明短整型变量或函数
sizeof 计算数据类型或变量长度(即所占字节数)
static 声明静态变量
struct 声明结构体类型
switch 用于开关语句
typedef 用以给数据类型取别名 #define N 10
void 声明函数无返回值或无参数,声明无类型指针
while 循环语句的循环条件

3.5 空格

空格是编译器用来区分各个元素的,比如int a = 0;inta 之间就至少有一个空格,用于区分。

当然,为了代码的可读性,通常我们会适当的多加些空格,这样看起来不会很累。

比如int a = 0; 就比 int a=0; 看起来舒服点,字符间不会显得很密集。

4 C 数据类型

sizeof 可以得到对象或类型的存储大小。

int a[5];// sizeof(a) = 20 sizeof(a[0]) = 4

4.1 整数类型

1GB = 1024MB

1MB = 1024KB

1KB = 1024Byte

1Byte = 8bit

1111 1111 = 255

类型 存储大小 值范围
char 1 字节 -128 到 127
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

关于存储,在计算机中是以二进制形式存储的,也就是说计算机只认识0 或者 1

以char为例,1字节(byte)=8位(bit),存储大小对应的是2的8次方,即256个位置,刚好存放256个数。

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 位有效位

4.3 void 类型

类型 描述
函数返回为空 C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
函数参数为空 C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
指针指向 void 类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

5 C 变量

5.1 变量定义

变量定义:变量类型 变量名1, 变量名2, 变量名3;

int i, j, k;

注意,命名不能取保留的关键字,必须以下划线或大小写字母开头!

5.2 变量声明

变量声明:一般使用extern关键字来声明,声明表示该变量已经定义,告诉别人该变量已经定义。

// main.c
int add(int a, int b);// 声明
int a;			// 全局
int main(void)
{
    add(1, 2);
    return 0;
}
// 定义
int add(int a, int b)
{
	return (a + b);
}
// 说明i已经在其他C文件被定义了,在这里声明是为了使用它
extern int i;

一般定义了,也就声明了,同一个变量,只能定义一次,但可以在外部文件被声明多次。

// a.c
extern int a;// 外部声明
...

5.3 左值与右值

  • 左值是指向存储空间的表达式,比如变量a,b。
  • 右值是具体的数值,比如数值10, 20。
// 有效语句
int a = 10;
int b = a;

// 无效语句
10 = 20;
10 = a;

6 C 常量

常量,即固定不变的值,定义后不能修改。

6.1 整数常量

整数常量一般为十进制、八进制或十六进制。

前缀用来指定进制,0x或0X表示十六进制,0表示八进制,无前缀则默认为十进制。

后缀用来指定无符号或长整数,U表示无符号整数(unsigned),L表示长整数(long),后缀大小写随意,但一般为大写。

85         /* 十进制 */
0213       /* 八进制 */
0x4b       /* 十六进制 */
30         /* 整数 */
30U        /* 无符号整数 */
30L        /* 长整数 */
30UL       /* 无符号长整数 */

6.2 浮点常量

浮点常量由整数部分、小数点、小数部分和指数部分组成,可以用小数形式或指数形式来表示浮点常量。

3.14159       /* 合法的 */
314159E-5L    /* 合法的 */
510E          /* 非法的:不完整的指数 */
210f          /* 非法的:没有小数或指数 */
.e55          /* 非法的:缺少整数或分数 */

E代表10为底数,-5为指数,后缀为L代表 long double,后缀为F代表 float

6.3 字符常量

字符常量可以为单个的普通字符(‘x’)、一个转义序列(‘\t’),或一个通用字符(‘\u02C0’)。

printf("123\r\n456");
123
456
printf("123\r456");
123
   456
	
转义序列 含义
\ \ 字符
’ 字符
" " 字符
? ? 字符
\a 警报铃声
\b 退格键
\f 换页符
\n 换行符
\r 回车
\t 水平制表符
\v 垂直制表符
\ooo 一到三位的八进制数
\xhh . . . 一个或多个数字的十六进制数

6.4 字符串常量

字符串常量是由""括起来的,一个字符串可以包含普通的字符、转义序列和通用的字符。

也可以使用空格做分隔符,把很长的字符串常量进行分行。

// 下面三种定义都是一样的效果
"hello,world"

"hello,\
world"
    
"hello," "w" "orld"

6.5 定义常量

6.5.1 #define 预处理器

#define PI		3.14
#define WIDTH 	10
#define LENGTH	20

6.5.2 const 关键字

const int i = 10;// const修饰,表示不能被改
str = "123";// 重要
int func(const char *str)
{
    // 只给看看 123
    str = 456
    str -> 123
    ...
}

7 C 运算符

7.1 算术运算符

以A=10,B=20为例:

printf("A++ = %d\n", A++);// 10	
printf("A = %d\n", A);	// 11
运算符 描述 实例
+ 把两个操作数相加 A + B 将得到 30
- 从第一个操作数中减去第二个操作数 A - B 将得到 -10
* 把两个操作数相乘 A * B 将得到 200
/ 分子除以分母 B / A 将得到 2
% 取模运算符,整除后的余数 B % A 将得到 0
++ 自增运算符,整数值增加 1 A++ 将得到 11
自减运算符,整数值减少 1 A-- 将得到 9

注意,如果打印A++,那么打印值仍为10,A++表示使用后再加1,而如果是打印++A,那么打印值为11,表示先加1,然后再使用。

7.2 关系运算符

运算符 描述 实例
== 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 为假。
!= 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。
> 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 为假。
< 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。
>= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 为假。
<= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。

7.3 逻辑运算符

以A=0,B=1为例:

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。
|| 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 (A || B) 为真。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 !(A && B) 为真。

7.4 位运算符

&|^(与、或、异或)的真值表如下:

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 0 1 1

7.5 赋值运算符

运算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C *= A 相当于 C = C * A
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|= 按位或且赋值运算符 C |= 2 等同于 C = C | 2
int c = 4; // 4 --- 0000 0100
c = c << 2;// 	--- 0001 0000
c = c & 2;// 0000 0100
		  // 0000 0010
		  // 0000 0000

7.6 杂项运算符

运算符 描述 实例
& 返回变量的地址。 &a; 将给出变量的实际地址。
* 指向一个变量。 *a; 将指向一个变量。
? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y

7.7 运算符优先级

类别 运算符 结合性
后缀 () [] -> . ++ - - 从左到右
一元 + - ! ~ ++ - - (type)* & sizeof 从右到左
乘除 * / % 从左到右
加减 + - 从左到右
移位 << >> 从左到右
关系 < <= > >= 从左到右
相等 == != 从左到右
位与 AND & 从左到右
位异或 XOR ^ 从左到右
位或 OR | 从左到右
逻辑与 AND && 从左到右
逻辑或 OR || 从左到右
条件 ?: 从右到左
赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左
逗号 , 从左到右

8 C 判断

C 语言中,判断决定了程序会执行哪一部分,条件为真时应该执行什么,为假时又应该执行什么。

语句 描述
if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。
if…else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。
嵌套 if 语句 您可以在一个 ifelse if 语句内使用另一个 ifelse if 语句。
switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。
嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。
switch (s1)
{
    case 1:
        switch (s2)
        {
            case 4:break;
            default:break;
        }
        break;
    case 2:break;
    case 3:break;
    default:break;
}

9 C 循环

9.1 循环类型

循环类型 描述
while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。
for 循环 多次执行一个语句序列,简化管理循环变量的代码。
do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。

9.2 循环控制语句

控制语句 描述
break 语句 终止循环switch 语句,程序流将继续执行紧接着循环或 switch 的下一条语句。
continue 语句 告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。
goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。

10 C 函数

10.1 函数定义

// 函数返回类型 + 函数名 + 函数参数
int function(int para)
{
    /* 函数主体 */
    ...
    rertun 0;
}

10.2 函数声明

//  函数返回类型 + 函数名 + 函数参数 + ;
int function(int para);

10.3 函数调用

// 函数名 + 函数实参
function(10);

10.4 函数参数

调用类型 描述
传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数不会影响实际参数。
传址调用 通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。
int x = 3, y = 4;
// 传值	交换失败!
swap1(x, y);
int swap1(int a, int b)
{
    int t;
    t = a;
    a = b;
    b = t;
}
// 传址	交换成功!
swap2(&x, &y);
int swap2(int *p1, int *p2)
{
    int t;
    t = *p1;
    *p1 = *p2;
    *p2 = t;
}

11 C 数组

一些数据元素的组合,按照顺序存储在内存中,通过下标索引来访问数据元素。(集合)

int a;
int a[5] = {1,2,3,4,5};
a[0] -- a[4]

11.1 声明并初始化数组

int a[5] = {1, 2, 3, 4, 5};

11.2 访问数组元素

for (int i = 0;i < 5;i++)
{
    printf("a[%d] = %d\n", i, a[i]);
}
/*
	a[0] = 1
	a[1] = 2
	a[2] = 3
	a[3] = 4
	a[4] = 5
*/

12 C 指针

12.1 指针定义与使用

int *p = NULL;			// 定义一个指针变量 p,可存放地址
int a = 3;				// 定义一个整型变量 a,存放的值为3
p = &a;					// 将 a 的地址赋给 p
printf("*p = %d\n", *p);// *p 为解引用,找到 p 存放的 a 地址然后打印里面的值
/*
实例讲解:
int *p 		给一个抽屉取名为p,我们叫它抽屉p,它存的的是int *类型,也就是地址
int a = 3 	给一个抽屉取名为a,我们叫它抽屉a,它存的是int类型,也就是整数类型
p = &a 		往抽屉p里放了一把抽屉a的钥匙
*p 			打开抽屉p取东西,取出来的是抽屉a的钥匙,所以打印*p,实际上就是打印抽屉a里存的值
*/

12.2 NULL 指针

int *p = NULL;	// 没有NULL,p就是一个野指针
p = (int *)malloc(sizeof(int));
if (NULL == p)
{
    return -1;
}
printf("p = 0x%p\n", p);	// p = 0x0

赋为 NULL 值的指针被称为空指针,它指向地址为0的内存,而在操作系统中,地址为0的内存是保留的,是不允许被访问的。

12.3 二重指针

二重指针其实就是嵌套了一层。

int *p1 = NULL;				// 给一个抽屉取名为p1,里面放的是int *类型,是地址
int **p2 = NULL;			// 给一个抽屉取名为p2,里面放的是int **类型,也是地址
int a = 3;					// 给一个抽屉取名为a,里面放的是int类型,也就是整数类型
p1 = &a;					// 往抽屉p1里放抽屉a的钥匙
p2 = &p1;					// 往抽屉p2里放抽屉p1的钥匙
printf("*p2 = %p\n", *p2);	// 打开抽屉p2取东西,取出来的是抽屉p1的钥匙,是地址
printf("**p2 = %d\n", **p2);// 打开抽屉p2,拿到抽屉p1的钥匙后,再去打开抽屉p1,取出来的就是a,是整数

12.4 函数指针

函数指针,从字面意思上来看,就是存储函数地址的指针变量。

int add(int x, int y)
{
    return (x + y);
}
int main(void)
{
    int (*pFunc)(int, int) = add;
    int sum = pFunc(1, 2);
    printf("sum = %d\n", sum);		// sum = 3
    return 0;
}

12.5 回调函数

回调函数其实是函数指针的一个应用,当你调用函数A时,需要把函数B当作A的参数传进去,那么此时,函数B就叫回调函数。

#include   
#include 
 
// 往数组里填充随机值
void populate_array(int *array, int arraySize, int (*getNextValue)(void))
{
    for (int i = 0; i < arraySize; i++)
        array[i] = getNextValue();// 假设我有个函数,已经实现了,而且就存在函数指针里
}
 
// 获取随机值	回调函数
// 回调函数可以自由修改,比如设置随机值范围
// 回过头去调用的函数
// rand()%100 + 1	[1, 100]
int getNextRandomValue(void)
{
    return rand();// 返回一个随机值,系统函数
}
 
int main(void)
{
    int myarray[10];
    // 往数组里填充 10 个随即值
    // &a[0]	--- 数组首元素地址
    // myarray  --- 数组地址
    populate_array(myarray, 10, getNextRandomValue);
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}

13 C 字符串

char str1[6] = {'h', 'e', 'l', 'l', 'o', '\0'};		// 输出 hello
char str2[] = "hello";								// 输出 hello
函数 目的
strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
strlen(s1); 返回字符串 s1 的长度。
strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回大于 0。
strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。
strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。

14 C 可变参数

double getAverage(int num, ...)
{
    va_list args;					// 定义一个va_list变量 args
    double sum = 0.0;
    int i;
    va_start(args, num);			// 初始化参数列表 num代表传入参数总数
    for (i = 0;i < num;i++)
    {
        sum += va_arg(args, int);	// va_arg可以访问参数列表里的每一个参数
    }
    va_end(args);					// 清理分配给va_list变量的内存
    return sum/num;					
}

15 C 命令行参数

// ./test.exe 10 20
// argc = 3
// argv[0] = ./test.exe
// argv[1] = 10
// argv[2] = 20
int main( int argc, char *argv[] )  
{
   for (int i = 0;i < argc;i++)
   {
       printf("argv[%d] = %s\n", i, argv[i]);
   }
    return 0;
}

16 C 内存管理

函数 描述
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。
char *p = (char *)malloc(100*sizeof(char));	// 动态分配内存
if (NULL == p)								// 判断分配结果
{
    printf("malloc error!\n");
    return -1;
}
memset(p, 0, 100);							// 清理分配的内存
...											// 使用分配的内存,如复制字符串之类的
free(p);									// 释放内存

17 C 作用域规则

17.1 局部变量

// 声明于函数或块内部的变量
int main(void)
{
    int a;		// 局部变量声明
    a = 10;		// 局部变量初始化
    return 0;
}

17.2 全局变量

int g;			// 定义与函数外部(一般在程序顶部),任意函数都能访问 
int main(void)
{
    int a = 10, b = 20;
    g = a + b;
    printf("g = %d\n", g);
    return 0;
}

注意:在函数内,如果全局变量名和局部变量名相同,会使用局部变量值(就近原则)。

18 C 枚举(enum)

// 枚举也是一种数据类型,字面意思就是把一个个可能用到的数据都列出来
// 比如定义一星期
// 1. 采用宏定义方式
#define MON	1
#define TUE	2
#define WED	3
#define THU 4
#define FRI 5
#define SAT 6
#define SUN	7
// 2. 采用枚举方式(更简洁)
enum DAY
{
    MON = 1,	// 默认从0开始的,这里让 MON = 1
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
};
enum DAY day;
day = WED;
printf("day = %d\n", day);	// day = 3

19 C 结构体

// 结构体变量初始化
struct EmployeeInfo
{
	int id;					// 职工编号
	char name[20];			// 职工姓名
}employee = {123, "Jack"};
// 访问结构体成员	使用 . 来访问
printf("id:%d, name:%s\n", employee.id, employee.name);

// 指向结构的指针
void printEmployee(struct EmployeeInfo *employee)
{
    printf("id: %d, name:%s\n", employee->id, employee->name);
}
int main(void)
{
    struct EmployeeInfo em1;
    em1.id = 123;
    strcpy(em1.name, "Jack");
    printEmployee(&em1);		// 通过传地址来输出信息
    return 0;
}

20 C 递归

递归就是函数自己调用自己。

// 斐波那契数列 1 1 2 3 5 8 13 21 34
int fibonaci(int i)
{
    if (0 == i)
    {
        return 0;
    }
    if (1 == i)
    {
        return 1;
    }
    return fibonaci(i-1) + fibonaci(i-2);
}    
for (int i = 1;i < 10;i++)
{
    printf("%d\n", fibonaci(i));
}

21 C 排序算法

21.1 冒泡排序

void swap(int *p1, int *p2)
{
	int temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
void bubble_sort(int arr[], int len) 
{
    int i, j;
    for (i = 0; i < len - 1; i++)			// 比较趟数,最多len-1趟
    {
        for (j = 0; j < len - 1 - i; j++)	// 每趟比较次数
        {
            if (arr[j] > arr[j + 1]) 		// 需要交换
            {
                swap(&arr[j], &arr[j + 1]);
            }
        }
    }
}

21.2 选择排序

void swap(int *p1, int *p2)
{
	int temp;
	temp = *p1;
	*p1 = *p2;
	*p2 = temp;
}
void select_sort(int arr[], int len) 
{
    int i, j, min;
 
    for (i = 0 ; i < len - 1 ; i++) 
    {
        min = i;                  		// 记录最小值,第一个元素默认最小
        for (j = i + 1; j < len; j++)	// 访问未排序的元素
        {
            if (a[j] < a[min])    		// 找到目前最小值
            {
                min = j;    			// 记录最小值
            }
        }
        if(min != i)					// 需要交换
        {
           swap(&a[min], &a[i]);
        }
    }
}

你可能感兴趣的:(c语言)