C语言进阶篇—指针

指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。

3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4. 指针的运算。

一、字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char* ;

一般使用:(*p存单个字符)

#include<stdio.h>

int main()
{    
    char ch = 'w';
    char* pc = &ch;//字符指针存放一个字符变量的地址
    *pc = 'b';
    printf("%c\n", ch);

    return 0;
}

还有一种使用方式如下:(*p存字符串)

#include<stdio.h>

int main()
{    
    char* p = "abcdef";
    printf("%s\n", p);
    
    return 0;
}

//问题1:这里是把一个字符串放到p指针变量里了吗?

//其实这里特别容易让同学以为是把字符串 abcdef 放到字符指针 p 里了,但是本质是把字符串 abcdef 首字符的地址放到了指针p中。

//问题2:在某些代码编辑器中,char* p = "abcdef"这串代码可能会报错,可能会显示将这个字符的首地址赋给p是不安全的,那该如何解决呢?

假设代码这样写:

#include<stdio.h>

int main()

{
    char* p = "abcdef";

    *p = ‘w’;//将*p的值进行修改
    printf("%s\n", p);
    
    return 0;
}

//上面的代码虽然不会报错但是会挂起,原因是*p在改变abcdef的值的时候发生了写入访问冲突,也就是改变*p的值时发生了错误

//实际上我们这样写的时候,abcdef是常量字符串,常量字符串不可修改,但是*p是可以修改的,这两种的关系不匹配导致会报错

//如果想让指针p不能修改且要编译器报错,可以写成const char* p = "abcdef";

下面看一道面试题:

#include <stdio.h>

int main()
{
    const char* p1 = "abcdef";
    const char* p2 = "abcdef";

    char arr1[] = "abcdef";
    char arr2[] = "abcdef";

    if (p1 == p2)
        printf("p1==p2\n");
    else
        printf("p1!=p2\n");

    if (arr1 == arr2)
        printf("arr1 == arr2\n");
    else
        printf("arr1 != arr2\n");
    
    return 0;
}

输出结果:

原因分析:

  • p1 和 p2 都指向相同的字符串字面量 "abcdef",它们的地址可能相等(因为编译器会合并相同的字符串字面量)。
  • 像 "abcdef" 这样的字符串字面量,编译器通常会把它们放在程序的只读数据段。如果多个指针变量都被赋值为同一个字符串字面量,那它们的值(也就是内存地址)可能会指向同一个内存位置,或者编译器优化后也会把它们合并到同一个地址
  • arr1 和 arr2 是两个独立的数组,它们在内存中开辟的空间肯定不同,所以它们的地址肯定不相等。

二、指针数组

在《指针》章节我们也学了指针数组,指针数组是一个存放指针的数组
这里我们再复习一下数组是什么?

定义一个整型数组,每个元素存的是整型的数字:

int arr[10];

如果每个元素存的是字符,叫做字符数组:

char ch[5];

那么指针数组就是每个元素是指针类型的,如下:

int* arr2[6]—数组的每个元素都是整型指针

char* arr3[5]—数组每个元素都是字符指针

下面看这样一段代码:

#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[3] = { arr1,arr2,arr3 };//存放指针的数组

    return 0;
}

它们在内存中存放的方式如下:

我们将上述代码进行修改:

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[3] = { arr1,arr2,arr3 };
    //parr数组每个元素的下标 0 1 2
    //parr[0]拿到arr1的首地址,即parr[0] = &arr1[0]
    //parr[1]拿到arr2的首地址
    //parr[2]拿到arr3的首地址
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        int j = 0;//每个arr数组的下标, j = 0,1,2,3,4
        for (j = 0; j < 5; j++)
        {
            printf("%d ", *(parr[i] + j));//每行的起始地址加j
        }
        printf("\n");//打印完一行换行
    }
    return 0;
}

编译结果:

注:实际上这个东西并不是二维数组,二维数组指的是包含多个一维数组的数组,二维数组的元素是int型,我们只是用parr指针数组模拟出一个二维数组,parr数组实际上还是一维数组,而是里面存放类型为int*的元素。

三、数组指针

1 数组指针的定义

数组指针是指针?还是数组? 答案是:指针。

我们已经熟悉:

整形指针: int * pint; 能够指向整形数据的指针。

浮点型指针: float * pf; 能够指向浮点型数据的指针。

那数组指针应该是:能够指向数组的指针

下面代码哪个是数组指针?

int *p1[10];  //指针数组—p1是数组名,10个元素,类型为int*

int (*p2)[10];//数组指针,p2可以指向一个数组,该数组有10个元素,每个元素是int类型

//p1, p2分别是什么?

