C语言 指针与数组

news2025/1/20 5:53:41

C语言 指针与数组

  • 引言
    • 1. 指针与数组之间的联系
    • 2. 指针与字符串之间的联系
  • 一、指针与数组
    • 1. 对比两者的区别
    • 2. 指针数组的用法
    • 3. 数组指针的用法
  • 二、数组参数、指针参数
    • 1. 一维数组传参
    • 2. 二维数组传参
  • 三、指针与函数 (了解)
    • 1. 其实函数也有地址
    • 2. 函数指针
    • 3. 函数指针数组
    • 4. qsort 函数中的 compare 函数指针
  • 四、指针与数组的笔试题
    • 结论
    • 例题1
    • 例题2
    • 例题3
    • 例题4
    • 例题5
  • 五、指针笔试题
    • 例题1
    • 例题2
    • 例题3
    • 例题4

引言

1. 指针与数组之间的联系

在 C语言中,虽然我们平时访问数组的时候是用 arr[ i ] 进行表示,但在底层解析的时候,其实是通过 *(arr + i) 这样的指针配合解引用的方式来做到的。这是指针与数组之间所能联系的核心所在。

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
arr[i]; // *(arr + i);

观察上面两行代码,arr 作为数组名,表示首元素的地址,即 &arr[0].
由于数组的地址是由低到高连续存储的,所以知道了首元素的地址,就能够间接访问到数组的剩余元素了。

2. 指针与字符串之间的联系

由于在 C语言中,字符串通常由字符数组构造,所以字符串也可以通过 " 头部指针 " 来间接地拿到每个字符。这样一来,指针就像一条线一样,可以 " 顺藤摸瓜 " 。

#include <stdio.h>

int main() {

	char arr[] = "hello world";
	printf("%s\n", arr); // arr 表示首元素的地址
 
	return 0;
}

// 输出结果:hello world

一、指针与数组

1. 对比两者的区别

#include <stdio.h>

int main() {

	int a = 1, b = 2, c = 3, x = 4, y = 5;

	int arr[5] = { 1,2,3,4,5 }; // 整型数组

	int* parr[5] = { &a, &b, &c, &x, &y }; // 指针数组

	int (*parr2)[5] = &arr; // 数组指针

	return 0;
}

注意事项: [ ] 的优先级要高于 * 号

① 整型数组表示数组,[ ] 先与 arr 结合,所以 arr 本质上就是一个数组。

② 指针数组表示数组,[ ] 先与 parr 结合,所以 parr 本质上就是一个数组。

③ 数组指针表示指针,* 先与 parr2 结合,所以 parr2 本质上就是一个指针,它指向长度为 5 的数组。

1-1

2. 指针数组的用法

指针数组即存放指针的数组,或者说,指针数组是存放地址的数组。

程序清单:打印二维数组

#include <stdio.h>

int main() {

	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	int* parr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 5; j++) {
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

// 输出结果:
// 1 2 3 4 5
// 2 3 4 5 6
// 3 4 5 6 7

分析:

1-2

3. 数组指针的用法

数组指针表示指向数组的指针。

程序清单:打印二维数组

void print(int (*arr)[5], int row, int column) {

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < column; j++) {
			printf("%d ", *(*(arr+i) + j ));
		}
		printf("\n");
	}
}

int main() {

	int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };
	int row = sizeof(arr) / sizeof(arr[0]);
	int column = sizeof(arr[0]) / sizeof(arr[0][0]);

	print(arr, row, column);

	return 0;
}

// 输出结果:
// 1 2 3 4 5
// 2 3 4 5 6
// 3 4 5 6 7

1-3

二、数组参数、指针参数

1. 一维数组传参

#include <stdio.h>

void test(int arr[]) {} // √
void test(int* arr) {} // √

void test2(int* arr[]) {} // √
void test2(int** arr) {} // √


int main() {

	int arr1[10] = { 0 };
	int* arr2[10] = { 0 };

	test(arr1); // 首元素的地址 &arr1[0]
	test2(arr2); // 首元素的地址 &arr2[0]
}

注意事项:

