数组是 C 语言中一组相同数据类型元素的有序集合,所有元素在内存中连续存储,通过 “数组名 + 下标” 可以快速访问任意元素。
通俗理解:数组就像一排编号的储物柜(下标从 0 开始),每个柜子只能放同一种类型的物品(如全是整数 / 字符),通过编号(下标)能直接找到对应的柜子,且柜子在内存中是紧挨着的。
一、数组概述
C 语言支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量的集合。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
数组是用于存储多个相同类型数据的集合。通常用Array表示,也称之为线性表。
线性表是一种数据结构,第一个节点没有前驱,最后一节节点没有后继,中间节点只有一个前驱节点和一个后继节点。
1.1 数组的特性
(1)数组是相同数据类型的元素的集合;数组中保存的数据叫做元素。
(2) 数组中各元素的存储是有先后顺序的,它们在内存中按照这个顺序连续存放到一起。内存地址(连续存储)。
(3)数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。例如a[0]表示名字为a的数组中的第一个元素。a[1]表示名字为a的数组中的第二个元素,以此类推。
(4)数组元素的访问是通过[ ]运算符;
1.2 数组定义
要想定义数组,需要考虑2点:
- 第一点,数组中保存什么类型的数据(char,int,short,long,float,double);
- 第二点,数组中保存数据的个数是多少;因为数组一旦被定义,其空间大小就确定了;
比如定义一个数组保存5个int类型的数据,那么该数组在内存占用的空间20Byte。在给数组起名字时,需要考虑标识符的命名规则;
定义方式1:元素类型 数组名[元素个数];
int Array[5];
定义方式2:元素类型 数组名[元素个数]={0,1,2,3…..};
int Array[5]={1,2,3,4,5};
注意:数组的初始化只能在定义的时候初始化;
1.3 数组初始化
第一种初始化形式:完全初始化
//定义一个整型数组保存12,13,14,15,16,17这些数据;
int Array[6]={12,13,14,15,16,17};
第二种初始化形式:不完全初始化
//定义一个整型数组保存10个元素前5个元素为:12,13,14,15,16,后5个元素未确定
int Array[10]={12,13,14,15,16};
//注意:后5个元素没有赋值默认初始化为0
第三种初始化形式:全部初始化为0
int Array[5]={0};
//等价于将第一个元素赋值为0,后四个元素没有赋值的情况下默认初始化为0
第四种初始化形式:在定义数组时不表明数组元素个数,通过数组初始化的元素个数表明;
//定义一个整型数组保存12,13,14,15,16,17这些数据;
int Array[]={12,13,14,15,16,17};
//数组的元素个数是由初始化的元素个数决定的
注意:数组错误的初始化形式
int Array[5];
Array[5]={1,2,3,4,5}; //错误
Array={1,2,3,4,5}; //错误
int Array[5]={1,2,3,4,5,6}; //错误,数组就5个元素,但是初始化了6个
1.4 数组名
<1> 数组名是常量,不能作为赋值运算符的左值;
int Array[5]={1,2,3,4,5};
int Buff[5];
Buff=Array; //错误,不管是Array还是Buff都是数组,数组名是常量不能作为赋值号左值
<2> 数组名表示的是数组首元素的地址;
<3> 数组名表示的是数组;