int (*p)  [10];

//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

2 &数组名vs数组名

对于下面的数组:

int arr[10];

arr 和 &arr 分别是啥?

我们知道arr是数组名,数组名表示数组首元素的地址。

那&arr数组名到底是啥?

我们看一段代码:

#include <stdio.h>

int main()
{    
    int arr[10] = { 0 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    printf("%p\n", &arr);
    //数组名是首元素地址?
    //数组名通常都表示的都是数组首元素地址
    //但是有两个除外
    //1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
    //2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
    return 0;
}

可见数组名和&数组名打印的地址是一样的。

难道两个是一样的吗?

我们再看一段代码:

#include <stdio.h>

int main()
{    
    int arr[10] = { 0 };


    printf("%p\n", arr);
    printf("%p\n", arr+1);

    printf("%p\n", &arr[0]);
    printf("%p\n", &arr[0]+1);

    printf("%p\n", &arr);
    printf("%p\n", &arr+1);


    return 0;
}

根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。

实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)

本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型

数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40(0x28).

3 数组指针的使用

那数组指针是怎么使用的呢?

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

看代码:

#include <stdio.h>

int main()
{    
    int arr[10] = { 0 };
    int* p = arr;
    int (*p2)[10] = &arr;

    //整型指针是用来存放整型的地址
    //字符数组是用来存放字符的地址
    //数组指针是用来存放数组的地址
    return 0;
}

一个数组指针的使用:

#include <stdio.h>

void print(int arr[3][5], int r, int c)//常规数组传参
{
    int i = 0;
    for (i = 0; i < r;i++)
    {
        int j = 0;
        for (j = 0; j < c;j++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

void print1(int (*p)[5], int r, int c)//数组指针的使用
{


    //int (*p)[5]这种写法要知道如下:
    //1.p的类型是:int (*)[5]
    //2.p是指向一个整型数组的,数组5个元素 int [5]
    //3.p+1跳过一个5个int元素的数组
    

    int i = 0;
    for (i = 0;i < r;i++)
    {
        int j = 0;
        for (j = 0;j < c;j++)
        {
            printf("%d ", *(*(p + i) + j));
            //printf("%d ", p[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} };
    print(arr, 3, 5);
    
    print1(arr, 3, 5);
    //arr表示第一行的地址,第一行的地址,是一个一维数组的地址,
    //假设第一行数组叫做date[5],&date传过去用一个指针p接收
    //p = &arr = &arr[0] 
    //p+i --> p+0指向第一行,p+1指向第二行,p+2指向第三行
    //*(p+i) --> arr[i]
    //
    return 0;
}

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

int arr[5];--》整型数组

int *parr1[10];--》整型指针数组,每个元素的类型是int*

int (*parr2)[10];--》整型数组指针,每个元素的类型是int

int (*parr3[10])[5];--》存放数组指针的数组,

四、数组参数、指针参数

1 一维数组传参

#include<stdio.h>

void test(int arr[])//ok,传形参数组,数组元素个数可省略
{
}

void test(int arr[10])//ok,传形参数组
{
}

void test(int* arr)//ok,数组名是首元素地址,首元素地址用指针存储
{
}

void test2(int* arr[20])//ok,传形参指针数组
{
}

void test2(int** arr)//ok,数组名首元素是指针类型的,传参可用二级指针接收数组首元素
{
}

int main()
{
    int arr[10] = { 0 };
    int* arr2[20] = { 0 };
    test(arr);
    test2(arr2);
}

2 二维数组传参

void test(int arr[3][5])//ok?,正确,二维数组的形参表示
{
}

void test(int arr[][])//ok?,错误,二维数组的形参列数不可省略
{
}

void test(int arr[][5])//ok?,正确,二维数组形参的行可以省略
{
}

void test(int* arr)//ok?,错误,二维数组的指针类型不匹配
{
}

void test(int* arr[5])//ok?,错误, int* arr[5]的类型是数组不是指针
{
}

void test(int (*arr)[5])//ok?,正确,二维数组的指针类型表示
{
}

void test(int** arr)//ok?,一维数组的地址不能放在二级指针里面
{
}

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

3 一级指针传参

void print(int* p, int sz)
{
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("%d\n", *(p + i));
    }
}
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9 };
    int* p = arr;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //一级指针p,传给函数
    print(p, sz);
    return 0;
}

问:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

void print(int* p)//一级指针可接收地址,一级指针本身,一维数组的首地址等等
{

}

int a = 10;
int ptr = &a;
int arr[10];

print(&a);
print(ptr);
print(arr);

4 二级指针传参

void test(int** ptr)
{
    printf("num = %d\n", **ptr);
}
int main()
{
    int n = 10;
    int* p = &n;
    int** pp = &p;
    test(pp);
    test(&p);
    return 0;
}

问:当函数的参数为二级指针的时候,可以接收什么参数?

test(int** p)//二级指针可接收一级指针的地址,二级指针本身等等
{

}

int* p1;
int** p2;
int* arr[10];

test(&p1);
test(p2);
test(arr);

五、函数指针

指向函数的指针叫做函数指针

先看一段代码:

经过以上操作,我们果然取出了函数的地址

数组指针里面有数组的地址和数组首元素的地址,那么函数有这个区别吗,再看一段代码:

对于函数来说,&函数名和函数名都是函数的地址

函数地址的储存

int (*pf)(int, int) = &Add;

那这个有什么用呢,相当于可以通过pf解引用,找到Add函数,然后运行Add函数的功能,看如下代码:

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int (*pf)(int, int) = &Add;
    int ret = (*pf)(2, 3);
    printf("%d\n", ret);

    return 0;
}

函数指针的用途:

我们先写一个简易的计算器

#include <stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****  1.add  2.sub    ****\n");
	printf("****  3.mul  4.div    ****\n");
	printf("****  0.exit          ****\n");
	printf("**************************\n");
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;		
		case 3:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;		
		case 4:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

我们会发现一个问题,就是在选择计算的方式的时候,代码的重复度很高,我们可以通过封装一个函数来减少重复度,这时候就可以通过函数指针的方式来解决。

以下是简化版本:

#include <stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****  1.add  2.sub    ****\n");
	printf("****  3.mul  4.div    ****\n");
	printf("****  0.exit          ****\n");
	printf("**************************\n");
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

void calc(int (*pf)(int, int))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
	ret = (*pf)(x, y);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(Add);
			break;
		case 2:
			calc(Sub);
			break;		
		case 3:
			calc(Mul);
			break;		
		case 4:
			calc(Div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);
	return 0;
}