① arr1 是一个一维整型数组,它存放着 10 个 int 类型的元素。arr1 表示首元素的地址,即 &arr1[0],所以形参在接收时,可以利用 int* 指针接收。

② arr2 是一个一维指针数组,它存放着 10 个 int* 类型的元素。arr2 表示首元素的地址,即 &arr2[0],所以形参在接收时,可以利用 int** 指针接收。

2. 二维数组传参

#include <stdio.h>

void test(int arr[3][5]) {} // √
void test(int arr[][5]) {} // √
void test(int arr[][]){} // X
void test(int (* arr)[5]){} // √


int main() {

	int arr[3][5] = { 0 };
	test(arr);

}

注意事项:

二维数组的数组名表示第一行数组的地址。在函数形参接收时,可以利用数组名直接接收 (但不能省略列数);也可以利用数组指针的方式来接收。

三、指针与函数 (了解)

1. 其实函数也有地址

程序清单:

#include <stdio.h>

int add(int a, int b) {

	return a + b;
}

int main() {

	add(10, 20);

	printf("%p\n", &add);
	printf("%p\n", add);

	return 0;
}

输出结果:

1-4

注意事项:

① 函数在内存中也是有对应的地址的。
② &函数名 和 函数名,两者是一样的意思,都可以当作函数的地址使用。

2. 函数指针

函数指针即一个函数地址,或者说指针指向一个函数。

#include <stdio.h>

int add(int a, int b) {
	return a + b;
}

int main() {
	
	int (*p) (int, int) = &add; // 1 (变量 p 是一个函数指针)
	int ret = (*p)(20, 30); // 2
	printf("%d\n", ret);

	return 0;
}

// 输出结果:50

注意事项:

① 注释1,我们可以说 p 指向 add 函数。第一个 int 表示 add 返回值为 int,(*p) 声明了这就是一个指针,(int, int) 表示 add 的形参类型。

② 注释2,(*p) 表示解引用 p,它的含义就等价于 add 函数。通过先解引用,再传参,就相当于使用了 add 函数。

3. 函数指针数组

函数指针数组,顾名思义就是存放函数指针的数组,或者说存放函数地址的数组。

#include <stdio.h>

int add(int a, int b) {
	return a + b;
}

int sub(int a, int b) {
	return a - b;
}

int mult(int a, int b){
	return a * b;
}

int div(int a, int b) {
	return a / b;
}

int main() {

	// 函数指针数组
	int (* parr[4])(int, int) = { add, sub, mult, div }; // parr 和 [] 先结合,所以 parr 本质上就是一个数组

	for (int i = 0; i < 4; i++) {

		int ret =  parr[i](40, 20);
		printf("%d ", ret);
	}

	return 0;
}

// 输出结果:
// 60  20  800  2

4. qsort 函数中的 compare 函数指针

下面是我之前写的 qsort 函数的博客,qsort 参数中有一个 compare 指针,用来指定用哪个自定义排序函数。如果读者感兴趣的,可以看一下。

qsort 函数博客

四、指针与数组的笔试题

结论

结论1:

对于一维数组来说,数组名就是首元素的地址;
对于二维数组来说,数组名就是第一行数组的地址。

但有两个例外:

① sizeof(数组名),此时数组名表示整个数组,计算的是整个数组占用内存的大小。
② &数组名,此时数组名表示整个数组,取出的是整个数组的地址。

结论2:

指针变量是用来存放地址的。所以,地址的存放需要多大空间,指针变量的大小就应该是多大。

① 32位 机器,支持 32位 虚拟地址空间,其产生的地址就是 32位,所以此时指针变量就需要 32位 的空间存储,即 4字节。
② 64位 机器,支持 64位 虚拟地址空间,其产生的地址就是 64位,所以此时指针变量就需要 64位 的空间存储,即 8字节。

结论3:

① sizeof 是一个操作符,它是用来计算变量 (类型) 所占内存空间大小的,计算单位是 " 字节 "。

② 字符串的结束标志是一个 ’ \0 ’ 的转义字符。在使用格式化输出时, ’ \0 ’ 的作用相当于告诉了编译器, ’ \0 ’ 是一个停止的标志。在使用 strlen 这个库函数计算字符串的长度时,也是一样的道理,它只计算 ’ \0 ’ 之前的长度。