数据类型:表示变量/数组存储什么数据,占用多大空间;
1.5 数组元素索引
当通过数组进行元素访问时,数组名是作为数组首元素地址的;
(1)下标法:使用[]运算符进行访问;
//数组元素的下标从0开始
int Array[5]={0x11,0x22,0x33,0x44,0x55};
元素1:Array[0]=0x11; //将每一个数组元素想象是一个int类型的变量
元素2:Array[1]=0x22;
元素3:Array[2]=0x33;
元素4:Array[3]=0x44;
元素5:Array[4]=0x55; -> Array[3]=178;
[]运算符
<1> 用于定义数组,则表示数组中的元素个数
<2> 用于访问数组元素:数组名[下标],运算原理->数组名先进行地址偏移再解空间
下标法:Array[0] == *(Array+0) ->地址偏移法
Arrayp[1] == *(Array+1)
(2)地址偏移法,[]运算符的原理就是地址偏移;
示例:定义一个数组Array,该数组保存5个int类型的值,初始值全部为0;通过scanf()向数组中输入5个元素的值,通过printf()输出数组中5个元素的值。
#include <stdio.h>
int main(void) {
int Array[5] = {0};
//Array[0]变量
//通过scanf()对数组进行输入
for (int i = 0; i < 5; i++) { //i=0,1,2,3,4
printf("Input Array[%d]:>", i);
scanf("%d", &Array[i]);
}
for (int i = 0; i < 5; i++) {
printf("Array[%d]=%d\\n", i, Array[i]);
}
}
Input Array[0]:>10
Input Array[1]:>20
Input Array[2]:>30
Input Array[3]:>40
Input Array[4]:>50
Array[0]=10
Array[1]=20
Array[2]=30
Array[3]=40
Array[4]=50
1.6 数组的分类
1.按照数组的维度来分
一维数组:int Array[3];
二维数组:int Array[3][4];
多维数组:int Array[3][4][5];
2.按照数组的元素类型来分
字符型数组:char Array[10];
整型数组:short Array[10]; int Array[10]; long Array[10];
浮点型数组:float Array[10]; double Array[10];
指针数组:int *Array[10];
二、一维数组
一维数组本质是数组,该数组的元素类型为基本数据类型(数组的元素类型不是数组类型);
2.1 数组赋值
int Array[4];
Array[0]=1;
Array[1]=2;
Array[2]=3;
Array[3]=4;
2.2 数组元素个数
int Array[]={1,2,3,4,5,6,7,10,89,78,33,54,22,56,64};
//数组的组成:元素类型 数组名 元素个数
//数组大小:元素类型大小和元素个数决定 数组大小=元素类型大小x元素个数
//元素个数=数组大小÷元素类型大小 -> sizeof(Array)/sizeof(int)
数组名有2种含义
<1> 表示整个数组:数据类型=元素类型+元素个数
只有2种情况下数组名才表示数组首元素地址
(1) 对数组做sizeof运算:sizeof(Array)
(2) 对数组做取地址运算:&Array
<2> 表示数组首元素的首地址:数据类型=元素类型*
2.3 数组做函数参数
C语言规定,数组在传参数,只是将数组的首元素地址进行传递;
第一种:在函数参数中以传参数组的数组类型再定义一个数组;在函数参数定义的数组只是形式上是数组,本质上是指针变量。
#include <stdio.h>
//编写一个函数用于输出数组的所有数据
void PrintArray(int p[], int len) {
for (int i = 0; i < len; i++)
printf("%d ", p[i]);
printf("\\n");
}
int main(void) {
//sizeof(Array) &Array
int Array[] = {19, 78, 45, 23, 77, 10, 98, 83, 22};
int Num = sizeof(Array) / sizeof(int);
//首元素:int类型
//首元素地址:int *类型
PrintArray(Array, Num);
}
第二种:因为数组名作为参数传递时,此时数组名作为数组首元素地址进行传参的,所以函数参数类型只要等价于数组名作为首元素地址类型即可;
#include <stdio.h>
//编写一个函数用于输出数组的所有数据
void PrintArray(int *p, int len) {
for (int i = 0; i < len; i++)
printf("%d ", p[i]);
printf("\\n");
}
int main(void) {
//sizeof(Array) &Array
int Array[] = {19, 78, 45, 23, 77, 10, 98, 83, 22};
int Num = sizeof(Array) / sizeof(int);
//首元素:int类型
//首元素地址:int *类型
PrintArray(Array, Num);
}
三、排序算法
3.1 冒泡排序
1、概述
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
2、算法描述
-
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
-
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
-
针对所有的元素重复以上的步骤,除了最后一个。
-
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
-
规律:元素个数如果为n个,需要排n-1轮,第一轮排n-1次,第二轮排n-2…….。
3、冒泡排序的核心原理
冒泡排序(Bubble Sort)是一种交换排序,核心逻辑:
重复遍历待排序数组,每次比较相邻的两个元素;
如果前一个元素大于后一个(升序排序),则交换两者位置;
每一轮遍历会将当前未排序部分的最大元素 “冒泡” 到末尾;
重复 n-1 轮(n 为数组长度),直到整个数组有序。
通俗理解:就像水里的气泡从下往上逐步上浮,每一轮遍历都会把当前最大的数 “推” 到数组的最后位置。
4、C 语言实现冒泡排序
以下代码不仅实现升序排序,还会打印每一轮的比较、交换过程,方便对照示意图理解:
#include <stdio.h>
// 冒泡排序函数(升序)
void bubbleSort(int arr[], int len) {
// 外层循环:控制排序轮数(n-1轮)
for (int i = 0; i < len – 1; i++) {
// 标记:本轮是否发生交换(优化:若无交换则数组已有序,提前退出)
int swapped = 0;
// 内层循环:每轮比较到“未排序部分末尾”(len-1-i)
for (int j = 0; j < len – 1 – i; j++) {
printf("第%d轮,比较arr[%d]=%d 和 arr[%d]=%d → ",
i+1, j, arr[j], j+1, arr[j+1]);
// 相邻元素比较,前>后则交换
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
swapped = 1;
printf("交换 → 当前数组:");
} else {
printf("不交换 → 当前数组:");
}
// 打印本轮当前数组状态
for (int k = 0; k < len; k++) {
printf("%d ", arr[k]);
}
printf("\\n");
}
// 优化:本轮无交换,说明数组已有序,直接退出
if (!swapped) {
printf("第%d轮无交换,数组已有序,提前结束排序\\n", i+1);
break;
}
printf("第%d轮结束,最大未排序元素已沉底\\n", i+1);
printf("—————————————-\\n");
}
}
int main() {
// 测试数组
int arr[] = {5, 2, 9, 1, 5, 6};
int len = sizeof(arr) / sizeof(arr[0]);
printf("初始数组:");
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\\n========================================\\n");
// 调用冒泡排序
bubbleSort(arr, len);
printf("========================================\\n");
printf("最终有序数组:");
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
return 0;
}
5、排序过程示意图(对应测试数组 {5,2,9,1,5,6})
为了更直观,用表格展示每一轮的核心变化(红色为交换的元素,蓝色为已排序的末尾元素):
| 初始 | – | [5, 2, 9, 1, 5, 6] | 原始数组 |
| 第 1 轮 | 1. 5>2 → 交换 → [2,5,9,1,5,6]2. 5<9 → 不交换 → [2,5,9,1,5,6]3. 9>1 → 交换 → [2,5,1,9,5,6]4. 9>5 → 交换 → [2,5,1,5,9,6]5. 9>6 → 交换 → [2,5,1,5,6,9] | [2,5,1,5,6, <font color="blue">9</font>] | 最大元素 9 “冒泡” 到末尾,已排序 |
| 第 2 轮 | 1. 2<5 → 不交换 → [2,5,1,5,6,9]2. 5>1 → 交换 → [2,1,5,5,6,9]3. 5=5 → 不交换 → [2,1,5,5,6,9]4. 5<6 → 不交换 → [2,1,5,5,6,9] | [2,1,5,5, <font color="blue">6, 9</font>] | 次大元素 6 “冒泡” 到倒数第二位置 |
| 第 3 轮 | 1. 2>1 → 交换 → [1,2,5,5,6,9]2. 2<5 → 不交换 → [1,2,5,5,6,9]3. 5=5 → 不交换 → [1,2,5,5,6,9] | [1,2,5, <font color="blue">5, 6, 9</font>] | 元素 5 已在正确位置 |
| 第 4 轮 | 1. 1<2 → 不交换2. 2<5 → 不交换 | 无交换,提前结束 | 数组已完全有序 |

