Void * 活用

本文深入探讨C/C++语言中的void及void指针类型,解析void的含义及其在函数返回值与参数中的应用规则,同时讨论void指针的特性与使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C/C++语言void及void指针深层探索  

1.概述  
许多初学者对C/C++语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说,并详述void及void指针类型的使用方法与技巧。  

2.void的含义  
void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。  
void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义:  
void a;  
这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。  
void真正发挥的作用在于:  
(1) 对函数返回的限定;  
(2) 对函数参数的限定。  
众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。  
例如:  
float *p1;  
int *p2;  
1 = p2;  
其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:  
1 = (float *)p2;  
而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:  
void *p1;  
int *p2;  
1 = p2;  
但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错:  
void *p1;  
int *p2;  
2 = p1;  
提示“'=' : cannot convert from 'void *' to 'int *'”。  

3.void的使用  
下面给出void关键字的使用规则:  

规则一 如果函数没有返回值,那么应声明为void类型  
在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如:  
add ( int a, int b )  
{  
return a + b;  
}  
int main(int argc, char* argv[])  
{  
rintf ( "2 + 3 = %d", add ( 2, 3) );  
}  
程序运行的结果为输出:  
2 + 3 = 5  
这说明不加返回值说明的函数的确为int函数。  
林锐博士《高质量C/C++编程》中提到:“C++语言有很严格的类型安全检查,不允许上述情况(指函数不加类型声明)发生”。可是编译器并不一定这么认定,譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确,所以不能寄希望于编译器会做严格的类型检查。  
因此,为了避免混乱,我们在编写C/C++程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。  

规则二 如果函数无参数,那么应声明其参数为void  
在C++语言中声明一个这样的函数:  
int function(void)  
{  
return 1;  
}  
则进行下面的调用是不合法的:  
function(2);  
因为在C++中,函数参数为void的意思是这个函数不接受任何参数。  
我们在Turbo C 2.0中编译:  
#include "stdio.h"  
fun()  
{  
return 1;  
}  
main()  
{  
rintf("%d",fun(2));  
getchar();  
}  
所以,无论在C还是C++中,若函数不接受任何参数,一定要指明参数为void。  

规则三 小心使用void指针类型  
按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:  
void * pvoid;  
void++; //ANSI:错误  
void += 1; //ANSI:错误  
//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。  
//例如:  
int *pint;  
int++; //ANSI:正确  
int++的结果是使其增大sizeof(int)。  
但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致。  
因此下列语句在GNU编译器中皆正确:  
void++; //GNU:正确  
void += 1; //GNU:正确  
void++的执行结果是其增大了1。  
在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:  
void * pvoid;  
(char *)pvoid++; //ANSI:正确;GNU:正确  
(char *)pvoid += 1; //ANSI:错误;GNU:正确  
GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。  

规则四 如果函数的参数可以是任意类型指针,那么应声明其参数为void *  
典型的如内存操作函数memcpy和memset的函数原型分别为:  
void * memcpy(void *dest, const void *src, size_t len);  
void * memset ( void * buffer, int c, size_t num );  
这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是char *,那才叫真的奇怪了!这样的memcpy和memset明显不是一个“纯粹的,脱离低级趣味的”函数!  
下面的代码执行正确:  
//示例:memset接受任意类型指针  
int intarray[100];  
memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0  
//示例:memcpy接受任意类型指针  
int intarray1[100], intarray2[100];  
memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1  
有趣的是,memcpy和memset函数返回的也是void *类型,标准库函数的编写者是多么地富有学问啊!  

规则五 void不能代表一个真实的变量  
下面代码都企图让void代表一个真实的变量,因此都是错误的代码:  
void a; //错误  
function(void a); //错误  
void体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(还有人妖?)。  
void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。  

4.总结  
小小的void蕴藏着很丰富的设计哲学,作为一名程序设计人员,对问题进行深一个层次的思考必然使我们受益匪浅。  