例题1

#include <stdio.h>

int main() {

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

	printf("%d\n", sizeof(a)); // 求整个数组所占内存的大小 -> 16

	printf("%d\n", sizeof(a + 0)); // sizeof(&arr[0]) -> 4/8

	printf("%d\n", sizeof(*a)); // sizeof(arr[0]) -> 4

	printf("%d\n", sizeof(a + 1)); // sizeof(&arr[1]) -> 4/8

	printf("%d\n", sizeof(a[1])); // 4

	printf("%d\n", sizeof(&a)); // 拿到整个数组的地址,依然是地址 -> 4/8

	printf("%d\n", sizeof(*&a)); // sizeof(a) -> 16

	printf("%d\n", sizeof(&a + 1)); // 跳过整个数组,拿到的还是地址 -> 4/8

	printf("%d\n", sizeof(&a[0])); // 4/8

	printf("%d\n", sizeof(&a[0] + 1)); // sizeof(&arr[1]) 4/8

	return 0;
}

例题2

#include <stdio.h>

int main() {

	char arr[] = { 'a','b','c','d','e','f' }; // [ a b c d e f ]

	printf("%d\n", sizeof(arr)); // 求整个数组所占内存的大小 -> 6

	printf("%d\n", sizeof(arr + 0)); // sizeof(&arr[0]) -> 4/8

	printf("%d\n", sizeof(*arr)); // sizeof(arr[0]) -> 1

	printf("%d\n", sizeof(arr[1])); // 1

	printf("%d\n", sizeof(&arr)); // 拿到整个数组的地址,依然是地址 -> 4/8

	printf("%d\n", sizeof(&arr + 1)); // 跳过整个数组,拿到的还是地址 -> 4/8

	printf("%d\n", sizeof(&arr[0] + 1)); // sizeof(&arr[1]) -> 4/8

	printf("%d\n", strlen(arr)); // 这里 arr 表示首元素的地址,由于 '\0' 不知道在什么位置,所以 strlen 求得为随机值

	printf("%d\n", strlen(arr + 0)); // 随机值

	printf("%d\n", strlen(*arr)); // strlen(arr[0]) ->  strlen 需要接收的是一个指针变量,这里非法访问

	printf("%d\n", strlen(arr[1])); // 同理,非法访问

	printf("%d\n", strlen(&arr)); // 由于 '\0' 不知道在什么位置,随机值

	printf("%d\n", strlen(&arr + 1)); // 由于 '\0' 不知道在什么位置,随机值

	printf("%d\n", strlen(&arr[0] + 1)); // 由于 '\0' 不知道在什么位置,随机值

	return 0;
}

例题3

#include <stdio.h>

int main() {

	char arr[] = "abcdef"; // [ a b c d e f \0 ]

	printf("%d\n", sizeof(arr)); // 7

	printf("%d\n", sizeof(arr + 0)); // sizeof(&arr[0]) -> 4/8

	printf("%d\n", sizeof(*arr)); // sizeof(arr[0]) -> 1

	printf("%d\n", sizeof(arr[1])); // 1

	printf("%d\n", sizeof(&arr)); // 4/8

	printf("%d\n", sizeof(&arr + 1)); // 4/8

	printf("%d\n\n", sizeof(&arr[0] + 1)); // 4/8

	printf("%d\n", strlen(arr)); // 6

	printf("%d\n", strlen(arr + 0)); // 6

	printf("%d\n", strlen(*arr)); // 非法访问

	printf("%d\n", strlen(arr[1])); // 非法访问

	printf("%d\n", strlen(&arr)); // 6

	printf("%d\n", strlen(&arr + 1)); // 跳过整个数组,由于 '\0' 不知道在什么位置,随机值

	printf("%d\n", strlen(&arr[0] + 1)); // strlen(&arr[1]) -> 5

	return 0;
}

例题4

#include <stdio.h>