六、函数指针数组

先回顾一下指针数组:存放指针的数组

例如:

int* arr1[5];

char* arr2[6];

所以函数指针数组就是存放函数指针的数组

函数指针数组的怎么写呢?

我们参照指针数组的写法可以发现:

int (*arr[4])(int, int) = { Add, Sub, Mul, Div };

例如下面的代码:

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

int main()
{
	int (*pf)(int, int) = Add;//pf是函数指针
	int (*arr[4])(int, int) = { Add, Sub, Mul, Div };//arr就是函数指针的数组
	int i = 0;
	for (i = 0;i < 4;i++)
	{
		int ret = arr[i](8, 4);
		printf("%d\n", ret);
	}
	return 0;
}

使用函数指针数组实现计算器:

#include<stdio.h>
void menu()
{
	printf("**************************\n");
	printf("****  1.add  2.sub    ****\n");
	printf("****  3.mul  4.div    ****\n");
	printf("****  0.exit          ****\n");
	printf("**************************\n");
}

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Div(int x, int y)
{
	return x / y;
}

int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;

	int (*pfArr[5])(int, int) = { 0, Add, Sub, Mul, Div };
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if (input>=1 && input<=4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

注:使用函数指针数组的好处就在如果要加入其他的功能,可以极大的省略代码的修改

七、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数 的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进 行响应。

首先演示一个非常经典的函数qsort:(展示快速排序)

解释一下这里面的参数

void* base 待排序数据的起始位置

size_t num 待排序数据元素的个数

size_t width 待排序数据元素的大小 (单位是字节)

int (*cmp)(const void* e1, const void* e2 ) 函数指针—比较函数

qsort的返回值如下:

首先演示一下qsort函数的使用:

例如我排序一个数组,之前使用了冒泡排序,这里我们用qsort函数

void bubble_sort(int arr[], int sz)//冒泡排序
{
	int i = 0;//i是排序需要的次数
	for (i = 0; i < sz - 1; i++)
	{
		int flag = 1;//假设数组是有序的
		//一趟冒泡排序
		int j = 0;//j是数组下标
		for (j = 0; j < sz-1-i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;//产生交换操作时将标志位改变,说明数组不是有序数组
			}
		}
		if (flag == 1)//如果本身就是有序数列,则跳出循环
		{
			break;
		}
	}
}

//void*指针不能解引用,所以比较大小的时候需要强制类转换
int cmp_int(const void* e1, const void* e2)
{
    return (*(int*)e1 - *(int*)e2);
}