6、代码关键解释
7、冒泡排序的特性
| 时间复杂度 | 最坏 / 平均:O(n2)(逆序数组);最好:O(n)(已排序数组,优化后) |
| 空间复杂度 | O(1)(原地排序,仅用临时变量) |
| 稳定性 | 稳定(相等元素不会交换位置,如示例中的两个 5 顺序不变) |
| 适用场景 | 小规模数据排序、基本有序的数据排序(优化后效率高) |
8、总结
冒泡排序的核心是相邻元素比较交换,每轮将当前最大元素 “冒泡” 到末尾;
代码实现需注意外层轮数(n-1)、内层比较边界(len-1-i),以及 “无交换提前退出” 的优化;
排序过程的核心规律:每轮结束后,至少有一个元素到达最终位置,未排序部分长度减 1;
冒泡排序是稳定的原地排序,适合小规模、基本有序的数据场景。
3.2 插入排序
1、概述
插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
2、算法描述
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
3、插入排序的核心原理
插入排序(Insertion Sort)类比日常 “整理扑克牌” 的逻辑:
将数组分为「已排序区间」(初始只有第一个元素)和「未排序区间」;
从「未排序区间」取出第一个元素,向前遍历「已排序区间」;
找到该元素在「已排序区间」的正确位置(比前一个大、比后一个小),将比它大的元素后移;
将该元素插入到正确位置;
重复上述步骤,直到「未排序区间」为空。
通俗理解:就像你手里抓着一堆乱序的扑克牌,从第二张开始,每次把当前牌插入到前面已经排好序的牌堆里,最终所有牌都有序。
4、C 语言实现插入排序
以下代码实现升序插入排序,同时打印每一步的 “取元素→比较→后移→插入” 过程,方便对照示意图理解:
#include <stdio.h>
// 插入排序函数(升序)
void insertionSort(int arr[], int len) {
// 外层循环:遍历未排序区间(从第2个元素开始,下标1)
for (int i = 1; i < len; i++) {
int current = arr[i]; // 取出未排序区间的第一个元素(待插入元素)
int j = i – 1; // 已排序区间的最后一个元素下标
printf("第%d轮:待插入元素 = %d(已排序区间:", i, current);
// 打印当前已排序区间
for (int k = 0; k < i; k++) {
printf("%d ", arr[k]);
}
printf(")\\n");
// 内层循环:向前遍历已排序区间,找到插入位置
// 条件:j>=0(不越界)且 已排序元素 > 待插入元素 → 后移
while (j >= 0 && arr[j] > current) {
printf(" 比较arr[%d]=%d > %d → 后移arr[%d]=%d到arr[%d]\\n",
j, arr[j], current, j, arr[j], j+1);
arr[j + 1] = arr[j]; // 元素后移
j–; // 继续向前比较
}
// 插入待排序元素到正确位置(j+1)
arr[j + 1] = current;
printf(" 插入%d到arr[%d] → 当前数组:", current, j+1);
// 打印本轮结束后的数组
for (int k = 0; k < len; k++) {
printf("%d ", arr[k]);
}
printf("\\n—————————————-\\n");
}
}
int main() {
// 测试数组
int arr[] = {5, 2, 9, 1, 5, 6};
int len = sizeof(arr) / sizeof(arr[0]);
printf("初始数组:");
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\\n========================================\\n");
// 调用插入排序
insertionSort(arr, len);
printf("========================================\\n");
printf("最终有序数组:");
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
return 0;
}
5、排序过程示意图(对应测试数组 {5,2,9,1,5,6})
用表格清晰展示每一轮的 “已排序区间”“待插入元素”“元素后移”“插入位置” 核心变化(绿色为已排序区间,红色为待插入元素,蓝色为后移元素):
| 初始 | [5] | – | – | – | [5, 2, 9, 1, 5, 6] |
| 第 1 轮 | [5] | <font color="red">2</font> | 5>2 → 5 后移到下标 1 | 下标 0 | [<font color="green">2,5</font>, 9, 1, 5, 6] |
| 第 2 轮 | [2,5] | <font color="red">9</font> | 5<9 → 无需后移 | 下标 2 | [<font color="green">2,5,9</font>, 1, 5, 6] |
| 第 3 轮 | [2,5,9] | <font color="red">1</font> | 9>1→后移、5>1→后移、2>1→后移 | 下标 0 | [<font color="green">1,2,5,9</font>, 5, 6] |
| 第 4 轮 | [1,2,5,9] | <font color="red">5</font> | 9>5→后移、5=5→停止 | 下标 3 | [<font color="green">1,2,5,5,9</font>, 6] |
| 第 5 轮 | [1,2,5,5,9] | <font color="red">6</font> | 9>6→后移、5<6→停止 | 下标 4 | [<font color="green">1,2,5,5,6,9</font>] |
6、代码关键解释
7、插入排序的特性(对比冒泡排序)
| 时间复杂度 | 最坏 / 平均:O(n2);最好:O(n)(已排序数组) | 最坏 / 平均:O(n2);最好:O(n)(优化后) |
| 空间复杂度 | O(1)(原地排序) | O(1)(原地排序) |
| 稳定性 | 稳定(相等元素不交换位置) | 稳定 |
| 交换 / 移动次数 | 最少0,最多n(n−1)/2 | 最少0,最多n(n−1)/2 |
| 实际效率 | 更高(少交换,多后移) | 更低(频繁交换) |
| 适用场景 | 小规模数据、基本有序数据、流式数据(边接收边排序) | 小规模数据、教学演示(逻辑简单) |
8、总结
插入排序的核心是分已排序 / 未排序区间,将未排序元素插入到已排序区间的正确位置;
代码实现关键:保存待插入元素、向前遍历后移大元素、找到位置后插入;
插入排序比冒泡排序更高效(减少交换操作),是实际开发中小规模数据排序的优选;
核心规律:每轮结束后,已排序区间长度 + 1,未排序区间长度 – 1,最终覆盖整个数组。
3.3 数组练习
1、下面程序是求出数组最大值,请填空;
void maxvalue(int *a,int n,int* pmax)
{
int i;
( *pmax=a[0]; ) //空1
for(i=1;i<n;i++)
{
if(*(a+i)>*pmax) //*(a+i)=a[i] *pmax=max -> a[i]>max
{
( *pmax=*(a+i); ) //空2
}
}
}
void main(void)
{
int a[8]={10,-1,8,30,90,-67,0,66};
int max;
( maxvalue(a,sizeof(a)/sizeof(int),&max); ) //空3
printf(“max=%d\\n”,max);
}
2、假设有10个数已经按照从小到大的顺序存放在数组中,要求向从键盘输入一个整数,插入这10个数中,使数组仍是从小到大的顺序排列。
#include <stdio.h>
#define SIZE 5 // 定义常量SIZE为5,表示原始数组长度
int main() {
int arr[SIZE + 1] = {1, 3, 5, 7, 9};
int num, i, position = SIZE;
printf("原始数组: ");
for (i = 0; i < SIZE; i++) printf("%d ", arr[i]);
printf("\\n请输入一个整数: ");
scanf("%d", &num);
for (i = SIZE; i > 0 && arr[i-1] > num; i–)
arr[i] = arr[i-1];
arr[i] = num; // 循环结束后,i即为插入位置,直接赋值
printf("插入后的数组: ");
for (i = 0; i <= SIZE; i++) printf("%d ", arr[i]);
return 0;
}
3、编写一个函数,将一个数组逆序输出,数组元素由用户输入。
#include <stdio.h>
#define Swap(a,b) {a=a^b;b=a^b;a=a^b;}
int main(void)
{
int Array[]={1,2,3,4,5,6,7,8,9,0};
int len=sizeof(Array)/sizeof(Array[0]);
for(int i=0;i<len/2;i++)
Swap(Array[i],Array[len-i-1]);
for(int i=0;i<len;i++)
printf("%d ",Array[i]);
printf("\\n");
}
4、输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main(void)
{
int n, i, max, min;
printf("请输入数组元素个数:");
scanf("%d", &n);
int Array[n];
printf("请输入数组元素:\\n");
for (i = 0; i < n; i++)
{
scanf("%d", &Array[i]);
}
max = 0;
min = 0;
for (i = 1; i < n; i++)
{
if (Array[i] > Array[max])
{
max = i;
}
if (Array[i] < Array[min])
{
min = i;
}
}
swap(&Array[0], &Array[max]);
if (min == 0)
{
min = max;
}
swap(&Array[n – 1], &Array[min]);
printf("交换后的数组为:\\n");
for (i = 0; i < n; i++)
{
printf("%d ", Array[i]);
}
printf("\\n");
}
5、有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数。
#include <stdio.h>
#define Swap(a,b) {a=a^b;b=a^b;a=a^b;}
int main(void)
{
int Array[]={1,2,3,4,5,6,7,8,9,0};
int len=sizeof(Array)/sizeof(Array[0]);
int move=0;
printf("Input move:>");
scanf("%d",&move);
move %= len;
int temp;
for(int i=0;i<len/2;i++)
Swap(Array[i],Array[len-i-1]);
for(int i=0;i<move/2;i++)
Swap(Array[i],Array[move-i-1]);
for(int i=0;i<(len-move)/2;i++)
Swap(Array[i+move],Array[len-i-1]);
for(int i=0;i<len;i++)
{
printf("%d ",Array[i]);
}
printf("\\n");
}
6、有1000个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
#include <stdio.h>
int main() {
// 1. 初始化数组:1000个元素,1表示在圈中,0表示退出
// 下标0对应1号,下标999对应1000号
int people[1000];
for (int i = 0; i < 1000; i++) {
people[i] = 1; // 初始所有人都在圈里
}
int count = 0; // 报数计数器(1-3循环)
int index = 0; // 当前遍历的数组索引(模拟围成圈)
int remain = 1000;// 圈中剩余人数
// 2. 核心循环:直到只剩1人
while (remain > 1) {
// 仅对“在圈中”的人报数
if (people[index] == 1) {
count++; // 报数+1
// 报到3时,淘汰当前人
if (count == 3) {
people[index] = 0; // 标记为退出
remain–; // 剩余人数-1
count = 0; // 重置报数计数器
}
}
// 索引循环(围成圈:到最后一个人后回到第一个)
index++;
if (index == 1000) {
index = 0;
}
}
// 3. 找到最后剩下的人(值为1的元素)
int result = 0;
for (int i = 0; i < 1000; i++) {
if (people[i] == 1) {
result = i + 1; // 下标+1 = 原始编号(下标0对应1号)
break;
}
}
// 输出结果
printf("1000人围成圈报数(1-3),最后留下的是原来第%d号\\n", result);
return 0;
}
7、两个有序数组合并后仍然是有序数组。 数组a:8 14 19 23 46 52 数组b:15 28 48 60 合并后:8 14 15 19 23 28 46 48 52 60
#include <stdio.h>
int main() {
// 1. 定义并初始化两个有序数组
int a[] = {8, 14, 19, 23, 46, 52};
int b[] = {15, 28, 48, 60};
// 计算两个数组的长度
int len_a = sizeof(a) / sizeof(a[0]); // 6
int len_b = sizeof(b) / sizeof(b[0]); // 4
// 合并数组长度 = 数组a长度 + 数组b长度
int merge_len = len_a + len_b;
int merge[merge_len]; // 存储合并后的有序数组
// 2. 初始化双指针
int i = 0; // 指向数组a的当前元素
int j = 0; // 指向数组b的当前元素
int k = 0; // 指向合并数组的当前位置
// 3. 核心循环:比较两个数组的当前元素,取较小值放入合并数组
while (i < len_a && j < len_b) {
if (a[i] < b[j]) {
merge[k] = a[i]; // 取a的当前元素
i++; // 移动a的指针
} else {
merge[k] = b[j]; // 取b的当前元素
j++; // 移动b的指针
}
k++; // 移动合并数组指针
}
// 4. 处理数组a的剩余元素(如果有)
while (i < len_a) {
merge[k] = a[i];
i++;
k++;
}
// 5. 处理数组b的剩余元素(如果有)
while (j < len_b) {
merge[k] = b[j];
j++;
k++;
}
// 6. 输出结果
printf("数组a:");
for (int m = 0; m < len_a; m++) {
printf("%d ", a[m]);
}
printf("\\n数组b:");
for (int m = 0; m < len_b; m++) {
printf("%d ", b[m]);
}
printf("\\n合并后:");
for (int m = 0; m < merge_len; m++) {
printf("%d ", merge[m]);
}
return 0;
}
三、二维数组
二维数组是一种数据结构,它由行和列组成,可以看作是一个表格或矩阵。在编程中,二维数组常用于存储和处理具有两个维度的数据,比如图像、棋盘状态或者任何需要行列表示的信息。
3.1 二维数组定义
元素类型 数组名[行数][列数];
int Array[3][4];
//起始C语言中没有二维数组这种数据类型,本质还是一维数组;地址连续
//二维数组:因为数组Array的元素还是个数组
//Array是一个数组,该数组有3个元素
Array:数组名
<1> 作为数组来说,数据类型:int [3][4]
<2> 作为首元素的首地址,数据类型:int (*)[4]
元素类型:int [4] -> 一维数组
3.2 二维数组初始化
第一种初始化:将二维数组看成是二维数组;
int Array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
Array[0]-> int [4] ->{1,2,3,4}
Array[1]-> int [4] ->{5,6,7,8}
Array[2]-> int [4] ->{9,10,11,12}
第二种初始化:将二维数组看成是一维数组;
int Array[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
3.3 二维数组元素索引
1、下标法 2、地址偏移法
int Array[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
Array[0]-> {1,2,3,4}; -> Array[0][0]=1,Array[0][1]=2,Array[0][2]=3,Array[0][3]=4
Array[1]-> {5,6,7,8}; -> Array[1][0]=5,Array[0][1]=6,Array[0][2]=7,Array[0][3]=8
Array[2]-> {9,10,11,12}; -> Array[2][0]=9,Array[2][1]=10,Array[2][2]=11,Array[2][3]=12
Array[3][4]
<1> Array[3] == *(Array+3)
<2> Array[3][4] == *(Array[3]+4)==*(*(Array+3)+4)

示例1:二维数组的类型说明
<1> Array数组名类型
(1)数组名表示整个数组:int [4][5]
(2)表示首元素地址:int (*)[5]
<2> 元素类型,有4个元素分别是Array[0]、Array[1]、Array[2]、Array[3] -> int [5]
<3> Array[0]数组名类型
(1)数组名表示整个数组:int [5]
(2)表示首元素地址:int *
注意:为什么数组的地址类型要加(),[]运算符优先级大于*
int *Array[3]; -> int * [3]; //Array是数组,元素类型为int*
int (*Array)[3]; -> int (*)[3]; //Array是一个指针,保存 int [3]类型数组的地址
示例2:一维数组与二维数组的区别
<1> 一维数组 int Array[5];
//Array表示整个数组:int [5] -> &Array:int (*)[5] sizeof(Array)=sizeof(int [5])
//Array的元素类型:int
//Array表示数组首元素地址:int * ,通过地址访问4Byte空间
<2> 二维数组 int Array[4][5];
//Array表示整个数组:int [4][5] -> Array数组有4个元素每个元素又是一维数组,该数组有5个元素每个元素是int类型-> &Array:int (*)[4][5] sizeof(Array)=sizeof(int [4][5])=4*4*5=80Byte
//Array的元素类型:int [5]
//Array表示数组首元素地址:int (*)[5],通过地址访问20Byte空间
练习1:有二维数组a为3行5列,实现数组逆置,将值转换到二维数组b中,b有5行3列;
for(int i=0;i<3;i++)
{
for(int j=0;j<5;j++)
{
b[j][i]=a[i][j];
}
}
练习2:按以下输出格式打印杨辉三角的前n行。

分析:因为杨辉三角的元素保存分行列保存,所以我们需要定义一个二维数组来保存杨辉三角的值,而且每一行的元素个数跟行号相等,并且两条边都为1(第i行的第0列为1,第i行的第i列也为1),其他元素的值
#include <stdio.h>
void YanHui(int n);
int main(void)
{
int line=0;
printf("Input Line:>");
scanf("%d",&line);
YanHui(line);
}
void YanHui(int n)
{
int Array[20][20]={0};
if(n>=20)
return;
//两条边全为1
for(int i=0;i<n;i++)
{
Array[i][0]=1;
Array[i][i]=1;
}
//其他位置元素的值Array[i][j]=Array[i-1][j]+Array[i-1][j-1]
for(int i=2;i<n;i++) //行数
{
for(int j=1;j<n;j++)
{
Array[i][j]=Array[i-1][j]+Array[i-1][j-1];
}
}
//输出
for(int i=0;i<n;i++) //行
{
for(int k=n;k>=i;k–)
printf(" "); //空3格
for(int j=0;j<=i;j++) //列
{
printf("%-5d ",Array[i][j]);
}
printf("\\n");
}
}
四、字符数组
字符数组:数组中保存的元素全部为字符。
字符数组的定义:char 数组名[元素个数];
char buff[5]={'A','B','2','0','<'}; //正确
char buff[5]={1,2,3,4,5}; //正确
char buff[5];
char buff[5]={0};
4.1 字符数组与字符串
C语言中没有字符串这种数据类型,字符串如何保存?在C语言中字符串保存于字符数组中,即字符数组是字符串的容器。字符串的结束标志为'\\0'。
- 字符串的保存:<1> 常量区; <2> 字符数组中;
- 字符串:“hello world” -> 存储 -> "hello world\\0"
- '0' -> 字符0,ASCII码值=48,可见字符
- '\\0' -> 字符串的结束标志,ASCII码值=0,不可见字符
#include <stdio.h>
int main(void)
{
char buff1[5]={'A','b','C','d','e'};
//buff1是一个字符数组,该字符数组中保存的不是字符串
char buff2[5]={'A','b','c','d','\\0'};
//buff2是一个字符数组,该字符数组保存的元素可以认为是一个字符串
char buff3[5]={'A','B','V','E',0};
//buff3是一个字符数组,该字符数组保存的元素可以认为是一个字符串
char buff4[5]={'A','B','C','D'}; //最后一个元素默认初始为0
//buff4是一个字符数组,该字符数组保存的元素可以认为是一个字符串
char buff5[]={'A','B','C','D'};
//buff5是一个字符数组,该字符数组保存的不是字符串
char buff6[20]="hello world"; //字符数量=11,但是还有一个不可见字符'\\0' 最终为12个字符
//buff6是一个字符数组,保存的数据是"hello world",
printf("buff2=%s\\n",buff2);
//%s输出:从buff2位置开始判断只要不是0或'\\0'就输出然后地址偏移+1
printf("buff3=%s\\n",buff3);
printf("buff4=%s\\n",buff4);
}
4.2 字符指针
指针就是地址,在内存中,系统会为内存进行编码。一个字节对应一个地址编码,指针就是内存的地址编码。指针变量也是变量,占用空间和计算机的位数相关:X86架构的计算机地址为32bit,X64架构的计算机地址为64bit。
字符指针:字符变量的地址; =》 char *p;//p是字符指针变量,用于保存字符变量/字符数组的地址。
内存分区:
<1> 栈区:临时变量,局部变量,函数参数,函数返回值
<2> 堆区:用户手动开辟的空间
<3> 全局区:静态变量和全局变量
<4> 常量区:常量数据和const修饰的全局变量
<5> 代码区:存储代码
int main(void)
{
int Num=178; //Num是变量存放于栈区,178是常量存放于常量区
//程序运行:先为Num在栈区中开辟空间,再将常量区的178存放至Num对应的空间中
}
char buff[5]={'A','B','C','D','E'};
//buff是一维数组,数组类型为char [5],在使用中都是按照首元素地址使用(char *)
//buff->char *
char *p=buff;//保存char类型变量的地址
buff[1]='B';
p[1]='B'; //此时p和buff是通用的
char *p1="hello world";
//p1是字符指针变量,保存字符变量的地址
//"hello world"是字符串常量,存放于常量区
//表达式含义:将常量区"hello world"的地址赋值给p1;
p1[4]=='o'; //读取
p1[5]='M'; //写操作–错误
char *p="hello world";
//p保存是常量区中“hello world”字符串的首地址,只能通过p来读取,不能写
char buff[12]="hello world";
//buff是字符数组,有自己的空间保存"hello world",可以通过buff来改写“hello world”
五、字符串API
string .h 头文件定义了一个变量类型、一个宏和各种操作字符数组的函数。<string.h> 是 C 标准库中的一个头文件,提供了一组用于处理字符串和内存块的函数。这些函数涵盖了字符串复制、连接、比较、搜索和内存操作等。
1、库变量
Windows系统:size_t == unsigned long long //无符号64位整型
Linux系统:size_t == unsigned long //无符号64位整型
2、库宏
#define ((void*)0) NULL
3、库函数
| 1 | void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。 |
| 2 | int memcmp(const void *str1, const void *str2, size_t n) 把 str1 和 str2 的前 n 个字节进行比较。 |
| 3 | void *memcpy(void *dest, const void *src, size_t n) 从 src 复制 n 个字符到 dest。 |
| 4 | void *memmove(void *dest, const void *src, size_t n) 另一个用于从 src 复制 n 个字符到 dest 的函数。 |
| 5 | void *memset(void *str, int c, size_t n) 将指定的值 c 复制到 str 所指向的内存区域的前 n 个字节中。 |
| 6 | char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 |
| 7 | char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。 |
| 8 | char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。 |
| 9 | int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。 |
| 10 | int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节。 |
| 11 | int strcoll(const char *str1, const char *str2) 把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 |
| 12 | char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。 |
| 13 | char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。 |
| 14 | size_t strcspn(const char *str1, const char *str2) 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。 |
| 15 | char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。 |
| 16 | size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 |
| 17 | char *strpbrk(const char *str1, const char *str2) 检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。 |
| 18 | char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。 |
| 19 | size_t strspn(const char *str1, const char *str2) 检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。 |
| 20 | char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。 |
| 21 | char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。 |
| 22 | size_t strxfrm(char *dest, const char *src, size_t n) 根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。 |
5.1 strlen
函数原型:size_t strlen(const char *str)
函数功能:计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
函数参数:
【1】const char *str,str保存一个字符串的地址;
<1> 将一个字符串的地址直接赋值给str;
<2> 将一个保存字符的字符数组的地址赋值str;
函数返回:
返回字符串长度;
函数说明:使用strlen计算字符串长度,是从str保存的地址位置开始判断该地址对应的字符是不是'\\0',如果不是则字符数量自增1,地址偏移到下一个字符直到判断到字符'\\0'结束。
函数示例:
#include <stdio.h>
#include <string.h>
int main(void)
{
size_t len1=0,len2=0;
char *p = "hello world";
char buff[]="hello world";
printf("strlen(p)=%ld\\n",strlen(p));
printf("strlen(buff)=%ld\\n",strlen(buff));
printf("sizeof(p)=%ld\\n",sizeof(p));
printf("sizeof(buff)=%ld\\n",sizeof(buff));
}
面试题:sizeof()和strlen()的区别 <1> 本质:sizeof是C语言的运算符不是函数,strlen()是C语言的库函数 <2> 功能:sizeof计算数据类型/变量占用的空间大小,strlen()计算字符串的长度(不包含\\0) <3> 应用:sizeof()和strlen()计算字符数组,一般sizeof()的结果都是大于strlen
5.2 strcat
函数原型:char *strcat(char *dest, const char *src)
char *strncat(char *dest, const char *src, size_t n)
函数功能:
【1】strcat,把 src 所指向的字符串追加到 dest 所指向的字符串的结尾
【2】strncat,把 src 所指向的字符串的n个字符追加到 dest 所指向的字符串的结尾
函数参数:
【1】char *dest,dest指向的空间可写 -> 一定是个字符数组
【2】const char *src,src指向的空间可读可写 -> 可以是字符数组也可以是字符串常量
【3】size_t n,追加的字符个数
函数返回:
返回一个指向最终的目标字符串 dest 的指针。
函数说明:
char dest[20]="hello";
char src[10]="world";
strcat(dest,src); -> dest="helloworld"
//注意:dest指向的空间要能够存放src所有的字符
函数示例:
#include <stdio.h>
#include <string.h>
int main(void)
{
char dest[20]="hello";
char *src="world";
char *p=strcat(dest,src);
printf("p=%s\\n",p);
printf("dest=%s\\n",p);
char buff[]="1234567890"; //buff中有11个字符
p=strncat(dest,buff,9);
printf("p=%s\\n",p);
printf("dest=%s\\n",p);
}
5.3 strchr
函数原型:char *strchr(const char *str, int c)
char *strrchr(const char *str, int c)
函数功能:
【1】strchr,在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。
【2】strrch,在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。
函数参数:
【1】str,要查找的字符串;
【2】c,待查找的字符;
函数返回:
成功,返回字符C在str中的地址
失败,返回NULL;
函数示例:
#include <stdio.h>
#include <string.h>
int main(void)
{
char *str="hello world";
char *pAddr=strchr(str,'l');
printf("str=%p\\n",str);
if(pAddr!=NULL)
{
printf("pAddr=%p\\n",pAddr);
printf("pAddr=%s\\n",pAddr);
}
}
5.4 strcmp
函数原型:int strcmp(const char *str1, const char *str2)
int strncmp(const char *str1, const char *str2, size_t n)
函数功能:
【1】strcmp,比较str1和str2两个字符串是否相等;
【2】strncmp,比较str1和str2两个字符串的前n个字符是否相等;
函数参数:
【1】str1,待比较字符串1;
【2】str2,待比较字符串2;
【3】n,比较的字符个数;
函数返回:
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str1 大于 str2。
如果返回值 = 0,则表示 str1 等于 str2。
函数说明:
该函数的比较两个字符串的原理是基于字符串的每个字符对应的ASCII编码进行比较;
<1> str1="hello 123"; str2="hello 124"; strcmp(str1,str2)<0
<2> str1="hello 123"; str2="hello 124"; strncmp(str1,str2,7)=0
函数示例:
#include <stdio.h>
#include <string.h>
int main(void)
{
char *str1="hello 123";
char *str2="hello 129";
int num1=strcmp(str1,str2); //'3'-'9'=-6
printf("num1=%d\\n",num1);
int num2=strncmp(str1,str2,7);
printf("num2=%d\\n",num2);
}
5.5 strcpy
函数原型:char *strcpy(char *dest, const char *src)
char *strncpy(char *dest, const char *src, size_t n)
函数功能:
【1】strcpy,将src指向的字符串的所有内容拷贝至dest指向的空间;
【2】strncpy,将src指向的字符串的前n个字符拷贝至dest指向的空间;
函数参数:
【1】dest,指向用于存储复制内容的目标数组。
【2】src,要复制的字符串。
【3】n,要从src中复制的字符数
函数返回:
该函数返回最终复制(dest)的字符串。
函数示例:
#include <stdio.h>
#include <string.h>
int main(void)
{
char dest[10]="123456789";
char *src="ABCD";
strcpy(dest,src); //src指向的字符串的所有内容(包括'\\0')赋值到dest
printf("dest=%s\\n",dest);
for(int i=0;i<10;i++)
printf("%c",dest[i]);
printf("\\n");
//dest="ABCD\\06789"
dest[4]='E';
printf("dest=%s\\n",dest);
strncpy(dest,"abcdefg",5); //不会携带\\0
printf("dest=%s\\n",dest);
}
dest=ABCD
ABCD6789
dest=ABCDE6789
dest=abcde6789
5.6 strstr
函数原型: char *strstr(const char *haystack, const char *needle);
函数功能: 字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 \\0。
函数参数:
【1】haystack — 要搜索的主字符串。
【2】needle — 要查找的子字符串。
函数返回:
成功,返回needle在haystack字符串中的地址;
失败,返回NULL;
函数示例:
int main(void)
{
char dest[10]="123456789";
char *p=strstr(dest,"567");
if(p!=NULL)
{
printf("p=%s\\n",p);
}
}
5.7 strtok
函数原型:char *strtok(char *str, const char *delim)
函数功能:用于将字符串分割成一系列子字符串
函数参数:
【1】str: 要分割的字符串。在第一次调用时,传入要分割的字符串;后续调用时,传入 NULL,表示继续分割同一个字符串。
【2】delim: 分隔符字符串。strtok() 会根据这个字符串中的任意一个字符来分割 str。
函数返回:
指向下一个标记的指针。如果没有更多的标记,则返回 NULL。
函数说明:该函数会将str指向的字符串中的delim替换成'\\0';
#include <string.h>
#include <stdio.h>
int main ()
{
char str[80] = "This is \\0 www.runoob.com – website";
const char s[2] = "-";
char *token;
/* 获取第一个子字符串 */
token = strtok(str, s);
/* 继续获取其他的子字符串 */
while( token != NULL ) {
printf( "%s\\n", token );
token = strtok(NULL, s);
}
return(0);
}
六、字符串的输入和输出
字符串的输入和输出,对于输入来说只能向字符数组中去写入字符串;输出的话可以将字符数组/常量区中的字符串进行输出。
6.1 标准输入输出
#include <string.h>
int main(void)
{
char buff[10]={0};
printf("Input buff:>");
scanf("%s",buff);
//<1> 注意输入的字符数量不要超出buff的容量
//<2> 输入遇到空格会输入截断
printf("buff=%s\\n",buff);
}
<1>正常输入
Input buff:>123456789
buff=123456789
<2> 输入溢出
Input buff:>123456789ABCDEFG
buff=123456789ABCDEFG
*** stack smashing detected ***: terminated
已放弃 (核心已转储)
<3> 输入带空格
Input buff:>1234 5678
buff=1234
6.2 gets()
函数原型:char *gets(char *str)
函数功能: 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
函数参数:
【1】str,执行字符数组的空间
函数返回:
如果成功,该函数返回 str。如果发生错误或者到达文件末尾时还未读取任何字符,则返回 NULL。
6.3 puts()
函数原型:int puts(const char *str)
函数功能:把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
函数参数:
【1】str,str就是输出的字符串
函数返回:
返回输出的字符数量;
6.4 fgets()
函数原型:char *fgets(char *str, int n, FILE *stream)
函数功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。会将'\\n'也写入str指向的空间;
函数参数:
【1】str — 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
【2】n — 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
【3】stream — 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。
函数返回:
如果成功,该函数返回 str。如果发生错误或者到达文件末尾时还未读取任何字符,则返回 NULL。
函数示例:
int main(void)
{
char buff[10]={0};
printf("Input buff:>");
fgets(buff,sizeof(buff),stdin);
//<1> 注意输入的字符数量不要超出buff的容量
//<2> 输入遇到空格会输入截断
printf("buff=%s\\n",buff);
}
6.5 fputs()
函数原型: int fputs(const char *str, FILE *stream)
函数功能: 把字符串写入到指定的流 stream 中,但不包括空字符。
函数参数:
【1】str,这是一个数组,包含了要写入的以空字符终止的字符序列。
【2】stream,这是指向 FILE 对象的指针,该 FILE 对象标识了要被写入字符串的流。
函数返回:
返回输出的字符个数
函数备注:puts输出时会追加一个'\\n',但是fputs不会;
补充1:const
在定义变量时,用于修饰所定义的变量是常量(只读);const的使用是在定义变量时使用;
用法1:const 数据类型 变量名=初始化值;
用法2:数据类型 const 变量名=初始化值;
int Num1=10; //Num1是一个变量
const int Num2=10; //Num2是变量,但是只读
Num1=100; //正确
Num2=100; //错误
补充2:atoi()
头文件: #include <stdlib.h>
函数原型: int atoi(const char *str)
函数功能: 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。
函数参数:
【1】str,带有数字字符的字符串
函数返回:
返回转换后的整型数据
函数示例:
int main(void)
{
char *p1="123abc56";
printf("%d\\n",atoi(p1)); //数字字符后出现其他字符则转换结束
char *p2="123 56";
printf("%d\\n",atoi(p2));
char *p3="a123"; //字符串的首个字符不是数字字符则转换结束
printf("%d\\n",atoi(p3));
char *p4="-123ab4"; //字符串的首个字符可以是正负号
printf("%d\\n",atoi(p4));
}
123
123
0
-123
头文件: #include <stdlib.h>
函数原型: double atof(const char *str)
函数功能: 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。
函数参数:
【1】str,带有数字字符的字符串
函数返回:
返回转换后的浮点型数据
函数示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char *p1="12.34abc";
printf("atof(p1)=%lf\\n",atof(p1));
}
补充3:大小端存储
系统在存储数据时,数据一般存储在内存中,数据的存储有2种方式:大端存储和小端存储;
int Num=0x12345678; Num变量的首地址(低地址)为0x11;【0x11,0x12,0x13,0x14】
//字节划分:低字节->高字节【0x78,0x56,0x34,0x12】
<1> 大端存储:高字节数据存储在低地址,低字节数据存储在高地址;
地址[低->高]:0x11 0x12 0x13 0x14
数据[高->低]:0x12 0x34 0x56 0x78
<2> 小端存储:高字节数据存储在高地址,低字节数据存储在低地址;
地址[低->高]:0x11 0x12 0x13 0x14
数据[低->高]:0x78 0x56 0x34 0x12
面试题:判断当前系统是大端存储还是小端存储;
int IsMemory_Mode(void)
{
int num=1;
char *p=(char *)# //基于p指针访问改地址空间->1Byte
if(*p==1)
{
printf("小端存储!\\n");
return 1;
}
else
{
printf("大端存储!\\n");
return 0;
}
}
补充4:随机数
头文件: #include <stdlib.h>
函数原型: int rand(void)
函数功能: 返回一个范围在 0 到 RAND_MAX 之间的伪随机数。
函数参数:void
函数返回:
返回一个随机数;
函数说明: RAND_MAX 是一个常量,它的默认值在不同的实现中会有所不同,但是值至少是 32767。
<1> 在Linux系统中:#defineRAND_MAX2147483647
<2> 该函数返回的随机数是需要一个随机数种子进行播种才能生成出不同的随机;
头文件: #include <stdlib.h>
函数原型: void srand(unsigned int seed)
函数功能: 播种由函数 rand 使用的随机数发生器。
函数参数:
【1】seed — 这是一个整型值,用于伪随机数生成算法播种。
函数返回: void
头文件: #include <time.h>
函数原型: time_t time(time_t *seconds)
函数功能: 返回自纪元 Epoch(1970-01-01 00:00:00 UTC)起经过的时间,以秒为单位。如果 seconds 不为空,则返回值也存储在变量 seconds 中。
函数参数:
【1】seconds,传出参数;
函数返回:
返回时间;
补充5:进制问题
在C语言中,数据的表现形式有二进制、八进制、十进制、十六进制;
<1> 十进制->二进制:除2取余,所有余数逆序
19÷2=9······1
9÷2=4·······1
4÷2=2·······0
2÷2=1·······0
1÷2=0·······1
19->10011
<2> 十进制->八进制:除8取余,所有余数逆序
<3> 十进制->十六进制:除16取余,所有余数逆序
<4> 二进制<->八进制:3位二进制表示1位八进制
000-0 001-1 010-2 011-3 100-4 101-5 110-6 111-7
<5> 二进制<->十六进制:4位二进制表示1位八进制
练习:
自定义实现strcat函数;
#include <stdio.h>
//char *strcat(char *dest,const char *src)
//将src指向的字符串追加到dest指向的末尾
char *my_strcat(char *dest,const char *src)
{
char *pAddr=dest;
while(*pAddr)
pAddr++; //pAddr最终指向字符串的'\\0'位置
while(*src)
{
*pAddr++=*src++;
}
*pAddr=0; //作为字符串的结束标志
return dest;
}
int main(void)
{
char dest[20]="hello";
char *src="world";
printf("dest=%s\\n",my_strcat(dest,src)); //链式表达式
}