int main() {

	char* p = "abcdef"; // p 指向 字符 'a',[ a b c d e f \0 ]

	printf("%d\n", sizeof(p)); // 字符 'a' 的地址 -> 4/8

	printf("%d\n", sizeof(p + 1)); // 字符 'b' 的地址 -> 4/8

	printf("%d\n", sizeof(*p)); // sizeof('a') -> 1

	printf("%d\n", sizeof(p[0])); // p[0] <==> *(p+0),sizeof('a') -> 1

	printf("%d\n", sizeof(&p)); // 指针 p 的地址 -> 4/8

	printf("%d\n", sizeof(&p + 1)); // 跳过指针 p 的地址 -> 4/8

	printf("%d\n\n", sizeof(&p[0] + 1)); // 字符 'b' 的地址 -> 4/8

	printf("%d\n", strlen(p)); // 6

	printf("%d\n", strlen(p + 1)); // 从字符 'b' 往后数字符数 -> 5

	//printf("%d\n", strlen(*p)); //  非法访问

	//printf("%d\n", strlen(p[0])); // 非法访问

	printf("%d\n", strlen(&p)); // 随机值

	printf("%d\n", strlen(&p + 1)); // 随机值

	printf("%d\n", strlen(&p[0] + 1)); // 从字符 'b' 往后数字符数 -> 5

	return 0;
}

例题5

#include <stdio.h>

int main() {

	int a[3][4] = { 0 };

	printf("%d\n", sizeof(a)); // 求整个二维数组所占内存的大小 -> 12 * 4 = 48

	printf("%d\n", sizeof(a[0][0])); // 4

	printf("%d\n", sizeof(a[0])); // a[0] 单独放在了 sizeof 的内部,二维数组第一行元素所占内存大小 -> 4*4 = 16

	printf("%d\n", sizeof(a[0] + 1)); // a[0] 并没有单独放在 sizeof 的内部,所以 a[0] 在这里就作为第一行第一个元素的地址
	// 所以 a[0] + 1 在这里就作为第一行第二个元素的地址,sizeof(&arr[0][1]) -> 4/8 

	printf("%d\n", sizeof(*(a[0] + 1))); // sizeof(arr[0][1]) -> 4

	printf("%d\n", sizeof(a + 1)); // 二维数组第二行的地址 ->  4/8

	printf("%d\n", sizeof(*(a + 1))); // 对二维数组第二行的地址解引用 -> 第二行元素的所有元素所占内存的大小 -> 4*4 = 16

	printf("%d\n", sizeof(&a[0] + 1)); // 二维数组第二行的地址 -> 4/8

	printf("%d\n", sizeof(*(&a[0] + 1)));// 对二维数组第二行的地址解引用 -> 第二行元素的所有元素所占内存的大小 -> 4*4 = 16

	printf("%d\n", sizeof(*a)); // 对二维数组第一行的地址解引用 -> 第一行元素的所有元素所占内存的大小 -> 4*4 = 16

	printf("%d\n", sizeof(a[3])); // a[3] 单独放在了 sizeof 的内部,二维数组第四行元素所占内存大小 -> 4*4 = 16

	return 0;
}

五、指针笔试题

例题1

#include <stdio.h>

int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };
	
	int* ptr = (int*)(&arr + 1); // 将数组指针转换成一个整型指针
	// arr+1 表示跳过一个元素,&arr+1 表示跳过整个数组
	printf("%d, %d\n", *(arr + 1), *(ptr - 1)); 
	return 0;
}

// 输出结果:2, 5

分析:

1-5

例题2

#include <stdio.h>

int main()
{
	int arr[3][2] = { {2, 4}, {6, 8}, {3, 7} };
	int* p = arr[0]; // arr[0] 表示数组名,p指向二维数组的第一行的第一个元素

	printf("%d\n", p[1]); // p[1] -> *(p + 1) -> 第一行第二个元素
	return 0;
}

// 输出结果:4

分析:

1-6

例题3

#include <stdio.h>