2.创建单任务—SRAM静态内存这里,我们创建一个单任务,任务使用的栈和任务控制块都使用静态内存,即预先定 义好的全局变量,这些预先定义好的全局变量都存在内部的 SRAM 中。2.1定义任务函数任务实际上就是一个无限循环且不带返回值的 C 函数。目前,我们创建一个这样的任 务,让开发板上面的 LED 灯以 500ms 的频率闪烁, /********************************************************************** * @ 函数名 : LED_Task * @ 功能说明: LED_Task任务主体 * @ 参数 : * @ 返回值 : 无 ********************************************************************/static void LED_Task(void* parameter){ while (1) { LED1_ON; vTaskDelay(500); /* 延时500个tick */ printf("LED_Task Running,LED1_ON\r\n"); LED1_OFF; vTaskDelay(500); /* 延时500个tick */ printf("LED_Task Running,LED1_OFF\r\n"); }}AI生成项目2.2空闲任务与定时器任务堆栈函数实现当我们使用了静态创建任务的时候,configSUPPORT_STATIC_ALLOCATION 这个宏 定 义 必 须为 1 (在 FreeRTOSConfig.h 文 件 中 ) , 并且 我 们需 要 实 现两 个 函数 : vApplicationGetIdleTaskMemory()与 vApplicationGetTimerTaskMemory(),这两个函数是用 户设定的空闲(Idle)任务与定时器(Timer)任务的堆栈大小,必须由用户自己分配,而 不能是动态分配,/* 空闲任务任务堆栈 */static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];/* 定时器任务堆栈 */static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];/* 空闲任务控制块 */static StaticTask_t Idle_Task_TCB; /* 定时器任务控制块 */static StaticTask_t Timer_Task_TCB; /** ********************************************************************** * @brief 获取空闲任务的任务堆栈和任务控制块内存 * ppxTimerTaskTCBBuffer : 任务控制块内存 * ppxTimerTaskStackBuffer : 任务堆栈内存 * pulTimerTaskStackSize : 任务堆栈大小 * @date 2018-xx-xx ********************************************************************** */ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize){ *ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */ *ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */ *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */}/** ********************************************************************* * @brief 获取定时器任务的任务堆栈和任务控制块内存 * ppxTimerTaskTCBBuffer : 任务控制块内存 * ppxTimerTaskStackBuffer : 任务堆栈内存 * pulTimerTaskStackSize : 任务堆栈大小 ********************************************************************** */ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize){ *ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */ *ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */ *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */}AI生成项目2.3 定义任务栈目前我们只创建了一个任务,当任务进入延时的时候,因为没有另外就绪的用户任务, 那么系统就会进入空闲任务,空闲任务是 FreeRTOS 系统自己启动的一个任务,优先级最 低。当整个系统都没有就绪任务的时候,系统必须保证有一个任务在运行,空闲任务就是 为这个设计的。当用户任务延时到期,又会从空闲任务切换回用户任务。 在 FreeRTOS 系统中,每一个任务都是独立的,他们的运行环境都单独的保存在他们 的栈空间当中。那么在定义好任务函数之后,我们还要为任务定义一个栈,目前我们使用 的是静态内存,所以任务栈是一个独立的全局变量,具体见代码清单 14-5。任务的栈占用 的是 MCU 内部的 RAM,当任务越多的时候,需要使用的栈空间就越大,即需要使用的 RAM 空间就越多。一个 MCU 能够支持多少任务,就得看你的 RAM 空间有多少。/* AppTaskCreate任务任务堆栈 */static StackType_t AppTaskCreate_Stack[128];/* LED任务堆栈 */static StackType_t LED_Task_Stack[128];AI生成项目2.4定义任务控制块定义好任务函数和任务栈之后,我们还需要为任务定义一个任务控制块,通常我们称 这个任务控制块为任务的身份证。在 C代码上,任务控制块就是一个结构体,里面有非常 多的成员,这些成员共同描述了任务的全部信息,/* AppTaskCreate 任务控制块 */static StaticTask_t AppTaskCreate_TCB;/* AppTaskCreate 任务控制块 */static StaticTask_t LED_Task_TCB;AI生成项目2.5 静态创建任务一个任务的三要素是任务主体函数,任务栈,任务控制块,那么怎么样把这三个要素 联合在一起?FreeRTOS 里面有一个叫静态任务创建函数 xTaskCreateStatic(),它就是干这 个活的。它将任务主体函数,任务栈(静态的)和任务控制块(静态的)这三者联系在一 起,让任务可以随时被系统启动, /* 创建 AppTaskCreate 任务 */ AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate, //任务函数 (const char* )"AppTaskCreate", //任务名称 (uint32_t )128, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )3, //任务优先级 (StackType_t* )AppTaskCreate_Stack, //任务堆栈 (StaticTask_t* )&AppTaskCreate_TCB); //任务控制块 if(NULL != AppTaskCreate_Handle)/* 创建成功 */ vTaskStartScheduler(); /* 启动任务,开启调度 */AI生成项目2.6 启动任务当任务创建好后,是处于任务就绪(Ready),在就绪态的任务可以参与操作系统的调度。 但是此时任务仅仅是创建了,还未开启任务调度器,也没创建空闲任务与定时器任务(如果使 能了 configUSE_TIMERS 这个宏定义),那这两个任务就是在启动任务调度器中实现,每个操 作系统,任务调度器只启动一次,之后就不会再次执行了,FreeRTOS 中启动任务调度器的函 数是 vTaskStartScheduler(),并且启动任务调度器的时候就不会返回,从此任务管理都由 FreeRTOS管理,此时才是真正进入实时操作系统中的第一步,  vTaskStartScheduler();   /* 启动任务,开启调度 */2.7 main.c文件内容全貌现在我们把任务主体,任务栈,任务控制块这三部分代码统一放到 main.c 中,我们在 main.c 文件中创建一个 AppTaskCreate 任务,这个任务是用于创建用户任务,为了方便管 理,我们的所有的任务创建都统一放在这个函数中,在这个函数中创建成功的任务就可以 直接参与任务调度了,/* FreeRTOS头文件 */#include "FreeRTOS.h"#include "task.h"/* 开发板硬件bsp头文件 */#include "bsp_led.h"#include "bsp_usart.h"static void AppTaskCreate(void);/* 用于创建任务 */ static void LED_Task(void* pvParameters);/* LED_Task任务实现 */ static void BSP_Init(void);/* 用于初始化板载相关资源 *//**************************** 任务句柄 ********************************//* * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么 * 这个句柄可以为NULL。 */ /* 创建任务句柄 */static TaskHandle_t AppTaskCreate_Handle;/* LED任务句柄 */static TaskHandle_t LED_Task_Handle; /********************************** 内核对象句柄 *********************************//* * 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核 * 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我 * 们就可以通过这个句柄操作这些内核对象。 * * 内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信, * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数 * 来完成的 * */ /******************************* 全局变量声明 ************************************//* * 当我们在写应用程序的时候,可能需要用到一些全局变量。 *//* AppTaskCreate任务任务堆栈 */static StackType_t AppTaskCreate_Stack[128];/* LED任务堆栈 */static StackType_t LED_Task_Stack[128]; /* AppTaskCreate 任务控制块 */static StaticTask_t AppTaskCreate_TCB;/* AppTaskCreate 任务控制块 */static StaticTask_t LED_Task_TCB; /* 空闲任务任务堆栈 */static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];/* 定时器任务堆栈 */static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH]; /* 空闲任务控制块 */static StaticTask_t Idle_Task_TCB; /* 定时器任务控制块 */static StaticTask_t Timer_Task_TCB; /** * 使用了静态分配内存,以下这两个函数是由用户实现,函数在task.c文件中有引用 * 当且仅当 configSUPPORT_STATIC_ALLOCATION 这个宏定义为 1 的时候才有效 */void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize); void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize); /***************************************************************** * @brief 主函数 * @param 无 * @retval 无 * @note 第一步:开发板硬件初始化 第二步:创建APP应用任务 第三步:启动FreeRTOS,开始多任务调度 ****************************************************************/int main(void){ /* 开发板硬件初始化 */ BSP_Init(); printf("这是一个[野火]-STM32全系列开发板-FreeRTOS-静态创建单任务!\r\n"); /* 创建 AppTaskCreate 任务 */ AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t )AppTaskCreate, //任务函数 (const char* )"AppTaskCreate", //任务名称 (uint32_t )128, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )3, //任务优先级 (StackType_t* )AppTaskCreate_Stack, //任务堆栈 (StaticTask_t* )&AppTaskCreate_TCB); //任务控制块 if(NULL != AppTaskCreate_Handle)/* 创建成功 */ vTaskStartScheduler(); /* 启动任务,开启调度 */ while(1); /* 正常不会执行到这里 */ } /*********************************************************************** * @ 函数名 : AppTaskCreate * @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面 * @ 参数 : 无 * @ 返回值 : 无 **********************************************************************/static void AppTaskCreate(void){ taskENTER_CRITICAL(); //进入临界区 /* 创建LED_Task任务 */ LED_Task_Handle = xTaskCreateStatic((TaskFunction_t )LED_Task, //任务函数 (const char* )"LED_Task", //任务名称 (uint32_t )128, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )4, //任务优先级 (StackType_t* )LED_Task_Stack, //任务堆栈 (StaticTask_t* )&LED_Task_TCB); //任务控制块 if(NULL != LED_Task_Handle)/* 创建成功 */ printf("LED_Task任务创建成功!\n"); else printf("LED_Task任务创建失败!\n"); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL(); //退出临界区} /********************************************************************** * @ 函数名 : LED_Task * @ 功能说明: LED_Task任务主体 * @ 参数 : * @ 返回值 : 无 ********************************************************************/static void LED_Task(void* parameter){ while (1) { LED1_ON; vTaskDelay(500); /* 延时500个tick */ printf("LED_Task Running,LED1_ON\r\n"); LED1_OFF; vTaskDelay(500); /* 延时500个tick */ printf("LED_Task Running,LED1_OFF\r\n"); }} /*********************************************************************** * @ 函数名 : BSP_Init * @ 功能说明: 板级外设初始化,所有板子上的初始化均可放在这个函数里面 * @ 参数 : * @ 返回值 : 无 *********************************************************************/static void BSP_Init(void){ /* * STM32中断优先级分组为4,即4bit都用来表示抢占优先级,范围为:0~15 * 优先级分组只需要分组一次即可,以后如果有其他的任务需要用到中断, * 都统一用这个优先级分组,千万不要再分组,切忌。 */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* LED 初始化 */ LED_GPIO_Config(); /* 测试硬件是否正常工作 */ LED1_ON; /* 让程序停在这里,不再继续往下执行 */ while(1); /* 串口初始化 */ USART_Config(); } /** ********************************************************************** * @brief 获取空闲任务的任务堆栈和任务控制块内存 * ppxTimerTaskTCBBuffer : 任务控制块内存 * ppxTimerTaskStackBuffer : 任务堆栈内存 * pulTimerTaskStackSize : 任务堆栈大小 * @date 2018-xx-xx ********************************************************************** */ void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize){ *ppxIdleTaskTCBBuffer=&Idle_Task_TCB;/* 任务控制块内存 */ *ppxIdleTaskStackBuffer=Idle_Task_Stack;/* 任务堆栈内存 */ *pulIdleTaskStackSize=configMINIMAL_STACK_SIZE;/* 任务堆栈大小 */} /** ********************************************************************* * @brief 获取定时器任务的任务堆栈和任务控制块内存 * ppxTimerTaskTCBBuffer : 任务控制块内存 * ppxTimerTaskStackBuffer : 任务堆栈内存 * pulTimerTaskStackSize : 任务堆栈大小 ********************************************************************** */ void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize){ *ppxTimerTaskTCBBuffer=&Timer_Task_TCB;/* 任务控制块内存 */ *ppxTimerTaskStackBuffer=Timer_Task_Stack;/* 任务堆栈内存 */ *pulTimerTaskStackSize=configTIMER_TASK_STACK_DEPTH;/* 任务堆栈大小 */} /********************************END OF FILE****************************/AI生成项目注意:在使用静态创建任务的时候必须要将 FreeRTOSConfig.h 中 的 configSUPPORT_STATIC_ALLOCATION 宏配置为 1。下载后,可以看到板子上面的 LED 灯已经在 闪烁,说明我们创建的单任务(使用静态内存)已经跑起来了。
最新发布
08-16
//装载问题 队列式分支限界法求解 #include "Queue.h" #include <iostream> using namespace std; int N = 0; template<class Type> class QNode { public: QNode *parent; //指向父节点的指针 bool LChild; //左儿子标识 Type weight; //节点所相应的载重量 }; template<class Type> void EnQueue(Queue<QNode<Type>*>&Q,Type wt,int i,int n,Type bestw,QNode<Type>*E,QNode<Type> *&bestE,int bestx[],bool ch); template<class Type> Type MaxLoading(Type w[],Type c,int n,int bestx[]); int main() { float c = 0; float w[100] = {0};//下标从1开始 int x[100]; float bestw; cin>>N; cin >>c; for(int i=1; i<=N; i++) { cin>>w[i]; } cout<<"Ship load:"<<c<<endl; cout<<"The weight of the goods to be loaded is:"<<endl; for(int i=1; i<=N; i++) { cout<<w[i]<<" "; } cout<<endl; bestw = MaxLoading(w,c,N,x); cout<<"Result:"<<endl; for(int i=1; i<=N; i++) { cout<<x[i]<<" "; } cout<<endl; cout<<"The optimal loading weight is:"<<bestw<<endl; return 0; } //将活节点加入到活节点队列Q中 template<class Type> void EnQueue(Queue<QNode<Type>*>&Q,Type wt,int i,int n,Type bestw,QNode<Type>*E,QNode<Type> *&bestE,int bestx[],bool ch) { if(i == n)//可行叶节点 { if(wt == bestw) { //当前最优装载重量 bestE = E; bestx[n] = ch; } return; } //非叶节点 QNode<Type> *b; b = new QNode<Type>; b->weight = wt; b->parent = E; b->LChild = ch; Q.Add(b); } template<class Type> Type MaxLoading(Type w[],Type c,int n,int bestx[]) {//队列式分支限界法,返回最优装载重量,bestx返回最优解 //初始化 Queue<QNode<Type>*> Q; //活节点队列 Q.Add(0); //同层节点尾部标识 int i = 1; //当前扩展节点所处的层 Type Ew = 0, //扩展节点所相应的载重量 bestw = 0, //当前最优装载重量 r = 0; //剩余集装箱重量 for(int j=2; j<=n; j++) { r += w[j]; } QNode<Type> *E = 0, //当前扩展节点 *bestE; //当前最优扩展节点 //搜索子集空间树 //**************begin************/ //**************end**************/ //构造当前最优解 //**************begin************/ //**************end**************/ return bestw; } 有一批共个集装箱要装上 2 艘载重量分别为 C1 和 C2 的轮船,其中集 装箱 i 的重量为 Wi ,且 装载问题要求确定是否有一个合理的装载方案可将这个集装箱装上这2艘轮船。如果有,找出一种装载方案。 容易证明:如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。 (1)首先将第一艘轮船尽可能装满; (2)将剩余的集装箱装上第二艘轮船。 在 //**************begin************/ //**************end**************/ 中间补全代码
06-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值