int main()
{       

    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };

    //把数组排成升序
    int sz = sizeof(arr) / sizeof(arr[0]);
    //bubble_sort(arr, sz);

    qsort(arr, sz, sizeof(arr[0]), cmp_int);

    int i = 0;
    for (i = 0;i < sz;i++)
    {
        printf("%d ", arr[i]);
    }

    return 0;
}

与之相类似的,qsort函数也可以排序其他的类型,只需要修改比较函数的函数指针即可,不同的函数指针排序的效果截然不同。

比如说我可以比较结构体不同类型的大小,代码如下:

struct Stu//创建一个结构体
{
	char name[20];
	int age;
};

int cmp_stu_by_name(const void* e1, const void* e2)//通过名字比较
{
	//strcmp 返回值--> >0,==0,<0
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

int cmp_stu_by_age(const void* e1, const void* e2)//通过年龄比较
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

int main()
{	
    struct Stu s[] = { {"zhangsan", 15},{"lisi", 30},{"wangwu", 25}};
	int sz = sizeof(s) / sizeof(s[0]);
	//qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	return 0;
}

以上说明了qsort函数的便捷之处,我们现在将这个函数模拟实现(使用冒泡排序的方式)

#include <stdio.h>

int cmp_int(const void* e1, const void * e2)
//比较两个数的大小需要知道这两个数的地址
{
  return (*(int*)e1 - *(int*) e2);
}

void Swap(void* buf1, void* buf2, int width)
//Swap函数需要设置需交换字符的每一位字节地址,还需要设置字节长度,以便可以知道
//需要交换几次
{
	int i = 0;
	for (i = 0; i < width; i++)//交换每一节的字节,直到所有字节交换完成,则两元素互换
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
//bubble函数的格式需跟qsort函数的格式保持一致,即可模拟相似的功能
{
	int i = 0;//i是排序需要的次数
	for (i = 0; i < sz - 1; i++)
	{
        int flag = 1;
		int j = 0;//j是数组下标
		for (j = 0; j < sz-1-i; j++)
		{
			if (cmp((char*)base+j*width, (char*)base + (j+1) * width) >0)
			{
				//交换
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
                //base需强制转换成char*类型才能进行地址的计算
                flag = 0;
			}
		}
        if(flag == 1)
        {
            break:    
        }
	}
}

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

	int sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

总结:本部分介绍回调函数概念,重点讲解 qsort 函数参数、使用示例,包括整数和结构体数组排序,最后用冒泡排序模拟 qsort 实现,突出其灵活性与便捷性。

打开链接下载源码: https://pan.quark.cn/s/05da658a2377 在信息技术领域中,输入法作为操作系统的一个核心构成部分,赋予了用户利用键盘输入多语种文字的能力。"ime-日语输入法安装必须文件"这一资源是一套为日语输入法部署而设计、包含全部必要元素的集成包,对于那些需要在个人计算机上执行日语文字输入的操作者而言具有不可替代的作用。接下来将深入剖析其中所包含的核心概念。 IME(Input Method Editor,输入法编辑器)是操作系统内的一种软件支持服务,其功能在于为非拉丁字符环境提供文字输入方案,例如中文、日文、韩文等文字系统。在日本地区,IME通常被用来将罗马字(罗马拼音)形式的输入转换为平假名、片假名乃至汉字。此压缩文件内含的日语IME文件夹即为执行这一转换功能的关键要素。 kbdjpn.dll被视为一个关键的系统性文件,其意指“Japanese Keyboard Layout”(日语键盘布局)。该动态链接库文件负责设定日语键盘的排列方式及快捷操作组合,使用户能够借助常规的QWERTY键盘输入日语文字。倘若缺少这一文件,即便已经安装了日语输入法,依然无法正常显示及输入日语字符。 另外,imjp81k.dll同样是一个重要的系统性构成,它属于日语IME的范畴,全称为“Input Method Japanese for Windows 8.1 and later, Katakana mode”(适用于Windows 8.1及更新版本的日语输入法,片假名模式)。该文件支持日语的片假名输入,是处理日语输入的核心组成部分。在安装或升级日语输入法的过程中,保证imjp81k.dll的准确性与完整性显得尤为关键。 压缩包所含的"Window...
内容概要:本文研究了基于DPWMA调制与正负序分离的ANPC三电平并网逆变器前馈控制策略,旨在解决传统三电平逆变器在谐波抑制、电网不平衡适应性及动态响应方面的技术瓶颈。通过构建融合双极性倍频脉宽调制(DPWMA)、正负序分离锁相控制与电网电压前馈的一体化控制体系,全面优化逆变器的输出波形质量、相位同步精度与抗扰能力。文章深入分析了ANPC三电平拓扑的结构优势,如开关损耗均衡、中点电位可控性强和电压利用率高等特点,并设计了包含信号采集、核心控制与调制驱动三层架构的完整控制系统。通过Simulink仿真平台对稳态运行、电网不平衡及动态扰动等多种工况进行验证,结果表明该策略显著降低了总谐波畸变率,提升了锁相精度与系统动态稳定性,有效增强了逆变器在复杂电网环境下的适应能力和运行可靠性。; 适合人群:具备电力电子、自动控制及新能源并网相关基础知识,从事新能源发电、微电网、电力系统仿真等领域的科研人员与工程技术人员,特别适合研究生及以上层次的研究者。; 使用场景及目标:①用于提升大功率并网逆变器在电网电压不平衡、谐波干扰和动态扰动等复杂工况下的运行性能;②为高电能质量要求的应用场景提供先进控制解决方案;③支持科研仿真、论文复现与实际工程项目中的高性能并网控制系统设计与优化。; 阅读建议:建议结合提供的Simulink仿真模型进行实践操作,重点理解DPWMA调制机制、正负序分离锁相算法与电网电压前馈控制之间的协同作用,按照文档结构系统学习,并与传统控制策略进行对比分析,以深入掌握改进策略的技术优势与实现细节。
代码下载链接: https://pan.quark.cn/s/d9794888cbc0 ### G代码经典解释程序知识点详解 #### 一、引言 随着数控技术的持续进步,尤其是开放式数控系统的广泛应用,软件层面的设计在数控领域占据了核心地位。G代码作为数控机床编程的基础语言,在自动化生产流程中发挥着不可或缺的作用。本文的核心内容是关于一个基于Linux平台、采用C语言开发的G代码解释程序的设计思路及其具体实现。 #### 二、G代码解释器概述 **1. 设计背景** - 当前数控技术发展的主要方向是开放式数控系统,这类系统具备出色的可扩展能力、良好的移植性、高度的互换性以及优异的互操作性等优势。 - 计算机硬件技术的快速发展使得在PC平台上构建数控系统成为可能,进而推动了全软件式数控系统的普及。 **2. G代码解释器的重要性** - G代码解释器在全软件式数控系统中是至关重要的组成部分,其主要职责是将G代码转化为数控系统能够识别的数据格式。 - 为了提升数控系统的开放程度,G代码解释器的设计必须兼顾开放性和灵活性。 #### 三、G代码解释器设计与实现 **1. 总体结构设计** - G代码解释器主要由两个核心部分构成:G代码关键字函数表(GKFT)和G代码分组(GG)。 - GKFT用于解析G代码中的关键字,它是解释器的核心骨架;而GG则是语法检查的基础框架。 **2. G代码关键字函数表(GKFT)** - GKFT是一种专门用于存储G代码关键字及其关联处理函数的数据结构。 - 解释器通过查询GKFT,能够根据特定的G代码关键字调用相应的处理函数,从而完成对G代码的有效解析。 - 此种设计方法不仅简化了解释器的构建过程,同时也增强了其可扩展性,因为新增功能...
已经博主授权,源码转载自 https://pan.quark.cn/s/458849d2eac8 Microblaze代表由Xilinx公司研发的一款软核处理器,其核心特性在于使用户能够针对FPGA(Field Programmable Gate Array)平台进行嵌入式系统的个性化构建。此“Xinlin中Microblaze的培训教程”致力于辅助学习人员深入理解和熟练掌握Microblaze在Xilinx开发环境中的实际应用。 一、Microblaze基础 Microblaze作为一款可配置的32位RISC处理器,具备高度适应性,允许在设计中根据具体需求对性能、功耗及面积进行灵活调整。Microblaze支持多种指令集架构(ISA),涵盖Xtensa-like和Classic两种模式,并且与包括UART、SPI、I2C在内的多种外设接口标准保持兼容。 二、Xilinx ISE与Vivado工具 Xilinx ISE(Integrated Software Environment)是一个用于FPGA系统设计、实现和调试的集成开发平台,而Vivado则是一款功能更为先进且全面的工具套件。在本次教程中,学员将学会如何在上述工具中配置和执行Microblaze处理器,以及如何开发相关的硬件描述语言(HDL)代码。 三、Microblaze硬件设计 在Xinlin提供的教程里,学员将学习如何在Xilinx FPGA中部署Microblaze处理器。这涉及到选择合适的处理器配置参数,如时钟频率、缓存容量和外设接口设置。此外,学员还将接触到创建和连接内存模块、中断控制器以及其他必需硬件组件的方法。 四、软件开发 Microblaze的软件开发通常涉及嵌入式编程,采用C或C++语言来...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值