int main()
{
	int arr[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

	int* ptr1 = (int*)(&arr + 1); // 跳过整个数组
	int* ptr2 = (int*)(*(arr + 1)); // arr为数组名,表示数组的第一行地址,所以 arr+1 为数组的第二行开头

	printf("%d, %d\n", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

// 输出结果:10, 5

分析:

1-7

例题4

#include <stdio.h>

int main()
{
	char* arr[] = { "go","to","school" };
	char** pa = arr; // arr 表示首元素的地址
	
	pa++;
	printf("%s\n", *pa);
	return 0;
}

//输出结果:to

1-8

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

03_SpringBoot项目配置

文章目录SpringBoot项目配置0x01_properties格式配置文件0x02_yml格式配置文件配置对象类型数据配置集合类型配置数组类型0x03_properties和yml的区别优先级区别0x04_配置文件在项目中的位置0x05_bootstrap配置文件0x06_springboot项目结构SpringBoot项目配置 SpringBoot默认读…

Mysql数据库和SQL语句

一、数据库介绍&安装 1. 什么数据库软件 在前期学习的过程中&#xff0c;对于数据的保存方式有两种体现&#xff1a; 一种是将数据保存到本地的文件中&#xff0c;优点是可以持久保存&#xff0c;但是数据管理查询等相当麻烦。 一种是将数据保存到本地的内存中&#xff0c…

Linux学习记录——삼 基本指令(3)及了解权限

接着上一篇把一些基本命令写完 unzip解压默认解压到当前目录&#xff0c;加上-d后面跟路径就可以解压到指定目的地 tar指令 不同文件格式的压缩指令&#xff0c;可以直接看内容&#xff0c;不需要打开。tar后面有几个指令选项。-c表示创建压缩文件&#xff0c;-z打包并压缩&am…

E. Matrix and Shifts(思维+遍历正对角线)

Problem - 1660E - Codeforces 你会得到一个大小为nn的二进制矩阵A。行从上到下从1到n编号&#xff0c;列从左到右从1到n编号&#xff0c;位于第i行和第j列交点的元素称为Aij。考虑一组4个操作。 循环地将所有行向上移动。索引为i的行将被写在i-1行的位置上&#xff08;2≤i≤…

文件权限概念,相关操作

一&#xff0c;文件权限的基本概念 权限&#xff1a;操作系统限制对资源访问的一种机制。 文件权限的信息展示&#xff0c;使用ls -l 命令即可查看&#xff1a; 整个文件信息可以分为以下几部分&#xff1a; &#xff08;一&#xff09;第一个字段表示文件类型 和 文件权限。…

Socket套接字编程

文章目录1、网络字节序列2、socket编程接口<1>socket常见ARI<2>sockaddr结构 VS sockaddr_in结构3、UDP套接字4、TCP套接字5、总结1、网络字节序列 内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端…

腾讯T9纯手写基于Mycat中间件的分布式数据库架构笔记

随着移动互联网的兴起和大数据的蓬勃发展&#xff0c;系统的数据量正呈几何倍数增长&#xff0c;系统的压力也越来越大&#xff0c;这时最容易出现的问题就是服务器繁忙&#xff0c;我们可以通过增加服务器及改造系统来缓解压力&#xff0c;然后采用负载均衡、动静分离、缓存系…

流媒体传输 - RTSP Over HTTP

RTSP 的标准端口是 554&#xff0c;但是由于各种不同的防火墙等安全策略配置的原因&#xff0c;客户端在访问 554 端口时可能存在限制&#xff0c;从而无法正常传输 RTSP 报文。 但是 HTTP 端口&#xff08;80 端口&#xff09;是普遍开放的&#xff0c;于是就有了让 RTSP 报文…

【Android App】给三维的地球仪贴上动物贴纸实战(附源码和演示 超详细必看)

需要源码和图片集请点赞关注收藏后评论区留言~~~ 一、纹理贴图 给三维物体穿衣服的动作&#xff0c;通常叫做给三维图形贴图&#xff0c;更专业地说叫纹理渲染。 渲染纹理的过程主要由三大项操作组成&#xff0c;分别说明如下&#xff1a; &#xff08;1&#xff09;启用纹理…

STL的常用遍历算法(20221128)

STL的常用算法 概述&#xff1a; 算法主要是由头文件<algorithm> <functional> <numeric> 组成。 <algorithm>是所有STL头文件中最大的一个&#xff0c;涉及比较、交换、查找、遍历等等&#xff1b; <functional>定义了一些模板类&#xff0…

2022VR高级研修班总结

本人有幸参加2022VR高级研修班&#xff0c;此次高级研修班由赵沁平院士和丁文华院士领衔&#xff0c;全国知名专家及长期在相关领域从事产业、管理、科研工作的专家参与&#xff0c;带来了18个专题讲座&#xff0c;内容涵盖虚拟现实技术与系统现状与发展、产学研合作与产业协同…

都什么年代了,你居然还连不上GitHub?

前言 众所周知&#xff0c;GitHub是我们程序员在上班或者学习的时候经常会逛的一个地方[手动狗头]&#xff0c;而且如果我们想参与开源项目的话&#xff0c;GitHub也是一个很好的平台。 可问题是&#xff0c;GitHub网页总是进不去&#xff0c;提交代码到GitHub也总是超时&…

在Navicat上如何停止正在运行的MYSQL语句

目录 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;正文 1. 图形化停止SQL 2. 用SQL方式停止运行的SQL &#xff08;1&#xff09;找到运行的SQL的ID &#xff08;2&#xff09;运行kill命令杀掉SQL &#xff08;一&#xff09;前言 众所周知&#xff0c;…

BI数据分析软件有哪些?为什么说奥威BI很特别?

经过十几年的发展&#xff0c;以及近几年国家的大力鼓励发展大数据智能产业等原因&#xff0c;BI数据分析软件开始越来越被大家所熟知&#xff0c;那么BI数据分析软件都有哪些&#xff0c;为什么说奥威BI数据分析软件很特别&#xff1f;它对企业的大数据智能数据可视化分析起到…

什么样的人最适合做软件测试---喜欢找人帮忙办事的人

今天和大家说下什么样类型的人适合做软件测试。 经验干货&#xff0c;可仔细品 很多测试工程师面试中也可能会遇到问题&#xff0c;说怎么做一名优秀合格的测试工程师&#xff0c;需要有哪些品质&#xff0c;很多人会回答说要仔细&#xff0c;要承受压力&#xff0c;要有责任感…

2013-2020年全国31省数字经济数据集

1、时间&#xff1a;2013-2020年 2、来源&#xff1a;整理自国家统计J和统计NJ 3、指标包括&#xff1a; 信息化基础&#xff1a;"光缆线路长度(公里)、移动电话基站&#xff08;万个&#xff09;、信息传输、软件和信息技术服务业城镇单位就业人员(万人)、年末常住人口…

Android-P夜间模式

0 前言 Android-P可通过“Developer options > Night mode”打开夜间模式&#xff0c;本文研究其配置和效果。 验证环境&#xff1a;Pixel 3a、模拟器 1 设置 图1.1 Night mode上图对应XML配置如下&#xff1a; <ListPreferenceandroid:key"dark_ui_mode"and…

【Python】六、程序流程控制综合训练

文章目录实验目的一、列表定义二、元组定义三、列表的常用操作append() -- 向列表的尾部添加元素insert(index&#xff0c;object) -- 向指定的下标处添加元素sort&#xff08;&#xff09;-- 进行排序&#xff08;从下到大 int类型&#xff09;可以对字母进行排序&#xff08;…

多态原理、虚表指针与虚基表的内存布局。

文章目录前言多态虚函数静态类型与动态类型重载、覆盖和隐藏的区别final和override虚函数的默认实参虚析构函数在构造函数和析构函数中调用虚函数多态原理各种形式继承的虚函数内存布局单一继承无覆盖单一继承有覆盖单一虚拟继承有覆盖多继承无覆盖多继承有覆盖菱形继承有覆盖菱…

STC/MLLT--学习笔记

gmm建模方差使用对角矩阵的前提是假设特征之间相互独立&#xff0c;使用full或者block-diagonal矩阵可以对相关性的特征建模&#xff0c;但是参数增多。为了解决使用这个问题&#xff0c;有两种方法&#xff1a; feature-space 使用DCT或者LDA去相关model-space 不同的模型可以…