【STM32+CUBEMX+HAL库】RTC设备学习小结

什么是RTC设备?

RTC设备即real time clock的缩写,是一种掉电也能继续计时的计时器。虽然它只有简单的计时和触发中断的功能,但它掉电也能继续运行则让它的价值瞬间上升了无数倍。

CUBEmx配置

图一

RTC设备因为其独特的运行方式(即掉电依旧运行)导致它不能使用HSE或者HSI进行分频,否则资源消耗太大,小小的纽扣电池根本吃不消。所以我们可以选择单片机内部的LSI或者使用外部晶振LSE,推荐使用外部晶振LSE,因为单片机内部的LSI容易受到电压以及温度的影响导致精度不足。

在这里插入图片描述

在这里插入图片描述

以上为基本的一些设置。

在这里插入图片描述

在这里插入图片描述

以上分别是设置中断和设置系统日期。

在这里插入图片描述

在这里插入图片描述

因为接下来的例程会使用的串口和led灯所以要在cube中配置一下。

HAL库中有关RTC设备的API讲解

在编程之前我们需要先了解一下我们一会用到的api:

/*设置系统时间*/
HAL_StatusTypeDef HAL_RTC_SetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format) 
/*读取系统时间*/
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format)
/*设置系统日期*/
HAL_StatusTypeDef HAL_RTC_SetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
/*读取系统日期*/
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_DateTypeDef *sDate, uint32_t Format)
/*启动报警功能*/
HAL_StatusTypeDef HAL_RTC_SetAlarm(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)
/*设置报警中断*/
HAL_StatusTypeDef HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format)
/*报警时间回调函数*/
__weak void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
/*写入后备储存器*/
void HAL_RTCEx_BKUPWrite(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister, uint32_t Data)
/*读取后备储存器*/
uint32_t HAL_RTCEx_BKUPRead(RTC_HandleTypeDef *hrtc, uint32_t BackupRegister	

代码实战

接下来的代码会演示如何实时显示时间,设置rtc报警中断并启用中断,以及解决rtc掉电重启时间会重置的问题。
cubemx会自动帮我们生成初始化代码:

#include "rtc.h"
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;
/* RTC 初始化函数 */
void MX_RTC_Init(void)
{
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef DateToUpdate = {0};
  /** 初始化RTC */
  hrtc.Instance = RTC;
  hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
  hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN Check_RTC_BKUP */
 
  /* USER CODE END Check_RTC_BKUP */

  /** 设置RTC系统时间以及日期  */
  sTime.Hours = 19;
  sTime.Minutes = 29;
  sTime.Seconds = 0;

  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  DateToUpdate.WeekDay = RTC_WEEKDAY_SUNDAY;
  DateToUpdate.Month = RTC_MONTH_APRIL;
  DateToUpdate.Date = 4;
  DateToUpdate.Year = 20;

  if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
}

void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
  if(rtcHandle->Instance==RTC)
  {
  /* USER CODE BEGIN RTC_MspInit 0 */

  /* USER CODE END RTC_MspInit 0 */
    HAL_PWR_EnableBkUpAccess();
    /* Enable BKP CLK enable for backup registers */
    __HAL_RCC_BKP_CLK_ENABLE();
    /* RTC clock enable */
    __HAL_RCC_RTC_ENABLE();

    /* RTC interrupt Init */
    HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
  /* USER CODE BEGIN RTC_MspInit 1 */

  /* USER CODE END RTC_MspInit 1 */
  }
}

void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
  if(rtcHandle->Instance==RTC)
  {
  /* USER CODE BEGIN RTC_MspDeInit 0 */

  /* USER CODE END RTC_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_RTC_DISABLE();

    /* RTC interrupt Deinit */
    HAL_NVIC_DisableIRQ(RTC_IRQn);
    HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn);
  /* USER CODE BEGIN RTC_MspDeInit 1 */

  /* USER CODE END RTC_MspDeInit 1 */
  }
} 
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

我已经把原本的英文注释翻译成了中文注释,简单的来说上面这些代码就是完成了RTC设备的初始化以及设置系统时间,并且初始化了中断。

接下来完成通过串口实时显示时间的功能:
首先为了方便接下来的使用我们需要把下面这个结构体变量转换为全局变量

RTC_TimeTypeDef sTime = {0};
#include "rtc.h"
/* USER CODE BEGIN 0 */
RTC_TimeTypeDef sTime = {0};
/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;

然后点开我们的main函数,在while循环中读取当前时间,并且每秒通过串口传输到电脑上

#include "main.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f){
     uint8_t temp[1]={ch};
     HAL_UART_Transmit(&huart1,temp,1,2);
     return ch;
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
extern RTC_TimeTypeDef sTime;
  /* USER CODE END 1 */
  
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    printf("%02d:",sTime.Hours);
    printf("%02d:",sTime.Minutes);
    printf("%02d\n",sTime.Seconds);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

为了方便我们打印时间,我们改写了printf函数。

实现RTC报警中断功能:
首先在全局变量中定义结构体变量。

/* USER CODE BEGIN 0 */
RTC_TimeTypeDef sTime = {0};
RTC_AlarmTypeDef sAlarm ;
/* USER CODE END 0 */

让我们来看一下RTC_AlarmTypeDef这个结构体里面都有些什么?

在这里插入图片描述

在这里我们又看到了一个结构体名称,有没有觉得这个名称在哪见过?
没错!在最开始初始化RTC系统时间时我们有见到过它

在这里插入图片描述

继续查看这个结构体的内容发现:
在这里插入图片描述

原来设置报警的时间和设置系统的时间所用的数据结构是一样的。
搞清楚了报警结构体后设置报警中断就很容易了:

RTC_AlarmTypeDef sAlarm ;
void sAlarm_Config(int hours,int minutes,int seconds){
    /*填写报警结构体变量*/
  sAlarm.Alarm=RTC_ALARM_A; 
  sAlarm.AlarmTime.Hours=hours; 
  sAlarm.AlarmTime.Minutes=minutes;
  sAlarm.AlarmTime.Seconds=seconds;
  HAL_RTC_SetAlarm(&hrtc,&sAlarm, RTC_FORMAT_BIN);     //开启中断功能
  HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);  //设置中断
}

然后写一下回调函数:

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc){
   HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}

接下来去main函数中调用一下:

#include "main.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f){
     uint8_t temp[1]={ch};
     HAL_UART_Transmit(&huart1,temp,1,2);
     return ch;
}
void sAlarm_Config(int hours,int minutes,int seconds) //声明中断函数
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
extern RTC_TimeTypeDef sTime;
  /* USER CODE END 1 */
  
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
void sAlarm_Config(19,30,0); //在1分钟后发送中断
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    printf("%02d:",sTime.Hours);
    printf("%02d:",sTime.Minutes);
    printf("%02d\n",sTime.Seconds);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE 

当然,如果我们要完成的是在5小时候后发送中断我们可以把中断函数改写成这样:

void sAlarm_Config2(int hours,int minutes,int seconds){
  HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);   //获取设置中断时的时间
  sAlarm.Alarm=RTC_ALARM_A;
  sAlarm.AlarmTime.Hours=hours+sTime.Hours;
  sAlarm.AlarmTime.Minutes=minutes+sTime.Minutes;
  sAlarm.AlarmTime.Seconds=seconds+sTime.Seconds;
  HAL_RTC_SetAlarm(&hrtc,&sAlarm, RTC_FORMAT_BIN);
  HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);
}

在这里要注意一下,因为我们设置的是多少时间后进行中断,那么我们就要把当前时间加上多少我们要求的时间,所以要先获取一下当前时间是多少,如果不获取一下,结构体里面的变量就都是0。具体请看以下代码:

在这里插入图片描述

在这里插入图片描述

我们从HAL_RTC_GetTime()这个函数内容可以看出sTime结构体是通过RTC_ReadTimeCounter()这个函数读取了当前RTC设备下的RTC_CNT寄存器中的值。如果不读取的话,这个结构体里面的值就为0。
另外,我在看操作手册时看到了以下这段话:在这里插入图片描述

我最开始以为就是操作手册中的这个原因,但是我分析一下后发现,应该不是这个原因,因为在这里应该是结构体中的变量没有被赋值导致的数据错误,而不是硬件上的问题,虽然结果都是0。如果有人发现我的分析是错误的话,非常欢迎在下面评论中指出我的错误。

好了言归正传,接下来我们还要解决掉电后,日期被重置的问题。
首先在创建如下函数:

void user_CheckRtcBkup(){
   HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 1); 
}

然后在RTC初始化函数中加入这句:

/* USER CODE BEGIN Check_RTC_BKUP */
   if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1)==1){
       return;
   }
  /* USER CODE END Check_RTC_BKUP */

然后在main函数中调用一下:

int main(void)
{
  /* USER CODE BEGIN 1 */
extern RTC_TimeTypeDef sTime;
  /* USER CODE END 1 */
  
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  user_CheckRtcBkup();
  sAlarm_Config2(0,0,10);
  user_sendmessage();
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    printf("%02d:",sTime.Hours);
    printf("%02d:",sTime.Minutes);
    printf("%02d\n",sTime.Seconds);
    HAL_Delay(1000);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

这样我们就在初始化好RTC设备后,在后备寄存器中写入一个值1,这个后备寄存器在断电重启后值依旧不会被改变,所以在断电重启后再一次初始化时,RTC初始化函数中就不会把最开始设置的系统时间赋给sTime这个结构体。

*额外功能

当然我们在真实的开发中回调函数不大可能就单单只亮个灯,就比如我要在12小时后开启电磁阀,首先我要输出一个高电平(假设),然后在几秒钟后我们还要让电磁阀关闭,那我们相对的是不是还要输出一个低电平。以下代码完成了这部分的功能:

void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *nhrtc){
  if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==GPIO_PIN_SET){  
   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);  //翻转led灯的电平
   sAlarm.AlarmTime.Hours=sTime.Hours;
   sAlarm.AlarmTime.Minutes=sTime.Minutes;
   sAlarm.AlarmTime.Seconds=sTime.Seconds+5;  //设置报警时间为5秒后
   HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm,RTC_FORMAT_BIN);
  return;  //这个return很关键,在设置完中断后要马上退出回调函数,否则led灯会在下个if中再次翻转
  }
  if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)==GPIO_PIN_RESET){
  HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
  return;
  }
}

当然我这里还是用led灯来演示了,毕竟C语言入门hello world,单片机入门点亮led灯。具体思路就是首先检测电平,如果为高(我这块开发板低电平点亮led灯),就代表led灯是暗着的,那我们就翻转电平,并且设置中断时间为5秒后。设置完后直接退出当前回调函数,5秒后中断触发,这时电平由于是低电平那么我们就再次翻转一下,但是不再设置下一次中断。

飞思卡尔 I.MX6ULL 使用U-boot在屏幕上显示自定义图片

相关代码与修改飞思卡尔/恩智浦的开机logo相关,并且参考了原版显示开机logo的代码,首先请确保你的板子烧写的是飞思卡尔提供的u-boot,保证lcd驱动正常并且启动时能显示NXP字样

 

1,找到工程中logo图像的位置并替换

6UL一般是通过Makefile将git/tools/logos文件夹下的bmp图像文件链接进程序中,再由bmp_logo.c文件转换成数组形式再进行显示的,首先需要保证bmp文件是256色BMP格式,不然程序转换会导致图片显示出错。

如图,freescale.bmp就是我们要找的文件

这时候如果我们将我们想要的bmp文件重命名成freescale.bmp后再放入文件夹中进行替换之后,重新将u-boot编译并烧写至板子中即可看到NXP已经被修改了。

 

2,分析git/drivers/video/cfb_console.c文件

在此文件中我们看到了关于bmp_logo_data.h的相关引用

#ifdef	CONFIG_VIDEO_LOGO
#ifdef	CONFIG_VIDEO_BMP_LOGO
#include <bmp_logo.h>
#include <bmp_logo_data.h>
#define VIDEO_LOGO_WIDTH	BMP_LOGO_WIDTH
#define VIDEO_LOGO_HEIGHT	BMP_LOGO_HEIGHT
#define VIDEO_LOGO_LUT_OFFSET	BMP_LOGO_OFFSET
#define VIDEO_LOGO_COLORS	BMP_LOGO_COLORS

 

找到函数*video_logo

static void *video_logo(void)
{
  char info[128];
  int space, len;
  __maybe_unused int y_off = 0;
  __maybe_unused ulong addr;
  __maybe_unused char *s;

  splash_get_pos(&video_logo_xpos, &video_logo_ypos);

#ifdef CONFIG_SPLASH_SCREEN
  s = getenv("splashimage");
  if (s != NULL) {
    splash_screen_prepare();
    addr = simple_strtoul(s, NULL, 16);

    if (video_display_bitmap(addr,
          video_logo_xpos,
          video_logo_ypos) == 0) {
      video_logo_height = 0;
      return ((void *) (video_fb_address));
    }
  }
#endif /* CONFIG_SPLASH_SCREEN */

  logo_plot(video_fb_address, video_logo_xpos, video_logo_ypos);

#ifdef CONFIG_SPLASH_SCREEN_ALIGN
  /*
   * when using splashpos for video_logo, skip any info
   * output on video console if the logo is not at 0,0
   */
  if (video_logo_xpos || video_logo_ypos) {
    /*
     * video_logo_height is used in text and cursor offset
     * calculations. Since the console is below the logo,
     * we need to adjust the logo height
     */
    if (video_logo_ypos == BMP_ALIGN_CENTER)
      video_logo_height += max(0, (int)(VIDEO_VISIBLE_ROWS -
                 VIDEO_LOGO_HEIGHT) / 2);
    else if (video_logo_ypos > 0)
      video_logo_height += video_logo_ypos;

    return video_fb_address + video_logo_height * VIDEO_LINE_LEN;
  }
#endif
  if (board_cfb_skip())
    return 0;

  sprintf(info, " %s", version_string);

  space = (VIDEO_COLS - VIDEO_INFO_X) / VIDEO_FONT_WIDTH;
  len = strlen(info);

  if (len > space) {
    int xx = VIDEO_INFO_X, yy = VIDEO_INFO_Y;
    uchar *p = (uchar *) info;
    while (len) {
      if (len > space) {
        video_drawchars(xx, yy, p, space);
        len -= space;

        p = (uchar *) p + space;

        if (!y_off) {
          xx += VIDEO_FONT_WIDTH;
          space--;
        }
        yy += VIDEO_FONT_HEIGHT;

        y_off++;
      } else {
        video_drawchars(xx, yy, p, len);
        len = 0;
      }
    }
  } else
    video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info);

#ifdef CONFIG_CONSOLE_EXTRA_INFO
  {
    int i, n =
      ((video_logo_height -
        VIDEO_FONT_HEIGHT) / VIDEO_FONT_HEIGHT);

    for (i = 1; i < n; i++) {
      video_get_info_str(i, info);
      if (!*info)
        continue;

      len = strlen(info);
      if (len > space) {
        video_drawchars(VIDEO_INFO_X,
            VIDEO_INFO_Y +
            (i + y_off) *
              VIDEO_FONT_HEIGHT,
            (uchar *) info, space);
        y_off++;
        video_drawchars(VIDEO_INFO_X +
            VIDEO_FONT_WIDTH,
            VIDEO_INFO_Y +
              (i + y_off) *
              VIDEO_FONT_HEIGHT,
            (uchar *) info + space,
            len - space);
      } else {
        video_drawstring(VIDEO_INFO_X,
             VIDEO_INFO_Y +
             (i + y_off) *
              VIDEO_FONT_HEIGHT,
             (uchar *) info);
      }
    }
  }
#endif

  return (video_fb_address + video_logo_height * VIDEO_LINE_LEN);
}

 

此函数就是I.MX6ULL用于显示LOGO的函数,只需要更改此函数即可达到修改LOGO的目的,首先Makefile在自动生成logo_bmp相关的信息时会生成此LOGO的相关定义头文件bmp_logo.h,如下是默认logo文件生成的头文件

/*
 * Automatically generated by "tools/bmp_logo"
 *
 * DO NOT EDIT
 *
 */


#ifndef __BMP_LOGO_H__
#define __BMP_LOGO_H__

#define BMP_LOGO_WIDTH		364
#define BMP_LOGO_HEIGHT		128
#define BMP_LOGO_COLORS		240
#define BMP_LOGO_OFFSET		16

extern unsigned short bmp_logo_palette[];
extern unsigned char bmp_logo_bitmap[];

#endif /* __BMP_LOGO_H__ */

 

其中有包含logo长度与宽度的宏定义,我们可以利用这些宏定义来进行代码的编写。

接下来我们进行对*video_logo函数的修改,在函数的定义变量后加入以下的语句

char info[128];
int space, len;
__maybe_unused int y_off = 0;
__maybe_unused ulong addr;
__maybe_unused char *s;

if(video_logo_xpos == 0 && video_logo_ypos == 0)
       { 
          video_logo_xpos = (VIDEO_VISIBLE_COLS - BMP_LOGO_WIDTH ) >> 1; 
          video_logo_ypos = (VIDEO_VISIBLE_ROWS - BMP_LOGO_HEIGHT) >> 1; 
       }

将video_logo_xpos与video_logo_ypos分别设置为(屏幕长度-图片长度)的一半

//将数据左移1就是除2。

即将图片完整的显示在中间(如果直接使用屏幕的一半的话会超过中间显示,可以自己去试)

接下来将U-boot烧进板子,可以看到LOGO图片已经显示在中间了。

非常完美的LOGO!

3,修改宏定义CONFIG_SPLASH_SCREEN_ALIGN字段

以下是*video_logo函数中对于宏定义CONFIG_SPLASH_SCREEN_ALIGN汇编定义中的一段代码

#ifdef CONFIG_SPLASH_SCREEN_ALIGN
  /*
   * when using splashpos for video_logo, skip any info
   * output on video console if the logo is not at 0,0
   */
  if (video_logo_xpos || video_logo_ypos) {
    /*
     * video_logo_height is used in text and cursor offset
     * calculations. Since the console is below the logo,
     * we need to adjust the logo height
     */
    if (video_logo_ypos == BMP_ALIGN_CENTER)
      video_logo_height += max(0, (int)(VIDEO_VISIBLE_ROWS -
                 VIDEO_LOGO_HEIGHT) / 2);
    else if (video_logo_ypos > 0)
      video_logo_height += video_logo_ypos;

    return video_fb_address + video_logo_height * VIDEO_LINE_LEN;
  }
#endif

看着注释我们就知道了这段代码的作用就是如果logo的显示位置不在(0,0)点就不显示info消息,但是如果我们依旧需要显示一些需要的信息,就不能定义这个宏了。

取消这段代码很简单,随便修改宏定义或者直接改成0,还有就是在mu6ullevk.h中注释掉这段宏定义。

甚至可以直接删光

#define CONFIG_SYS_CONSOLE_IS_IN_ENV
#define CONFIG_SPLASH_SCREEN
//#define CONFIG_SPLASH_SCREEN_ALIGN
#define CONFIG_CMD_BMP
#define CONFIG_BMP_16BPP

我们知道默认在屏幕上输出的信息是宏定义U_BOOT_VERSION_STRING,这是此宏定义在/include/version.h中的定义

#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
  U_BOOT_TIME " " U_BOOT_TZ ")" CONFIG_IDENT_STRING

就是uboot相关的一些编译信息,但是我们有些时候想显示一点自定义字段怎么办。

4,修改video_drawstring函数

通过观察*video_logo函数可以找到如下语句

sprintf(info, " %s", version_string);
     ···
else
     video_drawstring(VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *) info);

可以看出先通过sprintf函数将version_string[]中的字段提取到info中,看一下version_string的定义。

const char __weak version_string[] = U_BOOT_VERSION_STRING;

很明显就是宏定义U_BOOT_VERSION_STRING,那么如果我们要修改打印的字段只需要修改else后面的video_drawstring就可以了。

将原来的注释掉,并且写上新的,三个参数分别代表(左上角为零点)(x轴距离,y轴距离,字符串指针),所以在写最后的字符串时先写字符串再强制类型转换为uchar *型就行了。

5,想使用自己的logo名,不想重命名

修改git/tools/下的Makefile在200行左右关于LOGO_BMP的定义

在第三个endif后加入以下定义

LOGO_BMP= $(srctree)/$(src)/logos/“你的文件名”.bmp

 

即可完成logo文件名的更换(一定要256色的bmp文件!),之后Makefile会将指定的bmp文件转换成bmp_logo.h&bmp_logo_data.h

重新编译烧写之后就会发现logo正下方出现自定义信息了

高级语言程序设计基础 (吉顺如版)课后习题(四至六章)答案

循环结构程序设计

高级语言程序设计基础一至三章

选择题

1.下述循环体语句的循环次数为(A)。

int x=-1;

do

 {  x=x*x; } while(!x);

 

A.1次          B.2次     C.无限次        D.有语法错误

 

2.在下列选项中,没有构成死循环的程序段是(C)。

A.int i=100;

while(1)

{  i=i%100+1;

if(i>100) break;

}

B.for(;;);

C.int k=1000;

do  {++k; } while(k>=10000);

D.int s=36;

while(s);–s;

 

3.下面程序输出结果是(B)。

#include<stdio.h>

int main(void)

{  int k=0; char c='A';

 do

     { switch( c++ )

       { case 'A': k++; break;

        case 'B': k--;

        case 'C': k+=2; break;

        case'D': k=k%2; continue;

        case 'E': k=k*10; break;

        default: k=k/3;

       }

k++;

}while( c<'G' );

  printf( "k=%d\n",k );

  return 0;

}

 

A.k=3          B.k=4        C.k=2         D.k=0

 

4.程序段“int num=0; while(num<=2)  printf(“%d,”,num++);”的运行结果是(C)。

A.0,1,     B.1,2,       C.0,1,2,      D.1,2,3,

 

5.下面程序段的运行结果为(A)。

 int a=1,b=2,c=2,t;

 while(a<b<c){t=a;a=b;b=t;c--;}

 printf("%d,%d,%d",a,b,c);

 

A.1,2,0        B.2,1,0       C.1,2,1       D.2,1,1

 

6.设有以下语句:

int x=3;

do

{

printf("%d\n",x-=2);

}

while(!(--x));

 

该程序段的执行结果为(B)。

A. 显示1        B. 显示1和-2

C. 显示0        D.是死循环

 

7.下面有关for循环的正确描述是(D)。

A.for循环只能用于循环次数已经确定的情况

B.for是先执行循环体语句,后判断表达式

C.在for循环中,不能用break语句跳出循环体

D.for循环的循环体语句中,可以包含多条语句,但必须用花括号括起来

 

8.执行下面的程序后,a的值为(B)。

#include<stdio.h>

int main(void)

{  int a,b;

   for(a=1,b=1;a<=100;a++)

     {  if(b>=20)  break;

         if(b%3==1)

           { b+=3;  continue; }

         b-=5;

     }

return 0;

}

 

A.7     B.8     C.9     D.10

 

9.以下叙述正确的是(D)。

A.不能使用do-while语句构成的循环

B.do-while语句构成的循环必须用break语句才能退出

C.do-while语句构成的循环,当while语句中的表达式值为非零时结束循环

D.do-while语句构成的循环,当while语句中的表达式值为零时结束循环

 

10.以下叙述正确的是(B)。

A.continue语句的作用是结束整个循环的执行

B.只能在循环体内和switch语句体内使用break语句

C.在循环体内使用break语句或continue语句的作用相同

D.从多层循环嵌套中退出,只能使用goto语句

 

填空题

1.break语句只能用于 循环 语句和 switch  语句中。

2.下列for循环语句执行的次数是 4 

for(x=0,y=0;(y=123)&&(x<4);x++);

3.当运行以下程序时,从键盘键入right?,则下面程序运行结果是sjhiu 

#include<stdio.h>

int main(void)

  {

    char c;

    while((c=getchar())!='?')

          putchar(++c);

    return 0;

  }

 

4.下列程序的运行结果是 x=1,y=20 

#include<stdio.h>

int main(void)

  {

    int i,x,y;

    i=x=y=0;

    do{  ++i;

           if(i%2!=0)

            {  x=x+i;

               i++;

            }

          y=y+i++;

        }while(i<=7);

   printf("x=%d,y=%d\n",x,y);

   return 0;

 }

 

5.执行下列程序段后的输出是 02 

x=0;

 while(x<3)

 for(;x<4;x++)

   {

     printf("%d",x++);

     if( x<3) continue;

     else  break;

     printf("%d",x);

   }

 

6.设定义“int k=1,n=163;”,执行下面程序段后,k的值是 18 

do

 {

   k*=n%10;

   n/=10;

}while(n);

 

7.以下程序的运行结果是 1 

#include <stdio.h>

int main(void)

{   int y=10;

    do  { y--;} while (--y);

    printf("%d\n",++y);

 }

 

8.以下程序的运行结果是 s=7 

#include <stdio.h>

int main(void)

 {

   int s=0,k;

   for(k=7;k>=0;k--)

     {  switch(k)

         {  case 1: case 4:  

            case 7: s++; break;

            case 2:  case 3: case 6: break;

            case 0:  case 5: s+=2; break;

          }

     }

   printf( “s=%d \n”,s );

   return 0;

  }

 

9.下列程序的功能为:将从键盘输入的一组字符统计出大写字母的个数m和小写字母的个数 n,并输出m、n中的较大数。

#include<stdio.h>

int main(void)

{  int m=0,n=0;

   char c;

   while((  c=getchar() )!='\n')

    {   if(c>='A'&&c<='Z') m++;

        if(c>='a'&&c<='z') n++;

    }

   printf("%d\n",m<n?  n:m  );

  return 0;

 }

 

10.下列程序的功能为:求1~100以内所有能被13整除的数的累加和,当累加和超出100时停止累加。请填空。

#include <stdio.h> 

int main(void)

{  int i, sum = 0;

   for ( i=1; i<100; i++ )

    {   if (   i%13==0   )

               sum += i;

         if ( sum > 100 )  break ;

        }

   printf( “i=%d, sum=%d\n”, i,sum );

   return 0;

 }

 

编程题

(1)从键盘上输入若干字符,以按Enter键结束,统计其中字符’A’或’a’的个数。

源程序:



#include <stdio.h>

int main(void)

{

  char ch;

  int cnt=0;

  while((ch=getchar())!='\n')

   {

       if(ch=='A' || ch=='a')

       cnt=cnt+1;

   }      

  printf("cnt=%d\n",cnt);

  return 0;

}



运行结果:



wabcAAabc

cnt=4

-----------

(2)利用的前100项之积计算pi的值。

#include <stdio.h>

int main(void)

{

  double term, result = 1;     /*累乘项初值应为1*/

  int n;

  for (n=2; n<=100; n = n + 2)

   {

        term=(double)( n * n)/((n-1)*(n+1));  /*计算累乘项*/

        result = result * term;

   }

  printf("result = %f\n", 2*result);

  return 0;

}

 

运行结果:

result = 3.126079


(3)用1元5角钱人民币兑换5分、2分和1分的硬币(每一种都要有)共100枚,问共有几种兑换方案?每种方案各换多少枚?

#include <stdio.h>

int main(void)

{

  int five,two,one,count=0;

  for(five=1;five<=29;five++)

    for(two=1;two<=73;two++)

    {

      one=100-five-two;

      if(5*five+2*two+one==150)

      {

         count++;

         printf("five=%d,two=%d,one=%d\n",five,two,one);

      }

    }

  printf("count=%d\n",count);

  return 0;

}

 

运行结果:



five=1,two=46,one=53

five=2,two=42,one=56

five=3,two=38,one=59

five=4,two=34,one=62

five=5,two=30,one=65

five=6,two=26,one=68

five=7,two=22,one=71

five=8,two=18,one=74

five=9,two=14,one=77

five=10,two=10,one=80

five=11,two=6,one=83

five=12,two=2,one=86

count=12

----------------------

 

(4)鸡兔同笼,共有98个头,386只脚,编程求鸡、兔各多少只。

#include <stdio.h>

int main(void)

{

  int x, y;

  for (x=1;x<=97;x++)

   {

        y=98-x;

        if(2*x+4*y==386)

       printf("鸡=%d,兔=%d",x,y);

   }

  return 0;

}

运行结果:

鸡=3,兔=95


 

(5)将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。

#include <stdio.h>

int main(void)

{

  int n,i;

  printf("please input a number:");

  scanf("%d",&n);

  printf("%d=",n);

  for(i=2;i<=n;i++)

    while(n!=i)

     {

       if(n%i==0)

         {

           printf("%d*",i);

           n=n/i;

         }

      else  break;

       }

   printf("%d",n);

   return 0;

}

运行结果:



please input a number:80

80=2*2*2*2*5

------------------------

(6)从键盘任意输入一个4位数x,编程计算x的每一位数字相加之和。例如,输入x为1234,则由1234分离出其个位4、十位3、百位2、千位1,然后计算4+3+2+1=10并输出。

#include <stdio.h>

int main(void)

{

  int sum=0,x,i,y;

  printf("Input data:");

  scanf("%d",&x);

  if(x>=1000&&x<=9999)

  {

      for(i=1;i<4;i++)

     {

      y=x%10;

      sum+=y;

      if(y!=0)

        printf("%d+",y);

      x=x/10;

     }

     y=x%10;

    printf("%d=%d\n",y,sum+y);

  }

  else printf("data error!");

  return 0;

}
运行结果:

 

(1)

Input data:9876

6+7+8+9=30

 

(2)

Input data:1002

2+1=3

---------------

 

(7)打印出所有“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个水仙花数

#include <stdio.h>

int main(void)

{

  int i,j,k,n;

  printf("narcissus numbers are:\n");

  for (n=100;n<1000;n++)

   {

    i=n/100;

    j=n/10%10;

    k=n%10;

    if (n==i*i*i+j*j*j+k*k*k)

      printf("%d\n",n);

   }

  return 0;

}
narcissus numbers are:

153

370

371

407

----------------------

 

(8)利用泰勒级数 计算的值,要求最后一项的绝对值小于10^-5,并统计出此时累加了多少项(x由键盘输入)。注意键盘输入值为弧度。

#include <stdio.h>

#include  <math.h>

int main(void)

{

  int n=1,count=1;

  float x;

  double sum,term;     

  printf("Input x: "); //输入值为弧度

  scanf("%f", &x);

  sum=x;

  term=x;            

  do

       {

         term=-term*x*x/((n+1)*(n+2));

         sum=sum+term;      

         n=n+2;

         count++;

       }while(fabs(term)>=1e-5);

  printf("sin(x)=%.1f,count=%d\n",sum,count);

  return 0;

}
运行结果:



Input x: 0.523598

sin(x)=0.5,count=4

-------------------

(9)编写一个猜数游戏:任意设置一个整数,请用户从键盘上输入数据猜想设置的数是什么,告诉用户是猜大了还是小了。10次以内猜对,用户获胜;否则,告诉用户设置的数据是什么。

#include <stdio.h>

int main(void)

{

  int num=123,x,n;

  printf("hint:0<number<1000\n");

  for(n=1;n<=10;n++)

  {

   printf("guess:");

   scanf("%d",&x);

   if(x==num) 

{

printf("Right!\n");

break;

}

   if(x>num)  printf("bigger!\n");

   if(x<num)  printf("smaller!\n");

  }

  if(n==11) 

    printf("END! The number is %d\n",num);

  return 0;

}

运行结果:



hint:0<number<1000

guess:0

smaller!

guess:200

bigger!

guess:100

smaller!

guess:150

bigger!

guess:125

bigger!

guess:112

smaller!

guess:118

smaller!

guess:121

smaller!

guess:123

Right!

---------------------

(10)编程输出以下图案。

#include <stdio.h>

int main(void)

{

  int i,j;

  for(i=1;i<=4;i++)

  {

    for(j=10-i;j>=1;j--)

      printf("%c",' ');

    for(j=0;j<2*i-1;j++)    

      printf("*");

    printf("\n");

  }

  for(i=3;i>=1;i--)

  {

    for(j=10-i;j>=1;j--)    

      printf("%c",' ');

    for(j=0;j<2*i-1;j++)        

      printf("*");

    printf("\n");

  }

 return 0;

}


 

章节测验

【单选题】
以下说法正确的是(  A  )
  • A、用do-while语句构成的循环,在while后的表达式为0时结束循环
  • B、do-while语句构成的循环不能用其他语句构成的循环来代替
  • C、do-while语句构成的循环只能用break语句退出
  • D、用do-while语句构成的循环,在while后的表达式为非0时结束循环

 

【单选题】以下程序运行时从键盘输入3.6,2.4,输出结果是(A  )。

#include<math.h>

void main()

{ 

 float x,y,z;

    scanf("%f,%f",&x,&y);

    z=x/y;

    while(1)

    {  if(fabs(z)>1.0)

      { 

 x=y;

 y=x;

 z=x/y;

 }

     else  break;

   }

   printf("%f",y);

}
  • A、2.4
  • B、1.5
  • C、1.6
  • D、2

如果c是大于1的正整数,与以下程序段功能相等的赋值语句是( D )。

s=a;

for(b=1;b<=c;b++)

   s=s+1;

 

  • A、s=b+c;
  • B、s=s+c;
  • C、s=a+b;
  • D、s=a+c;

有一条长阶梯,若每步跨2阶,则最后剩余1阶,若每步跨3阶,则最后剩余2阶,若每步跨5阶,则最后剩余4阶,若每步跨6阶,则最后剩余5阶,若每步跨7阶,最后才正好一阶不剩。请问,这条阶梯共有多少阶。

请补充while语句后的判断条件。(C)

 

  #include<stdio.h>

         void main()

{ 

int i=1;

    while(         )

        ++i;

    printf("阶梯共有%d阶.\n",i);

}

 

  • A、!((i%2==1)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==1))
  • B、!((i%2==0)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==0))
  • C、!((i%2==1)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==0))
  • D、(i%2==1)&&(i%3==2)&&(i%5==4)&&(i%6==5)&&(i%7==0)

【单选题】以下程序段的输出结果是(  C )。

#include<stdio.h>

void main()

{

  int a=3;

  do

  {

  printf("%d",a--);

  }while(!a);

}

 

  • A、32
  • B、不输出任何内容
  • C、3
  • D、321

【单选题】若定义int i; 则以下for语句的执行结果是( B   )。

 for(i=1;i<10;i++)

 { 

     if(i%3) i++;

     ++i;

     printf("%d",i);

 }

 

  • A、35811
  • B、369
  • C、258
  • D、2468

【单选题】以下程序段的输出结果是(    )。

  int n=10;

  while(n>7)

  { 

 printf("%d,",n);

      n--;

   }

 

  • A、9,8,7,
  • B、9,8,7,6,
  • C、10,9,8,
  • D、10,9,8,7,

【单选题】以下程序的输出结果是(  A  )。

#include<stdio.h>

void main()

{ 

int i=5;

  for(;i<15;)

  { 

 i++;

    if(i%4==0)    printf("%2d",i);

    else          continue;

  }

}

 

  • A、812
  • B、81216
  • C、1216
  • D、8
【单选题】
以下关于for语句的说法不正确的是(  B  )
  • A、for循环是先判断表达式,后执行循环体语句
  • B、for循环只能用于循环次数已经确定的情况
  • C、for循环中,可以用break语句跳出循环体
  • D、for循环体语句中,可以包含多条语句,但要用花括号括起来。
【单选题】
C语言中,while语句和do-while语句的主要区别是(  B  )
  • A、do-while的循环体不能是复合语句
  • B、do-while的循环体至少无条件执行一次
  • C、while的循环控制条件比do-while的循环控制条件严格
  • D、do-while允许从外部转到循环体内

【单选题】若定义int i;则以下循环语句的循环执行次数是(  C  )。

for(i=2;i==0;)

printf(“%d”,i–);

  • A、1
  • B、2
  • C、0
  • D、无限次

语句while(!y)中的!y等价于y!=0。(X)

下面程序段循环一次也不执行。(√)

int x=10;

while(x=0)

x=x-1;

for语句中的表达式可以部分或全部省略,但是两个分号不能省略。(

用于直接中断最内层循环的语句是break。(√)

以下程序的输出结果是n=4(√)

#include<stdio.h>

void main()

{

int i,n=0;

  for(i=2;i<5;i++)

  {

do {

  if(i%3) continue;

         n++;

      } while(!i);

      n++;

   } printf("n=%d",n);

}

 

【简答题】

图形打印。

题目描述

题目内容:编写程序,打印以下图形(行n的值由键盘输入)。

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN

int main()



{

  volatile int i, j, n;

  char a = ' ', b = '*';

  scanf("%d", &n);

  for (i = 0; i<n; i++)

  {

    for (int j = i; j; j--)



    {

      printf("%c", a);



    }

    for (j = n; j>i; j--) {

      printf("%c", b);



    }

    printf("\n");

  }

  return 0;



}

 

【简答题】

特征四位数。

题目描述

题目内容:已知四位数3025具有一个特殊性质:它的前两位数字30与后两位数字25之和是55,而55的平方正好等于3025。编程找出所有具有这种性质的四位数。

 

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN

int main()



{

  volatile int n, x, y;

  for (n = 1000; n <= 9999; n++)

  {

    x = n % 100;

    y = n / 100;

    if (n == (x + y)*(x + y))

    {

      printf("%d\n", n);



    }

  }

  return 0;



}

 

【简答题】

求和S。

题目描述

题目内容:求s=1+(1+2)+…+(1+2+3+…+n)的值,其中n由键盘输入。

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN

int main()



{

  volatile int i, n, s = 0;

  scanf("%d", &n);

  if (n <= 0)



  {

    printf("Error!\n");

  }

  else {



    int a = 0;

    for (i = 1; i <= n; i++)

    {

      a = a + i;

      s = s + a;

    }

    printf("%d\n", s);

  }

  return 0;





}

【简答题】

追查车号。

题目描述

题目内容:一辆卡车违反交通规则,撞人逃逸。现场三人目击事件,但都没有记住车号,只记下车的一些特征。甲说:牌照的前两位数字是相同的;乙说:牌照的后两位数字是相同的;丙是位数学家,他说:四位的车号正好是一个整数的平方。请根据以上线索求出车号。

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN

int main()



{

  volatile int a, b;

  int x;

  for (a = 1; a<10; a++)

    for (b = 1; b<10; b++) {



      x = sqrt(a * 1000 + a * 100 + b * 10 + b);

      if (x*x == (a * 1000 + a * 100 + b * 10 + b))

      {

        printf("The number is %d%d%d%d\n", a, a, b, b);

      }

    }

  return 0;



}

 


数组

选择题

1.已定义“int i; char x[10];”,为了给x数组赋值,以下正确的语句是(D)。

A.x[10]= “Hello! “;

B.x=”Hello! “;

C.x[]=”Hello! “;

D.for(i=0; i<6; i++)  x[i]=getchar();

 

2.若有以下的数组定义:“char a[ ]=”abcd”; char b[]={‘a’,’b’,’c’,’d’,’e’};”,则以下正确的描述是(A)。

A.a数组和b数组长度相同

B.a数组长度大于b数组长度

C.a数组长度小于b数组长度

D.两个数组中存放相同的内容

 

3.若有定义“int i; int x[3][3]={2,3,4,5,6,7,8,9,10};”,则执行语句

“for(i=0;i<3;i++)  printf(“%4d”,x[i][2-i]);”的输出结果是(D)。

A.2 5  8            B.2 6  10

C.4 7  10         D.4 6  8

 

4.下列对二维数组a进行正确初始化的是(B)。

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

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

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

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

 

5.下列说法正确的是(D)。

A.数组的下标可以是float类型

B.数组的元素的类型可以不同

C.初始化列表中初始值的个数多于数组元素的个数也是可以的

D.区分数组的各个元素的方法是通过下标

 

6.若有定义“char str1[30],str2[30];”,则输出较大字符串的正确语句是(C)。

A.if(strcmp(strl,str2))  printf(“%s”,strl);

B.if(strl>str2)  printf(“%s”,strl);

C.if(strcmp(strl,str2)>0)  printf(“%s”,strl);

D.if( strcmp(strl)>strcmp(str2)) printf(“%s”,strl);

 

7.下列程序段的输出结果是(B)。

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

int i,s=0;

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

s+=aa[i][1];

printf("%d\n",s);

 

A. 11  B. 19    C. 13    D. 20

 

8.下列程序段的输出结果是(D)。

charstr[15]= "hello!";

printf("%d\n",strlen(str));

 

A.15   B.14    C.7    D.6

 

9.有以下程序段,当输入为happy!时,程序运行后输出结果是(B)。

charstr[14]={"I am "};

strcat(str,"sad!");

scanf("%s",str);

printf("%s",str);

 

A.I am sad!         B.happy!

C.I am happy!    D.happy!sad!

 

10.下列关于数组的描述中错误的是(D)。

A.一个数组只允许存储同种类型的数据

B.数组名是数组在内存中的首地址

C.数组必须先定义,后使用

D.如果在对数组进行初始化时,给定的数据元素个数比数组元素少,多余的数组元素自动初始化为最后一个给定元素的值

 

填空题

1.有定义语句“int i=3,x[4]={1,2,3};”,则数组元素x[i]的值是  0  

2.有定义语句“char a[ ]={ “I am a student”};”,该字符串的长度是  14  ,a[3]=  m  

3.有二维数组定义“k[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};”,则其中元素k[2][1]的值是 10 ,k[1][2] 的值是  7  

4.若有定义“int a[4][4];”,则a数组中行下标的下限为  0  ,列下标的上限为   3   

5.若有定义“char a[]=”abcdef”;”,则执行语句

“printf(“%d,%d\n”,sizeof(a),strlen(a));”后的输出结果是  7,6   

6.下列程序段的运行结果是  4  

char str[20]="This is my book";

str[4]='\0'; str[9]= '\0';

printf(" %d",strlen (str));

 

7.下列程序的运行结果是  Tony  

char name[3][20]={"Tony", "Join", "Mary"};

int m=0,k;

for(k=1;k<=2;k++)

if(strcmp (name[k],name[m])>0)

 m=k;

puts(name[m]);

 

8.下列程序运行时输入:20 30 5 85 40,运行结果为:

max=85

min=5

sum=180

aver=30.00 

#include<stdio.h>

#define N 5

int main(void)

{

int a[N],max,min,sum,i;

 for (i=0;i<N;i++)

        scanf("%d",&a[i]);

sum=max=min=a[0];

for (i=1;i<N;i++)

       {

  sum+=a[i];

         if (a[i]>max)  max=a[i];

         if (a[i]<min)  min=a[i];

    }

printf("max=%d\nmin=%d\nsum=%d\naver=%4.2f\n",max,min,sum,(float)(sum-max-min)/(N-2));

 return 0;

}

 

9. 下列程序运行时输入:This_is_a_C_Program! 运行结果为:

   Ti_saCPorm

Tss_Pgm   

#include<stdio.h>

#include<string.h>

int main(void)

{

char str[81], a[81], b[81];

 int n, i, j=0, k=0;

 gets( str );

 n=strlen( str );

for ( i=0; i<n; i++ )

{

       if ( i%2==0 )  a[j++] = str[i];

       if ( i%3==0 )   b[k++] = str[i];

}

a[j] = b[k] = '\0';

puts( a );

 puts( b );

 return 0;

}

 

10.下列程序的运行结果为:  1 1 1 5  

#include<stdio.h>

int main(void)

{

  char s[]="12345678";

  int c[4]={0},k,i;

  for (k=0; s[k]; k++)

    {

        switch (s[k])

       {

           case'1': i=0; break;

            case'2': i=1; break;

         case'3': i=2; break;

            case'4': i=3;

         }

        c[i]++;

    }

  for(k=0;k<4;k++)

        printf("%d ",c[k]);

  return 0;

}

 

 

编程题

 

(1)求最小数

从键盘输入15个整数,存放在数组中,找出其中最小数并指出其所在的位置。

#include<stdio.h>

#define N 15

int main(void)

{

  int min,i,j=0,a[N];

printf("Enter %d integers:\n",N);

for(i=0;i<N;i++)

     scanf("%d",&a[i]);

min=a[0];

  for(i=1;i<N;i++)

    if(min>a[i])

     {

  min=a[i];

        j=i;

     }

  printf("min=%d,loc=%d",min,j);

  return 0;

}

运行结果:



Enter 15 integers:

6 5 4 3 0 5 4 33 23 -9 6 5 1 2 8

min=-9,loc=9

-----------------------------

 

(2)进制转换

将输入的十进制正整数化为十六进制数。

#include <stdio.h>

int main(void)

{

int i,x,j,a[20];

while(1)

  { 

    scanf("%d",&x);

    if(x<0)

      printf("Data error,Reenter:\n");

    else break;

  }

i=0;

while(x)

  {

a[i]=x%16;

   x=x/16;

i++;

  }

  for(j=i-1;j>=0;j--)

  if(a[j]<=9)

         printf("%d",a[j]);

  else if(a[j]<=15)

         printf("%c",a[j]+'A'-10);

return 0;

}
运行结果:



123

7B

---------

 

(3)统计单词数

从键盘输入一行字符,统计其中有多少单词,假设单词之间以逗号分隔。

#include <stdio.h>

int main(void)

{

  char string[81];

  int i,num=0,word=0;

  char c;

  gets(string);

  for (i=0;(c=string[i])!='\0';i++)

     if(c==',') word=0;

     else if(word==0)

           {  word=1;

              num++;

           }

  printf("There are %d words in the line.\n",num);

  return 0;

}
运行结果:



I,love,Shanghai!

There are 3 words in the line.

--------------------------

 

(4)字符排序

从键盘输入一字符串,放在字符数组a中,将字符数组a中下标值为偶数的元素按从小到大排序。例如:原始字符串为zabkam,则排序后字符串为aabkzm。

#include<stdio.h>

#include<string.h>

int main(void)

{

  char a[80],t,m;

  int i,j,k;

  printf("Input String:\n");

  gets(a);

  k=strlen(a);

  //采用选择排序算法

  for(i=0;i<=k-2;i+=2)

   {

     m=i;

     for(j=i+2;j<k; j+=2  )

       if(a[m]>a[j])

         m=j;

     if(m!=i)

       {

 t=a[i];

 a[i]=a[m];

a[m]=t;

 }

  }

  puts(a);

  printf("\n");

  return 0;

}
运行结果:



Input String:

zabkam

aabkzm

----------

 

(5)扬灰三角形

编写程序输出以下杨辉三角形(要求输出10行)。

1      

1    1

1    2    1

1    3    3    1

1    4    6    4    1

1    5    10  10   5    1

…    …  …   …   …   …
#include<stdio.h>

int main(void)

{

int i,j;

  int a[10][10];

  for(i=0;i<10;i++)

{

 a[i][0]=1;

 a[i][i]=1; 

    }

for(i=2;i<10;i++)

for(j=1;j<i;j++)

a[i][j]=a[i-1][j-1]+a[i-1][j];

for(i=0;i<10;i++)

{   for( j=0;j<=i;j++)

printf("%4d",a[i][j]);

printf("\n");

}

   return 0;

 }
运行结果:



1

1   1

1   2   1

1   3   3   1

1   4   6   4   1

1   5  10  10   5   1

1   6  15  20  15   6   1

1   7  21  35  35  21   7   1

1   8  28  56  70  56  28   8   1

1   9  36  84 126 126  84  36   9   1

------------------------------

 

(6)字符串逆序连接

编程将s数组中的字符串的正序和反序进行连接,形成一个新串放在t数组中。例如,当s数组中字符串为”ABCD” 时,则t数组中的内容应为:”ABCDDCBA”。

#include<stdio.h>

#include<string.h>

int main(void)

{
//ZZYMMM

char s[100], t[100];

int i,d;

printf("Enter string:");

scanf("%s",s);

d=strlen(s);

for(i=0;i<d;i++) 

  t[i]=s[i];

for(i=0;i<d;i++)

  t[d+i]=s[d-1-i];

t[2*d]='\0';

printf("The result is: %s\n",t);

return 0;

}
运行结果:



Enter string:ABCD

The result is: ABCDDCBA

----------------------

(7)数据加密 歪比外币

某公司在传输数据过程中为了安全要对数据进行加密,若传递的是四位的整数,对其进行加密的规则为:每位数字都加上5,然后用和除以10的余数代替该数字,再将第一位和第四位交换,第二位和第三位交换。如:输入数字7659,则加密后的数字为4012。

#include<stdio.h>

int main(void)

{

  int x,i,aa[4],t;

printf("输入一个四位整数:");

scanf("%d",&x);

aa[0]=x%10;

aa[1]=x%100/10;

aa[2]=x%1000/100;

aa[3]=x/1000;

 for(i=0;i<=3;i++)

 {

   aa[i]+=5;

   aa[i]%=10;

 }

for(i=0;i<=3/2;i++)

  {

   t=aa[i];

   aa[i]=aa[3-i];

   aa[3-i]=t;

  }

 printf("加密后的数字为:");

 for(i=3;i>=0;i--)

    printf("%d",aa[i]);

 printf("\n");

 return 0;

}
运行结果:



输入一个四位整数:7659

加密后的数字为:4012

----------------------

(8)查找数值位置

编写程序查找数值18在以下二维数组中第一次出现的位置。

#include <stdio.h>

int main(void)

{

  int i,j,a[3][4]={{3,4,5,18}, {8,12,16,54}, {43,34,18,7}};

  for(i=0;i<3;i++)

   { 

      for(j=0;j<4;j++)

         if(a[i][j]==18) break;

     if(j<4)break;

  }

  printf("数值18第一次出现的位置在%d行,第%d列\n",i+1,j+1);

  return 0;

}
运行结果:



数值18第一次出现的位置在1行,第4列

----------------------------------

 

(9)求二维数组元素的和平均值及个数统计

设有4行4列的数组a,其元素a[i][j]=3*i+2*j-6。编写程序,实现如下功能:(1) 求第二行4元素的累加和;(2) 求第四列4元素的平均值;(3) 求主对角线4元素中负数的个数。

#include <stdio.h>

int main(void)

{ 

   int a[4][4],i,j,s=0,m=0;

float n=0;

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

     for(j=0;j<4;j++)                                        

        a[i][j]=3*i+2*j-6;                                  

  printf("原始数组为:\n");

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

 {  

    for(j=0;j<4;j++)

 printf("%3d",a[i][j]);

printf("\n");

}

for(j=0;j<4;j++)

 s+=a[1][j];

printf("第二行4元素累加和为:%d\n",s);

for(j=0;j<4;j++)

n+=a[j][3];

printf("第四列4元素平均值为:%.2f\n",n/4.0);

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

if(a[i][i]<0)

m++;

printf("主对角线4元素中负数的个数为:%d\n",m);

return 0;

}
运行结果:



原始数组为:

-6 -4 -2  0

-3 -1  1  3

 0  2  4  6

 3  5  7  9

第二行4元素累加和为:0

第四列4元素平均值为:4.50

主对角线4元素中负数的个数为:2

---------------------------

(10)约瑟夫环问题

编号为1,2,3,…,n的n个人按顺时针方向围坐一圈,每人持有一个正整数密码。一开始任选一个正整数m作为报数上限值,从第一个人开始按顺时针报数,报到m时停止,报m的人出列,将他的密码作为新的m值,从他在顺时针方向的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。设计程序求出出列顺序。

#include <stdio.h>

int main(void)

{

  int a[100];  /* 数组a中保存n个人的密码*/

  int i,j,m,n,k=0;

  printf("输入人数n和报数上限m:");

  scanf("%d%d",&n,&m);

  printf("输入%d个人的密码: ",n);

  for(i=0;i<n;i++)

     scanf("%d",&a[i]);

  printf("\n出列顺序为: ");

  for(i=0;i<n;i++)

    {

       j=1;

      while(j<m)

       { 

         while(a[k]==0)  /*跳过已出列的人*/

             k=(k+1)%n;

          j++;

         k=(k+1)%n;

      }

     while(a[k]==0)  /*跳过已出列的人*/

          k=(k+1)%n;

     printf("%d ",k+1);

     m=a[k];

     a[k]=0;

   }

return 0;

}
运行结果: 
输入人数n和报数上限m:5 6 
输入5个人的密码: 3 2 1 4 5 
出列顺序为: 1 4 5 2 3 
------------------------

 

章节测验

【单选题】若定义以下变量和数组:   (A)

int i;

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

则下列语句的输出结果是(    )。

for(i=0;i<3;i++)

printf("%d  ",a[i][2-i]);

 

 
  • A、3 5 7
  • B、1 5 9
  • C、3 6 9
  • D、1 4 7

【单选题】以下程序执行后的输出结果是(  B  )。

    #include<stdio.h>

    void main()

   {

    int i,j,s=0;

    int a[4][4]={1,2,3,4,0,2,4,6,3,6,9,12,3,2,1,0};

    for(j=0;j<4;j++)

   {

      i=j;

      if(i>2) i=3-j;

      s+=a[i][j];

   }

   printf("%d\n",s);

   }

 

  • A、18
  • B、16
  • C、12
  • D、11
【单选题】
设有定义语句int a[2][4];则以下叙述不正确的是(  D  )。
  • A、元素a[0]是由4个整型元素组成的一维数组
  • B、a[0]代表一个地址常量
  • C、a数组可以看成是由a[0]、a[1]两个元素组成的一维数组
  • D、可以用a[0]=5;的形式给数组元素赋值
【单选题】
假设a、b均为字符数组,则以下正确的输入语句是(  D  )。
  • A、gets(“a”);gets(“b”);
  • B、gets(a,b);
  • C、scanf(“%s %s”,&a,&b);
  • D、scanf(“%s %s”,a,b);
【单选题】
下列描述正确的是(  C  )。
  • A、两个字符串所包含的字符个数相同时,才能比较字符串
  • B、字符个数多的字符串比字符个数少的字符串大
  • C、字符串”That”小于字符串”The”
  • D、字符串 “STOP   ” 与 “STOP” 相等
【单选题】
以下能对二维数组a进行正确初始化的是(  A  )。
  • A、int a[][3]={{1,2,3},{4,5,6}};
  • B、int a[2][4]={1,2,3},{{4,5},{6}};
  • C、int a[2][]={{1,0,1},{5,2,3}};
  • D、int a[][3]={{1,0,1},{},{1,1}};
【单选题】
以下合法的数组定义是(  B  )。
  • A、int a[]=”Language”;
  • B、char a[]=”C Program Language.”;
  • C、char a=”C Program”;
  • D、int a[5]={0,1,2,3,4,5};
【单选题】
在C语言中引用数组元素时,其数组下标的数据类型允许是(   B )。
  • A、整型常量
  • B、整型常量或整型表达式
  • C、整型表达式
  • D、任何类型的表达式

【单选题】以下程序的输出结果是(  A  )。

#include<stdio.h>

#include<string.h>

void main()

{ 

char str[10]="Ch\nina";

printf("%d",strlen(str));

}

 

  • A、6
  • B、5
  • C、7
  • D、10

【单选题】已知int a[3][2]={3,2,1};则表达式“a[0][0]/a[0][1]/a[1][0]”的值是(  C  )。

  • A、0.16667
  • B、错误的表达式
  • C、1
  • D、0

调用strlen(“abcd\0ed\0g”)的返回值为4。(√)

当接受用户输入的含空格的字符串时,应使用getc函数。(X)

数组名实际是该数组首元素的起始地址。(√)
一个字符数组就是一个字符串。(X)
指向数组元素的指针只能指向数组的首元素。(X)
你知道什么是指针数组吗?

函数

选择题

1.以下关于函数的说法中正确的是(A)。

A.调用函数时,只能把实参的值传送给形参,形参的值不能传送给实参

B.一个函数可以定义在其他函数中

C.函数必须有返回值和参数

D.一个函数只能调用定义在其之前的函数

2.对于声明为int fun();的函数,以下不能作为返回值的语句是(B)。

A.return ‘A’;   B.return 3.14;    C.return0;      D.return (int)(3.14);

3.对于声明为void func(char ch, double x);的函数,以下能调用该函数的语句是(C)。

A.func(“abc”, 3.0);

B.func(’65’,10.5);

C.func(‘A’, 10.5);

D.int t = func(‘a’, 65);

4. 已知调用函数的语句f(f1(al,a2),(a3,a4),a5=x+y);,则该语句中f()的参数个数为(C)。

A.l      B.2      C.3     D.5

5.用数组名作为函数调用时的实参,则传递给形参的是(A)。

A.数组首地址

B.数组的第一个元素值

C.数组中全部元素的值

D.数组元素的个数

6.以下关于函数声明的说法正确的是(B)。

A.函数必须先声明再定义才可以调用

B.如果要在定义位置之前使用函数,则必须先声明函数

C.函数声明必须和函数定义中的首部完全一致

D.函数声明可以省略参数类型说明符

7.关于函数的递归调用,以下说法错误的是(D)。

A.递归可以分为直接递归和间接递归

B.递归函数中一定有递归出口

C.通常使用选择结构设置结束递归或继续递归的条件

D.递归函数的效率比功能相同的非递归函数更高

8.以下说法中不正确的是(D)。

A.在不同的函数中可以定义相同名字的变量

B.形式参数是函数内的局部变量

C.在函数内定义的变量只在该函数范围内可访问

D.在函数内的复合语句中定义的变量在整个函数范围内都可访问

9.全局变量的作用域为(D)。

A.定义该变量的整个源文件

B.离变量定义位置最近的函数

C.所有函数

D.变量定义的位置开始到文件结束

10.关于静态存储的变量,以下说法正确的是(C)。

A.全局变量不是静态变量

B.局部变量默认不是静态变量

C.静态变量只初始化一次

D.局部静态变量可以在任何位置访问

填空题

1.程序执行的入口点、不能被其他函数调用的是  main()  函数。

2.若函数没有返回值语句,则函数的返回值类型说明符为  void  

3.函数由  函数首部  和函数体两个部分组成。

4.若函数类型缺省没定义,则隐含的函数返回值类型是  int  

5.已知函数的定义为int fun(int a, double b){…},则声明函数的语句为  int fun(int a, double b); 

6.声明一个局部变量用静态方式存储的关键字是  static  

7.以下程序的输出结果是  7,14  

#include<stdio.h>

int func(int a, int b)

{       

  static int m = 1, i = 2;

  i += m;

  m = i + a + b;

  return m;

}

int main(void)

{

  int k = 3, m = 1, p;

  p = func(k, m);

  printf("%d,", p);

  p = func(k, m);

  printf("%d\n", p);

  return 0;

}

 

8.以下程序的输出结果是  1  2  

#include<stdio.h>

int func(int a)

{

  int m = 0;

  switch (a)

  {

    case 1: m = 0; return a;

    case 2: m = 1;

    case 3: m = 2; break;

    case 4: m = 3;

   }

  return m;

}

int main(void)

{

  int n = 1;

  printf("%d ", func(n));

  n = 2;

  printf("%d ", func(n));

  return 0;

}

 

9.以下程序的输出结果是  a=48  

#include<stdio.h>

int f(intd[], int m)

{

  int j, s = 1;

  for (j = 0; j < m; j++)

   {

     s = s*d[j];

   }

  return s;

}

int main(void)

{

  int a, z[] = { 2,4,6,8,10 };

  a = f(z, 3);

  printf("a=%d\n", a);

  return 0;

}

 

10.以下程序的输出结果是  654321 

#include<stdio.h>

void func(int n)

{

  printf("%d", n % 10);

 if (n >= 10)

  {

    func(n / 10);

  }

 else

  {

    printf("\n");

  }

}

int main(void)

{

  func(123456);

  return 0;

}

 

 

编程题

编写一个判断质数的函数,在主函数输入一个整数,输出是否是质数。

#include <stdio.h>

#include<math.h>

int prime(int);

int main(void)

{

int n;

printf("n=");

scanf("%d", &n);

if (n == 1 || n == 0 || !prime(n))

{

       printf("%d is not a prime number.\n", n);

}

else

{

       printf("%d is a prime number.\n", n);

}

return 0;

}

int prime(int n)

{

int flag = 1, i, m = (int)(sqrt(n));

for (i = 2; i <= m; i++)

{

       if (n%i == 0)

       {

         flag = 0;

         break;

       }

}

return flag;

}

编写一个能比较字符串大小的函数,将两个字符串中第一个不相同字符的ASCII码值之差作为返回值。

#include <stdio.h>

#define N 100

int MyStrcmp(char s[], char t[]);

int main()

{

  char  c, str1[N], str2[N];

  printf("Input a string:");

  scanf("%s", str1);

  printf("Input another string:");

  scanf("%s",str2);

  if (MyStrcmp(str1, str2) > 0)

    printf("str1>str2\n");

  else if (MyStrcmp(str1, str2) < 0)

        printf("str1<str2\n");

      else

 printf("str1=str2\n");

  return 0;

}



int MyStrcmp(char s[], char t[])

{

  int i;

  for (i = 0; s[i] == t[i]; i++)

  {

    if (s[i] == '\0')

return 0;

  }

  return (s[i] - t[i]);

}

编写程序,从键盘输入10个整数,用函数实现将其中最大数与最小数的位置对换,输出调整后的数组。

#include <stdio.h>

#define ARRSIZE 10  

void MaxMinExchang(int a[], int n);

int main(void)

{

  int a[ARRSIZE], i;

  printf("Input %d numbers:\n",ARRSIZE);

  for(i=0; i<ARRSIZE;i++)

scanf("%d", &a[i]);

  MaxMinExchang(a, ARRSIZE);

  printf("After exchange:\n");

  for(i=0;i<ARRSIZE;i++)

printf("%4d",a[i]);

  return 0;

}

void MaxMinExchang(int a[], int n)

{

  int maxValue=a[0],minValue=a[0],maxPos=0,minPos=0;

  int i, temp;

  for(i=1;i<n;i++)

  {

if (a[i] > maxValue)

{

maxValue = a[i];

maxPos = i;

}

else if (a[i] < minValue)

{

 minValue = a[i];

minPos = i;

}

  }

  temp = a[maxPos];

  a[maxPos] = a[minPos];

  a[minPos] = temp;

}

 

编写函数,对给定的二维数组(3×3)进行转置(即行列互换)。

#include <stdio.h>

#define N 3

void convert(int array[][3]);

int main()

{

  int array[N][N];

  int i, j;

  printf("Input array:\n");

  for(i=0;i<N;i++)

   for(j=0;j<N;j++)

     scanf("%d",&array[i][j]);

  printf("Original array :\n");

  for(i=0;i<N;i++)

  {

    for(j=0;j<N;j++)

      printf("%5d",array[i][j]);

    printf("\n");

  }

  convert(array);

  printf("New array:\n");

  for(i=0;i<N;i++)

   {

     for(j=0;j<N;j++)

          printf("%5d", array[i][j]);

     printf("\n");

   }

 return 0;

}

void convert(int array[][3])

{

  int i, j, t;

  for (i = 0; i < N; i++)

   for (j = i + 1; j < N; j++)

     {

        t=array[i][j];

         array[i][j]=array[j][i];

         array[j][i]=t;

     }

}

编写函数,用冒泡法对输入的字符(不超过10个)按从小到大顺序排序。

#include <stdio.h>

#include <string.h>

#define N 10

void sort(char str[]);

int main(void)

{

  char str[N+1];

  printf("Input a string with less than %d characters: \n", N);

  while(1)

  {

      scanf("%s", str);

     if(strlen(str)>N)

         printf("String too long, input again!\n");

       else break;

  }

  sort(str);

  printf("Sorted string: %s\n", str);

  return 0;

}

void sort(char str[])

{

  int i,j,len=strlen(str);

  char t;

  for(j=0;j<len-1;j++)

       for(i=0;i<len-1-j;i++)

       {

         if(str[i]>str[i+1])

              {

             t=str[i];

                str[i]=str[i+1];

                str[i+1]=t;

              }

       }

}

 

编写程序,输出3~10000内的可逆质数。可逆质数是指:一个质数将其各位数字的顺序倒过来构成的反序数也是质数。如157和751均为质数,它们是可逆质数。要求调用两个函数实现。

#include<stdio.h>

#include<math.h>

int invert(int a)

{

  int s=0;

  while(a>0)

       {

         s=10*s+a%10;

         a/=10;

       }

  return s;

}

int prime(int a)

{

  int i,r;

  r=(int)sqrt(a);

  for(i=2;i<=r;i++)

       {

        if (a%i == 0)

             return 0;

       }

  return 1;

}

int main(void)

{

  int i;

  for(i=3;i<10000;i++)

  {

        if(prime(i) && prime(invert(i)))

        {

          printf("%5d", i);

        }

  }

  return 0;

}

编写函数,将一个十进制数转换成八进制数。

#include <stdio.h>

int trans10_8(char x[], int m);

int main(void)

{

       int i, num,len;

       char a[33];

       printf("Input a positive integer: ");

       scanf("%d", &num);

       len=trans10_8(a,num);

       for(i=len-1;i>=0;i--)

       {

              printf("%c",a[i]);

       }

       printf("\n");

       return 0;

}



int trans10_8(char x[], int m)

{

       int r,i=0;

       while(m>0)

       {

              r=m%8;

              x[i]=r+48;

              m=m/8;

              i++;

       }

       x[i]='\0';

       return i;

}

从键盘输入一个正整数,逆序输出。要求使用循环和递归两种方法分别实现。

循环

#include <stdio.h>

void Inverse(int num);

int main(void)

{

  int num;

  printf("Input an Integer: ");

  scanf("%d",&num);

  Inverse(num);

  return 0;

}

void Inverse(int num)

{

  while(1)

  {

    if(num<10)

     {

       printf("%d\n",num);

       break;

     }

    else

     {

       printf("%d",num%10);

         num=num/10;

        } 

  }

}

递归

#include <stdio.h>

void Inverse(int num);

int main(void)

{

  int num;

  printf("Input an Integer: ");

  scanf("%d",&num);

  Inverse(num);

  return 0;

}

void Inverse(int num)

{

  if(num<10)

    printf("%d\n",num);

  else

  {

      printf("%d",num%10);

      Inverse(num/10);

  } 

}

 

定义带参数的宏,计算三角形的周长和面积。

#include<stdio.h>

#include<math.h>

#define lt(a,b,c) ((a)+(b)+(c))

#define hlt(a,b,c) 0.5*lt(a,b,c)

#define st(a,b,c) sqrt(hlt(a,b,c)*(hlt(a,b,c)-(a))*(hlt(a,b,c)-(b))* (hlt(a,b,c)-(c))) 

int main(void)

{

  double a, b, c;

  printf("a=");

  scanf("%lf", &a);

  printf("b=");

  scanf("%lf", &b);

  printf("c=");

  scanf("%lf", &c);

  printf("Perimeter=%.2f\n", lt(a, b, c));

  printf("Area=%.2f\n", st(a, b, c));

  return 0;

}

定义函数,参数分别表示行数、列数和字符,输出由该行该列该字符构成的以下这种空心图形。

********

*      *

*      *

********
#include<stdio.h>

void drawRectangle(int Row, int Column, char symbol);

int main(void)

{

       int Row, Column;

       char symbol;

       printf("Row=");

       scanf("%d",&Row);

       printf("Column=");

       scanf("%d",&Column);

       getchar();

       printf("Input a character as a symbol: ");

       scanf("%c",&symbol);

       drawRectangle(Row, Column, symbol);

       return 0;

}

void drawRectangle(int Row, int Column, char symbol)

{



       int i,j;

       for(i=0;i<Column;++i)

       {

              printf("%c",symbol);

       }

       printf("\n");

       for(j=1;j<Row-1; ++j)

       {

              printf("%c", symbol);

              for(i = 1;i<Column-1;++i)

              {

                     printf(" ");

              }

              printf("%c\n",symbol);

       }

       for(i=0;i<Column;++i)

       {

              printf("%c",symbol);

       }

       printf("\n");

}

 

高级语言程序设计基础 (吉顺如版)课后习题(一至三章)答案

char *JUN[]

高级语言程序设计基础四至六章

C语言程序设计概述

选择题

1.一个C程序的执行是从(A)。

A.本程序的main函数开始,到main函数结束

B.本程序文件的第一个函数开始,到本程序文件的最后一个函数结束

C.本程序的main函数开始,到本程序文件的最后一个函数结束

D.本程序文件的第一个函数开始,到本程序main函数结束

 

2.以下叙述正确的是(C)。

A.在C程序中,main函数必须位于程序的最前面

B.C程序的每行中只能写一条语句

C.C语言本身没有输入输出语句

D.在对一个C程序进行编译的过程中,可发现注释中的拼写错误

 

3.以下叙述不正确的是(D)。

A.一个C源程序可由一个或多个函数组成

B.一个C源程序必须包含一个main函数

C.C程序的基本组成单位是函数

D.在C程序中,注释说明只能位于一条语句的后面

 

4.在一个C程序中下列说明正确的是(B)。

A.main()函数必须出现在所有函数之前

B.main()函数可以在任何地方出现

C.main()函数必须出现在所有函数之后

D.main()函数必须出现在固定位置

 

5.下面描述不正确的是(C)。

A.C语言兼有高级语言和低级语言的双重特点,执行效率高

B.C语言既可以用来编写应用程序,也可以用来编写系统软件

C.C语言的可移植性较差

D.C语言是一种结构化程序设计语言

 

6.下列符号串中,属于C语言合法标识符的是(C)。

A.else   B.a-2    C._123     D. 123

 

7.以下选项中合法的C语言字符常量是(D)。

A.’\128 ‘     B.”a”      C.’ab ‘        D.’\x43′

 

8.在C语言编译系统中,char、long、float、double类型数据所占字节数分别为(C)。

A.1,2,4,8    B.1,4,2,8    C.1,4,4,8    D.1,4,8,8

 

9.编译程序的功能是(B)。

A.建立并修改程序    B.将C源程序编译成目标程序

C.调试程序          D.命令计算机执行指定的操作

 

10.二进制代码程序属于(A)。

A.面向机器语言   B.面向问题语言   C.面向过程语言   D.面向汇编语言

填空题

1.C程序是由函数构成的。其中有并且只能有 1 个主函数。一个函数由两部分组成:函数的首部和 函数体  

2.C程序必须要有一个 main() 函数,而且只能有一个。C语言程序总是从main()函数开始执行,并且终止于该函数。

3.用高级语言编写的源程序必须通过 编译 程序翻译成二进制程序计算机才能识别,这个二进制程序称为 目标 程序。

4.C语言源程序文件的后缀是 .c ,经过编译后,生成文件的后缀是 .obj ,经过连接后,生成文件的后缀是 .exe 

5.为了提高程序的可读性,在编写C程序时通常使用 缩进 格式,并给程序添加必要的注释。注释可出现在程序的任何位置,注释对程序的执行 没有影响 ,C程序中一般块注释用 /*…*/ 表示,行注释用 // 表示。

6.十进制数123,转换为二进制数为 1111011 ,转换为八进制数为 0173 ,转换为十六进制数为 0x7B 

7.将二进制数转换为十进制数:(110101)2=( 53 )10。

8.将十进制浮点数转换为二进制浮点数:(52.625)10=( 110100.101 )2。

编程题

1.下列程序的功能是:从键盘输入两个数 a 和 b,求它们的平方和,并在屏幕上输出。输入该 C 程序,编译并运行之,记下屏幕的输出结果,以文件名 sy1_1.c 保存。

#include <stdio.h>
void main()
{
int a,b,sum; /* 定义整型变量 a、 b、 sum */
printf("Please Input a,b \n "); /* 输出提示信息 */
scanf("%d%d",&a,&b); /* 从键盘输入两个整数分别赋予 a 和 b*/
sum=a*a+b*b; /* 赋值语句,把 a方+b方的结果赋给变量 sum*/
printf("%d*%d+ %d*%d=%d\n",a,a,b,b,sum); /* 输出语句 */
}

 

2.下列程序的功能为:计算 x*y 的值并将结果输出。试纠正程序中存在的错误,以实现其功能。程序以文件名 sy1_2.c 保存。

#include <stdio.h>

void main()
{

  int x = 2, y = 3, a;

        a = x*y;

  printf("a=%d",a);

  printf("\n");


}

3.下程序的功能为:求两数中的较大数据并输出。纠正程序中存在的错误,以实现
其功能。程序以文件名 sy1_3.c 保存。

#include <stdio.h>

void main(void)

{

  int a, b, max;

  scanf("%d,%d", &a, &b);   /*从键盘输入两个整数分别赋予变量a和b */

  max = a;

  if(max<b)  max = b;   /*如果max中的值小于b中的值,则把b的值赋给max */

  printf("max=%d", max);   /*输出max的值*/


}

4.从键盘输入两个整数,输出这两个整数的和。 根据注释信息填写完整程序,以实
现其功能。以文件名 sy1_4.c 保存。

#include<stdio.h>
void main()
{
    int x,y,total; /* 定义整型变量 x,y,total*/
//    printf("Please input x,y ! "); /* 输出提示信息 */
    scanf(“%d%d”,&x,&y); /* 由键盘输入两个数分别赋予 x 和 y*/
    total=x+y; /* 赋值语句 */
    printf("%d + %d=%d\n",x,y,total); /* 输出两个整数的和 */
}

5. 从键盘输入两个整数,输出这两个整数的差。根据注释信息填写完整程序,以
实现其功能。程序以文件名
sy1_5.c 保存。

#include<stdio.h>
void main()
{
   int a,b,m;
   printf("Input a,b please ! ");
   scanf("%d%d",&a,&b);
   m=a-b; /* 赋值语句,将 a 和 b 的差值赋给 m */
   printf("m=%d\n",m); /* 输出 a 和 b 差的结果值后换行 */
}

6. 编写程序, 运行后输出如下信息: “ Better City,Better Life! ”。 程序以文件名sy1_6.c保存。

#include <stdio.h>
void main()
{
printf("Better City,Better Life!\n");
}

7. 编写程序,从键盘输入 3 个整数,输出它们的平方和。程序以文件名 sy1_7.c 保存。

#include <stdio.h>
void main()
{
   int a,b,c,sum;
 //  printf("Please Input a,b,c \n ");
   scanf("%d,%d,%d",&a,&b,&c);
   sum=a*a+b*b+c*c;
   printf("sum=%d",sum);
}

 

章节测验

 

下列属于C语言关键字的是(      A   )

  • A、typedef
  • B、ENUM
  • C、Int
  • D、unien

下列叙述中,不正确的是(   C      )

  • A、分号是C语句的必要组成部分
  • B、C语句的注释可以写在语句的后面
  • C、主函数的名字不一定用main表示
  • D、函数是C程序的基本单位

构成C语言源程序的基本单位是(   B      )
  • A、过程
  • B、函数
  • C、子程序
  • D、命令

一个C程序的执行是从(     C  )

  • A、main函数开始,直到最后一个函数结束
  • B、第一个函数开始,直到最后一个函数结束
  • C、main函数开始,直到main函数结束
  • D、第一条语句开始,直到最后一条语句结

下列属于C语言中合法标识符的是(     B     )

  • A、ex-1
  • B、_cook
  • C、951a
  • D、if

以下叙述不正确的是(    D     )

  • A、注释说明被计算机编译系统忽略
  • B、注释说明必须跟在“//”之后不能换行或者括在“/*”和“*/”之间且注释符必须配对使用
  • C、注释符“/”和“*”之间不能有空格
  • D、在C程序中,注释说明只能位于一条语句的后面

下面四组选项中,均是C语言关键字的选项是(  B     )

  • A、auto     enum     include
  • B、switch    typedef    continue
  • C、signed    union    scanf
  • D、if   struct   type

在C程序中,main函数的位置(     A    )

  • A、可以任意
  • B、必须作为最后一个函数
  • C、必须作为第一个函数
  • D、必须放在它所调用的函数之后

C程序是由(    B   )构成的

  • A、若干过程
  • B、一个或多个函数
  • C、若干子程序
  • D、一个主程序与若干子程序

C语言的源程序通常的扩展名是(     B     )

  • A、.cpp
  • B、.c
  • C、.obj
  • D、.exe

C语言属于(    C      )

  • A、机器语言
  • B、汇编语言
  • C、高级语言
  • D、低级语言

 

C语言允许直接访问内存地址。(√)


C语言本身没有输入输出语句。(√)
 

C语句以冒号作为结束。(X)
 

1C_fun是合法的标识符。(√)
 

while、If、double均是C语言的关键字。(X)

 

 

求解矩形的面积 题目描述

题目内容:编写一个程序,从键盘读入一个矩形的两个边的值(整数),输出矩形面积。输入样例:3,5

输出样例:15

#include<stdio.h>

void main(void){

int a,b,c;

scanf("%d,%d",&a,&b);

c=a*b;

printf("%d",c);


}

求解梯形的面积题目描述

题目内容:从键盘上输入一个梯形的上底a、下底b和高h,输出梯形的面积。

输入样例:2,4,3

输出样例:9.000000

输入样例:2.4,2.6,3.8

输出样例:9.500000

#include <stdio.h>

#include <math.h>

void main(void) {

  float a, b,c,ans;

  scanf("%f,%f,%f", &a, &b,&c);

  ans = ((a+b)*c)/2;

  printf("s=%.2f", ans);

}

 


顺序结构程序设计

选择题

1.在C语言中,要求运算数必须是整型的运算符是(D)。

A./      B.++    C.!=    D.%

 

2.若有以下语句:char w; int x; f1oat y;double z;则表达式w*x+z-y值的数据类型为(D)。

A.float    B.char     C.int    D.double

 

3.假设所有变量均为整型,则表达式(a=2,b=5,b++,a+b)的值是(B)。

A.7   B.8     C.6   D.2

 

4.执行语句“x=(a=3,b=a–);”后,x、a、b的值依次为(C)。

A.3,3,2     B.2,3,2   C.3,2,3    D. 2,3,3

 

5.若有以下语句:int a=3,b=6,c;c=a^b<<2;,则变量c的二进制值是(A)。

A.00011011     B.00010100   C.00011000    D.00000110

 

6.用函数从终端输出一个字符,可以使用函数(B)。

A.getchar()    B.putchar()   C.gets()   D.puts()

 

7.要输出长整型的数值,需用格式符(B)。

A.%d      B.%ld      C.%f    D.%c

 

8.设a、b为float型变量,则以下不合法的赋值语句是(B)。

A.–a    B.b= (a%4)/5   C.a*=b+9   D.a=b=10

 

9.以下程序的输出结果是(D)。

#include<stdio.h>

int main(void)

  {

     char c1='6',c2='0';

     printf("%c,%c,%d,%d\n",c1,c2,c1-c2,c1+c2);

 }

 

A.6,0,7,6      B.6,0,5,7     C.输出出错信息       D.6,0,6,102

 

10.有以下程序:

#include<stdio.h>

int main (void)

  {

     int m,n,p;

    scanf("%d%d%d",&m,&n,&p);

    printf("m+n+p=%d\n", m+n+p);

    return 0;

 }

 

当从键盘上输入的数据为:2,3,5<Enter>,则正确的输出结果是(D)。

A.m+n+p=10   B.m+n+p=5   C.m+p=7   D.不确定值

填空题

1.表达式“3.5+(int)(8/3*(3.5+6.7)/2)%4”的值为  5.5  

2.表达式 5.7 + 2/19=  5.7 。若定义“int a=4,b;”,则表达式“(b=6*5,a*4),a+16”的值是 20 

3.假设x是一个两位正整数,使该数的个位和十位调换的表达式为  x%10*10+x/10  。(如78调换后变为87)

4.将数学式  转换成C语言表达式为:  sin(sqrt(x*x))/(a*b)  

5.设整型变量 x、y、z均为3,执行“x%=y+z”后,x=  3  

6.若有char x=32 , y=3 ; 则表达式 ~x&y的值为  3  

7.算法通常具有以下5个方面的特性:  有穷性  确定性  可行性  、零个或多个输入、一个或多个输出。

8.若有定义“char c1=’a’,c2=’e’;”,已知字符“a”的ASCII码值是97,则执行语句“printf(“%d,%c”,c1,c2+2); ”后的输出结果是  97,g  

9.程序填空。

#include<stdio.h>

#include<math.h>

int main(void)

   { 

float x,y,z;

      scanf("  %f,%f  ",  &x,&y );  //从键盘输入两个浮点数

      z=2*x*sqrt(y);

      printf("z=%5.2f",z);

     return 0;

  }

 

 

10.下列程序的运行结果是 16.00 

#include<stdio.h>

int main(void)

   {

   int a=9,b=2;

      float x=6.6,y=1.1,z;

      z=a/2+b*x/y-1/2;

     printf("%5.2f",z);

     return 0;  

  }

编程题

1. 【问题描述】

下列程序的功能为: 输入一个华氏温度,要求输出摄氏温度。公式为:c=(5/9)*(f-32),输出取2位小数。纠正程序中存在的错误,以实现其功能。程序以文件名sy2_1.c保存。

#include <stdio.h>

int main(void)

{

float c,f ;

//printf("请输入一个华氏温度:\n");

scanf("%f", f);

c=(5/9)*(f-32);

printf("c=%5.2f\n",c);

return 0;

}

【样例输入】70

【样例输出】c=21.11

#include <stdio.h>

void main(void)

{

  float c, f;

  //printf("请输入一个华氏温度:\n");

  scanf("%f", &f);

  c = (5.0 / 9)*(f - 32);

  printf("c=%5.2f\n", c);

}

2. 【问题描述】

下列程序的功能为:按下列公式计算并输出x的值。其中a和b的值由键盘输入。纠正程序中存在的错误,以实现其功能。程序以文件名sy2_2.c保存。

#include <stdio.h>

int main(void)

{

  int a,b;

  float x;

  scanf("%d,%d",a,b);

  x=2ab/(a+b)(a+b);

  printf("x=%d\n",x);

  return 0;

}

【样例输入】2,4

【样例输出】x=0.444444

#include <stdio.h>

void main(void)

{

  int a, b;

  float x;

  scanf("%d,%d", &a, &b);

  x = 2.0*a*b / (a + b)*(a + b);

  printf("x=%f\n", x);
}

3. 【问题描述】

下列程序的功能为:从键盘输入一个小写字母,要求输出该小写字母及其ASCII码,并将该小写字母转换成大写字母并输出。请纠正程序中存在的错误,使程序实现其功能,程序以文件名sy2_3.c保存。

#include <stdio.h>

int main(void)

{

  char c1,c2;

  c1=getchar;               // 从键盘输入一个小写字母  

  printf("%c,%d\n",c1,c1);    // 输出该小写字母及其ASCII码值  

  c2=c1+26;                // 转换为大写字母  

  c2=putchar();              // 输出大写字母  

  return 0;

                  }

【样例输入】a

【样例输出】a,97 A

#include <stdio.h>

int main(void)

{

  char c1, c2;

  c1 = getchar();               // 从键盘输入一个小写字母  

  printf("%c,%d\n", c1, c1);    // 输出该小写字母及其ASCII码值  

  c2 = c1 -32;                // 转换为大写字母  

  putchar(c2);              // 输出大写字母  

  return 0;

}

4. 【问题描述】

下列程序的功能为:按给定格式输入数据,按要求格式输出结果。补充完善程序,以实现其功能。程序以文件名sy2_4.c保存。

【输入形式】 2 3.4

【输出形式】 x+y=5.4

#include <stdio.h>

int main(void)

{

  int x;

  float y;

  scanf("%d%f", &x, &y);

  printf("x+y=%.1f\n", x + y);

  return 0;

}

5. 【问题描述】

下列程序的功能为:设圆半径r=1.5,圆柱高h=3,求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积。用scanf输入数据r、h,输出计算结果,输出时要求有文字说明,取小数点后2位数字。请补充完善程序,以实现其功能。程序以文件名sy2_5.c保存。

#include <stdio.h>

int main(void)

{

  float h, r, l, s, sq, vq, vz;

  static float pi = 3.1415926;

  scanf("%f%f", &r, &h);

  l = 2 * pi*r;

  s = pi*r*r;

  sq = r*r*4*pi;

  vq =(4.0/3)*r*r*r*pi ;

  vz =r*r*h*pi ;

  printf("l=%.2f\n",l);

  printf("s=%.2f\n",s);

  printf("sq=%.2f\n",sq);

  printf("vq=%.2f\n",vq);

  printf("vz=%.2f\n",vz);

  return 0;

}

6. 【问题描述】

下列程序的功能为:从键盘输入3个整数分别存入变量x、y、z,然后,将变量x的值存入变量z,将变量y的值存入变量x,将变量z的值存入变量y,输出经过转存后变量x、y、z的值。(提示:使用中间变量)补充完善程序,以实现其功能。程序以文件名sy2_6.c保存。

#include <stdio.h>

int main(void)

{

int x,y,z,      ;

//printf("Please input x,y,z: ");

scanf("%d%d%d",             );

                       ;

                       ;

                       ;

                       ;

printf("x=%d\ny=%d\nz=%d\n",x,y,z);

return 0;

}

【样例输入】

2 3 4

【样例输出】

x=3

y=4

z=2

#include <stdio.h>

int main(void)

{

  volatile int x, y, z, i;

  scanf("%d%d%d",&x,&y,&z);

  i = z;z = x;x= y;y= i;

  printf("x=%d\ny=%d\nz=%d\n", x, y, z);

  return 0;

}

7. 【问题描述】

编写程序实现如下功能:从键盘输入数据两个整数、两个浮点数、两个字符,分别赋给整型变量a和b、浮点型变量x和y以及字符变量ch1和ch2,要求输出如下结果形式,程序以文件名sy2_7.c保存。

【样例输入】

3 4

2.5 5.6

AB

【样例输出】

a=3,b=4

x=2.5,y=5.6

ch1=A,ch2=B

#include <stdio.h>

int main(void)

{

  volatile int a, b;
  volatile float x, y;
  char c1, c2;
  scanf("%d%d%f%f", &a, &b, &x, &y);
  getchar();
  c1 = getchar(); c2 = getchar();
  printf("a=%d,b=%d\nx=%.1f,y=%.1f\n", a, b, x, y);
  printf("ch1="); putchar(c1);
  printf(",ch2="); putchar(c2); putchar('\n');
  return 0;
}

8. 【问题描述】

编写程序实现如下功能:输入一元二次方程ax2+bx+c=0的系数a、b、c,求方程的根。要求:运行该程序时,输入a、b、c的值,分别使b2-4ac的值大于、等于和小于零,观察并分析运行结果。程序以文件名sy2_8.c保存。求根公式如下:

【样例输入】

2,5,2

【样例输出】

x1=-0.50

x2=-2.00

#include <stdio.h>
#include <math.h>
int main() {
  volatile float a, b, c, __ans, x1, x2;
  scanf("%f,%f,%f", &a, &b, &c);
  if (a == 0) {
    /* 预留代码块 用于判断方程任意解 无解 一元一次的情况*/
  }
  else {
    __ans = b*b - 4 * a*c;
    if (__ans < 0)
      /* 预留代码块*/
      while (1);
    else if (__ans == 0) {
      printf("x1=%.2f\nx2=%.2f\n", -b / 2 / a, -b / 2 / a);
    }
    else {
      x1 = (-b + sqrt(__ans)) / 2 / a;
      x2 = (-b - sqrt(__ans)) / 2 / a;
      printf("x1=%.2f\nx2=%.2f\n", x1, x2);
    }
  }
  return 0;
}

 

章节测验

下列不合法的字符常量是(     B   )

  • A、‘2’
  • B、‘ab’
  • C、‘\n’
  • D、‘\101’

 

下列合法的转义字符是(    A   )

  • A、’\”‘
  • B、‘\ee’
  • C、‘\018
  • D、‘xab’

 

以下选项中不合法的实型常量是( D)

  • A、-62000
  • B、0.123
  • C、5
  • D、E3

 

若已定义x和y为double型变量,则表达式x=1,y=x+3/2的值是(    B      )

  • A、2
  • B、2.5
  • C、2.0
  • D、1

 

执行语句“x=(a=5,b=a–)”后,x,a,b的值分别是(      C     )

  • A、5,4,4
  • B、5,5,4
  • C、5,4,5
  • D、4,5,4

 

已知int x=5,y=5,z=5;则执行语句x%=y+z;后,x的值是(       D

  • A、6
  • B、1
  • C、0
  • D、5

 

表达式(k=3*2,k+4),k*3的值是(    A    )

  • A、18
  • B、14
  • C、30
  • D、10

 

下面程序的输出结果是(  B  )。

#include<stdio.h>

void main()

{

 int x=10,y=3,z;

  printf("%d\n",z=(x%y,x/y));

   }

 

  • A、4
  • B、3
  • C、1
  • D、0

 

经过下述赋值后,变量x的数据类型是( A   )。

float x=21.0;

int y;

y=(int)x;

  • A、float
  • B、double
  • C、int
  • D、char

 

执行以下程序后,输出结果为 (A)

#include<stdio.h>

void main()

{

int x;

float y;

y=3.6;

x=(int)y+10;

printf("x=%d,y=%f",x,y);

}

 

  • A、x=13,y=3.600000
  • B、x=13.5,y=3.60000
  • C、x=13,y=3
  • D、x=13,y=3.6

 

下列浮点数的形式表示错误的是(  B   )

  • A、10.6
  • B、12E2.0
  • C、100000
  • D、.5
若x为int类型,则表达式x=1,x+5,x++的值是1。(对)
8E2是不合法的整型常量。(错)
-6.2e4是不合法的实型常量。(错)
执行语句“printf(“%x”,-1);”屏幕显示-ffff。(错)
若有int x=8,y=5,z;则执行语句“z=x/y+0.4”后,z的值为1。(对)

 

题目内容:输入一个三位的正整数,按逆序打印出该数的各位数字。输入样例:369

输出样例:963

输入样例:680

输出样例:086

#include<stdio.h>

void main(void)

{
  volatile int n,a,b,c;

  scanf("%d",&n);

  a=n%10;

  b=n/10%10;

  c=n/100%10;

  printf("%d%d%d",a,b,c);

 } 

题目内容:编写程序,计算并输出华氏温度为80F所对应的摄氏温度C。

                  转换公式为:C=5*(F-32)/9

输出样例:**.******测试用例

用例1

分数: 20

错误提示:

题目运行时间限制: 500ms

题目运行内存限制: 32000kb

#include<stdio.h>

int GKD(volatile double F)

{

  volatile double C;

  C=5*(F-32)/9;

  printf("%lf",C);

 } 

int main(void)

{

GKD(80.0);

return 0;

}

 


选择结构程序设计

选择题

1.要判断char型变量m是否是数字字符,可以使用表达式(B)。

A.m>=0 && m<=9

B.m>=’0′ && m<=’9′

C.m>=”0″ && m<=”9″

D.m>=0 & m<=9

2.要判断char型变量c是否为大写字母,可以使用表达式(B)。

A.c>=A && c<=Z

B.c>=’A’ && c<=’Z’

C.c>=”A” && c<=”Z”

D.’A'<c<‘Z’

3.为了避免嵌套的if-else语句的二义性,C语言规定else总是与(C)组成配对关系。

A.缩排位置相同的if

B.在其之前未配对的if

C.在其之前未配对的最近的if     

D.同一行上的if

4.假设有定义int a=1,b=2,m=1,n=1。则执行表达式 (m=a>b)&&(n=a!=b)后,m,n的值为(A)。

A.0 1       B.1 0

C.1 1       D.0 0

5.以下选项中不合法的表达式是(C)。

A.x>=1&&x<=50       B.x=y==1

C.x+1=x                     D.y=y+1

6.下列运算符中优先级最高的是(A)。

A.!        B.&&

C.>       D.!=

7.有下面的程序段,执行后c的值为(D)。

int a=3, b=2, c;

char p='A';

c=(a && b)&&(p>'B')

 

A.false        B.true

C.1              D.0

8.若已定义a=3,b=c=4,则执行下列语句后变量x、y的值分别为(A)。

x=(c>=b>=a)?1:0;

y=c>=b&&b>=a;

A. 0 1         B.1 1

C.0 0          D.1 0

9.在C语言中,多分支选择结构语句为:

switch(表达式)

{

case 常量表达式1: 语句1;

……

case 常量表达式k: 语句k;

   default:                    语句k+1;

}

其中switch括号中表达式的类型(C)。

A.只能是整型

B.可以是任意类型

C.可以是整型或字符型     

D.可以是整型或实型

10.执行以下程序后的输出结果是(C)。

int main(void)

{

         int a=4, b=5,c=5;

a=b==c;

printf("%d  ",a);

a=a==(b-c);

printf("%d\n",a);

}

 

A.5  0        B.5  1

C.1  0        D.1  1

填空题

1.有int x,y,z;且x=3,y=4,z=5,则表达式 !(x+y)+z-1&&y+z/2的值为 1  

2.用x描述1到100之间的所有偶数的表达式为 (x>=1&&x<=100)&&(x%2==0)

3.当a=3,b=-4,c=5时,表达式(a&&b)==(a||c)的值是 1  

4.判定闰年year的条件是能被4整除但不能被100整除,或能被400整除的年份,用表达式表示为 (year%4==0&&year%100!=0)||(year%400==0)

5.若有定义“int a=1,b=0;”表达式a–>(b+a)?10:5>b++?’A’: ‘Z’的值为 A 

6.能正确表示x<0或10<x<50关系的C语言表达式是 x<0||(x>10&&x<50) 

7.下面程序的输出结果是 5  3  2 

 #include<stdio.h>

int main(void)

{        

 int x,y=1,z;

        if(y!=0)x=5;

        printf("%d  ",x);

        if(y==0)x=4;

        elsex=3;

        printf("%d  ",x);

        x=2;

        if(y<0)

           if(y>0) x=4;

           else x=5;

        printf("%d\n",x);

        return 0;

     }

 

8.下面程序的输出结果是 #& 

#include<stdio.h>

int main(void)

{ 

  int a=2,b=7,c=5;

  switch(a>0)

    {  case 1:switch(b<0)

                { case 1:printf("@");break;

  case 2:printf("!");break;

 }

       case 0:switch(c==5)

{ case 0:printf("*");break;

  case 1:printf("#");break;

  default:printf("$");break;

}

       default:printf("&");

     }

  return 0;

 }

 

9.下面程序的输出结果是:

      (1)2

      (2)3

      CC

      a=2,b=2,c=4    

#include<stdio.h>

int main(void)

{

  int a,b,c;

  a=b=c=1;

  a+=b;

  b+=c;

  c+=a;

  printf("(1)%d\n",a>b?a:b);

  printf("(2)%d\n",a>c?a--:c++);

  (a>=b>=c)?printf("AA"):printf("CC");

  printf("\na=%d,b=%d,c=%d\n",a,b,c);

  return 0;

 }

 

10.下面程序的输出结果是 a=2, b=3 

#include<stdio.h>

int main(void)

{  int a=1, b=1;

     switch(a-b)

{

case 0: b++;

            case1: a++;b++;break;

            case 2: b++;

}

printf("a=%d, b=%d\n", a, b); 

return 0;

}

编程题

 

(1)求分段函数

有一个函数如下:

编写程序,输入x的值,计算相应的y值输出(保留2位小数)。

#include<stdio.h>

#include<math.h>

int main(void)

{

  float x,y;

  scanf("%f", &x);

  if(x<4)  y=(x+7)/(2*x-1);

  else if(x<70) y=3*x*x+5;

          else y=x-sqrt(4*x-1);

  printf("y=%.2f\n",y);

  return 0;

}

运行结果:

(1)

3

y=2.00

(2)

9.5

y=275.75

(3)

75

y=57.71

 

(2)判断三角形类型,计算三角形面积

编写一个程序,根据输入的三角形的三条边判断是否能组成三角形,如果可以则输出它的面积和三角形类型(等边、等腰、直角、等腰直角、一般三角形等)。

学校网上的oj是英文,请注意,踩一下oj的乐色编译环境

#include <stdio.h>

#include <math.h>

int main(void)

{

  float a, b, c, s, area;

  int flag;

  printf("Input the three edge length:");

  scanf("%f,%f,%f",&a,&b , &c);

  if(a+b>c && b+c>a && a+c>b)

   {

     s=(a+b+c)/2;

    area=sqrt(s*(s-a)*(s-b)*(s-c));

    printf("area=%.2f\n",area);

    if(fabs(a-b)<=0.0001&&fabs(b-c)<=0.0001&&fabs(c-a)<=0.0001)

     {

       printf("等边三角形");

     }

         else if(fabs(a-b)<=0.0001||fabs(b-c)<=0.0001||fabs(c-a)<=0.0001)

       {

      if(fabs(a*a+b*b-c*c)<=0.0001||fabs(a*a+c*c-b*b)<=0.0001||fabs(b*b+c*c-a*a)<=0.0001)

        {

           printf("等腰直角三角形");

        }

       else

            printf("等腰三角形");

          }

else if(fabs(a*a+b*b-c*c)<=0.0001||fabs(a*a+c*c-b*b)<=0.0001||fabs(b*b+c*c-a*a)<=0.0001)

       {

         printf("直角三角形");

       }

        else printf("一般三角形\n");

   }

else   printf("不是三角形\n");

return 0;

}
运行结果:



(1)

Input the three edge length:3,4,5

area=6.00

直角三角形



(2)

Input the three edge length:4,4,5

area=7.81

等腰三角形



(3)

Input the three edge length:5,5,5

area=10.83

等边三角形



(4)

Input the three edge length:5,5,7.071065

area=12.50

等腰直角三角形



(5)

Input the three edge length:5,6,7

area=14.70

一般三角形



(6)

Input the three edge length:4,5,10

不是三角形

--------------------------------

 

(3)计算实际奖金值

由键盘输入奖金值,计算并输出相应的税率和实际应得奖金值。分别用if-else的嵌套语句和switch多分支选择语句编写程序。

源程序一:



#include<stdio.h>

int main(void )

{ 

   float n, r, s;

   int m;

   printf("请输入奖金值:");

   scanf("%f", &n);

   if(n<1000) r=0;

     else if(n<3000) r=0.03;

          else if(n<5000) r=0.05;

               else if(n<10000) r=0.07;

                    else r=0.1;

    s=n-r*n;  //计算实际所得奖金值

    printf("税率r=%.0f%%, 奖金值n=%.2f, 实际所得奖金s=%.2f\n",r*100, n, s);

    return 0;

}



源程序二:



#include<stdio.h>

int main(void )

{ 

   float n, r, s;

   int m;

   printf("请输入奖金值:");

   scanf("%f", &n);

   if(n>=10000)  m=10;

   else  m=n/1000;

   switch(m)

   {  case 0: r=0; break;

      case 1:

      case 2: r=0.03; break;

      case 3:

      case 4: r=0.05; break;

      case 5: case 6:

      case 7: case 8:

      case 9:r=0.07; break;

      case 10:r=0.1;

   }

  s=n-r*n;  //计算实际所得奖金值

  printf("税率r=%.0f%%, 奖金值n=%.2f, 实际所得奖金s=%.2f\n",r*100, n, s);

  return 0;

}
运行结果:



(1)

请输入奖金值:3000

税率r=5%, 奖金值n=3000.00, 实际所得奖金s=2850.00



(2)

请输入奖金值:6000

税率r=7%, 奖金值n=6000.00, 实际所得奖金s=5580.00



(3)

请输入奖金值:11000

税率r=10%, 奖金值n=11000.00, 实际所得奖金s=9900.00

--------------------------------

(4)判断能否整除数

从键盘输入一个正整数,判断其是否能同时被5和7整除,若是则输出“YES”,否则输出“NO”。

#include<stdio.h>

int main(void)

{ 

   int x;

   scanf("%d", &x);

   if(x>0)

   { if(x%5==0&&x%7==0)

         printf("YES\n");

     else  printf("NO\n");

   }

   else printf("data error!");

return 0;

}

 

运行结果:



(1)

14

NO



(2)

35

YES



(3)

-9

data error!

---------------

(5)判断整数的位数

给出一个不超过4位数的正整数,判断它是几位数,并按逆向输出各位数字。例1234,输出为4321。

#include<stdio.h>

int main(void)

{ 

   int num,i,j,k,m;

   printf("输入一个少于4位的正整数:");

   scanf("%d",&num);

   if(num>=0 && num<=9999)

   {

    if(num>1000 && num<=9999) 

    {

       printf("是一个4位数\n");

       m=num%10;     /*求个位上的数字*/

       k=num/10%10;   /*求十位上的数字*/

       j=num/100%10;  /*求百位上的数字*/

       i=num/1000;     /*求千位上的数字*/

      printf("逆序数为:%d%d%d%d\n",m,k,j,i);

    }

   else if(num>=100)

   {   printf("是一个3位数\n");

       m=num%10;    /*求个位上的数字*/

       k=num/10%10;  /*求十位上的数字*/

       j=num/100;     /*求百位上的数字*/

      printf("逆序数为:%d%d%d\n",m,k,j);

   }

    else if(num>=10)

       {  

          printf("是一个2位数\n");

          m=num%10;    /*求个位上的数字*/

          k=num/10;    /*求十位上的数字*/

          printf("逆序数为:%d%d\n",m,k);

       }

    else

       {   printf("是一个1位数\n");

           printf("逆序数为:%d\n",num);

       }

   }

   else printf("是一个无效的数\n");

   return 0;

}
运行结果:



(1)

输入一个少于4位的正整数:63

是一个2位数

逆序数为:36



(2)

输入一个少于4位的正整数:135

是一个3位数

逆序数为:531



(3)

输入一个少于4位的正整数:1234

是一个4位数

逆序数为:4321

--------------------------------

章节测验

【单选题】
下列条件语句中,功能与其他语句不同的是( A  )。
  • A、if(a==0) printf(“%d\n”,x); else     printf(“%d\n”,y);
  • B、if(a) printf(“%d\n”,x); else  printf(“%d\n”,y);
  • C、if(a==0) printf(“%d\n”,y); else     printf(“%d\n”,x);
  • D、if(a!=0) printf(“%d\n”,x); else     printf(“%d\n”,y);

 

【单选题】以下程序正确的说法是(A  )。

#include<stdio.h>

void main()

{ 

 int x=0,y=0;

 if(x=y) printf("*****\n");

   else    printf("#####\n");

}
  • A、输出#####
  • B、有语法错误不能通过编译
  • C、可以通过编译,但不能通过连接,因此不能运行
  • D、输出*****

【单选题】分析以下程序的运行结果。 (C)

#include <stdio.h> 

void main()

{  

int k=2;     

switch(k)    

{  

case 1:printf("%d\n",k++);   break;   

case 2:printf("%d ",k++);   

case 3:printf("%d\n",k++);  break;    

case 4:printf("%d\n",k++);     

default:printf("Full!\n");     

} 

}

 

  • A、3 4
  • B、3 3
  • C、2 3
  • D、2 2

 

以下程序的输出结果为(  D  )。

#include<stdio.h>

void main()

{ 

int a=30;

  printf("%d",(a/3>0)?a/10:a%3);

}

 

  • A、0
  • B、1
  • C、10
  • D、3

 

【单选题】
希望当num的值为奇数时,表达式的值为“真”;num的值为偶数时,表达式的值为“假”。则以下不能满足该要求的表达式是(  B  )。
  • A、num%2==1
  • B、!(num%2)
  • C、!(num%2==0)
  • D、num%2

【单选题】以下程序的输出结果是(    C  )。

#include<stdio.h>

void main()

{ 

int x=1,y=0,a=0,b=0; 

  switch(x) 

 { 

case 1:switch(y)

          { 

   case 0:a++;break;

   case 1:b++;break;

          } 

   case 2:a++;b++;break;

  } 

  printf("a=%d,b=%d",a,b);

}

 

  • A、a=1,b=1
  • B、a=1,b=2
  • C、a=2,b=1
  • D、a=2,b=2

 

【单选题】以下程序运行时,输入的x值在(  A  )范围时才会有输出结果。

#include<stdio.h>

void main()

{

int x;

  scanf("%d",&x);

  if(x<5);

  else if(x!=20)

  printf("%d",x);

}

 

  • A、大于等于5且不等于20的整数
  • B、不等于20的整数
  • C、小于5的整数
  • D、大于等于5且等于20的整数

 

以为了避免嵌套的条件分支语句if-else二义性,C语言规定,C程序中的else总是与(  B  )组成配对关系。

  • A、缩排位置相同的if
  • B、在其之前未配对的最近的if
  • C、在其之前未配对的if
  • D、同一行上的if

【单选题】语句  if(x!=0)  y=1;

else      y=2;与(A )等价。

  • A、if(x)   y=1; else    y=2;
  • B、if(x) y=2;else y=1;
  • C、 if(!x) y=1;else   y=2;
  • D、if(x=0) y=2;else      y=1;

【单选题】下列程序段运行后x的值是( C )。

int a = 0, b = 0, c = 0, x = 35;

if (!a)    x--;

else if (b);

if (c)    x = 3;

else     x = 4;

 

  • A、34
  • B、35
  • C、4
  • D、3

下列程序段运行后的结果是( B )。

#include<stdio.h>

int main()

{  

      int  a = 2, b = -1, c = 2;

      if(a < b)

            if(b < 0) 

                  c = 0;

      else  c++;

      printf("%d\n",c);      

      return 0;

}

 

  • A、0
  • B、2
  • C、3
  • D、4
if(a=5) 是允许的。  (√)
在if语句的三种形式中,如果要想在满足条件时执行一组(多个)语句,则必须把这一组语句用{}括起来组成一个复合语句。(√)

if-else语句的一般形式如下,其中的语句1、语句2只能是一条语句。

if (表达式)   语句1

else            语句2   (X)

if-else语句的一般形式如下,若表达式的值为“真”,则执行语句1;否则,就执行语句2。

if (表达式)   语句1

else            语句2  (√)

执行以下程序段后,x的值为0,y的值为1。

int x, y;

if (x = 0)  y = 0;

else        y = 1;    (

 

星期推算。

题目描述

题目内容:假设今天是星期日,编写一个程序,求2019天后是星期几。

输出样例:星期*(星期一、星期二、星期三、星期四、星期五、星期六、星期日)

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define CONF_JUN

int main() {

  volatile int a, b;

  a = 2019 / 7;

  b = 2019 - a * 7;

  if (b == 0)

    printf("星期天");

  else if (b == 1)

    printf("星期一");

  else if (b == 2)

    printf("星期二");

  else if (b == 3)

    printf("星期三");

  else if (b == 4)

    printf("星期四");

  else if (b == 5)

    printf("星期五");

  else if (b == 6)

    printf("星期六");

  return 0;

}

2.特征数判断。

题目内容:编写程序,判断输入的正整数是否既是5又是7的整倍数。

输入样例:-9

输出样例:Data error!

 

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN
int main() {

  volatile int a;

  scanf("%d", &a);

  if (a <= 0)

    printf("Data error!");

  else {

    if (a % 5 == 0 && a % 7 == 0)

      printf("Yes!");

    else {

      printf("No!");

    }

  }

return 0;

}

 

 

天数判断。

题目描述

题目内容:编写程序,从键盘上输入年份和月份,计算并输出这一年的这一月共有多少天。

输入样例:2019-3

输出样例:31

 

#include<stdio.h>

#include<stdlib.h>

#include<math.h>

#define JUN

int main() {

  volatile int a, b;

  scanf("%d-%d", &a, &b);

  if (a % 4 == 0 && a % 400 == 0)

  {

    if (b == 1 || b == 3 || b == 5 || b == 7 || b == 8 || b == 10 || b == 12)

      printf("31\n");

    if (b == 2)

      printf("29\n");

    else {

      printf("30\n");

    }

  }

  else {

    if (b == 1 || b == 3 || b == 5 || b == 7 || b == 8 || b == 10 || b == 12)

      printf("31\n");

    else if (b == 2)

      printf("28\n");

    else {

      printf("30\n");

    }

  }

  return 0;

}

 

 

 

struct my_struct->”四至六章”

学习通/超星 网课视频后台播放教程(Chrome)

网课时期,带伙们都需要上网课,可惜学习通的播放视频鼠标一切出去就会无法播放,非常的8行。

于是这里给出后台播放视频的 非脚本方法 比较安全,不会被学习通后台查到。

此方法每次打开/刷新都需要重新操作一遍,如果想挂后台看的话建议右键标签页,选择静音此标签页选项。

需要软件:Chrome


1,打开Chrome,然后登录学习通账号,并且进入到视频播放页面

2,右键选择 检查,或按键盘上的F12按钮(目的都是打开开发者工具)

这时你的浏览器右边应该是这样的。

看到代码不要慌,这些是由JS/CSS代码与HTML组成的,有兴趣的同学可以自己去了解。

顺带一提,由于超星的鼠标移出页面自动暂停也是通过JS代码实现的,我们这次的操作相当于把这个JS代码(事件)移除了,所以它就检测不了了。

3,点击开发者选项右边栏位下面的Event Listeners

4,点开下方的 mouseout ,并点击展开的Remove

5,再次按F12,关闭开发者工具,再次播放视频,就不会被暂停啦!!!


 

停课不停学,本方法建议同学不要滥用,不然没学会期末考试不过就不是什么好事了,,,

 

 

 

下面出一道C语言算法题,有兴趣的同学可以挑战一下自己,答案可以在下方留言。

谢谢茄子!

HAL库与stm32CUBEMX学习记录——UART设备(1)

由于自己忘性比较大,所以打算花点时间把近期学的一些知识点给整理顺便巩固一下,话不多说直接开始。

一、什么时UART?

uart就是通用异步收发传输设备的简称,从名字里我们可以捕捉到几个关键词:异步、收发。那么也就是说UART没有时钟线或者时钟校准这一说因为它是异步的,而且它不仅可以收信息也可以发信息。因为这一简单实用的特点,在一些数据传输时UART被大量使用。

二、用cubemx配置UART

首先还是选择芯片型号啥的,这里我就不截图了。

然后点开connectivity,所有和通讯相关的大部分都在这儿,

点开后我们会发现不仅有uart还有usart,这两个其实没啥区别,区别就在于uart是异步传输,而usart不仅支持异步传输还支持同步传输。结合我的开发板所以我选择了pa10和pa9两个引脚作为串口设备,也理所应当地选择了usart1。

点开Mode界面我们会发现一堆模式:

从上到下分别是:异步模式

同步模式

单线模式

多处理器通讯

红外线传感器

LIN(一种运用于汽车电子设备通讯协议)

智能卡

有计时功能的智能卡

我们在这里使用第一个异步模式,软件自动为我们选择了两个引脚pa10和pa9,其中pa10作为RX(收信息),pa9作为TX(发信息)。

设置完后再去设置一下系统时钟,因为待会我们会使用串口进行中断所以我们还要使能一下NVIC(中断),最后生成一下代码。

三、HAL库函数讲解

在HAL库中关于UART设备的函数有很多,我会挑选常用且一会使用到的函数进行讲解。

我们打开stm32f1xx_hal_uart.c文件,会大体看到st公司把这么多关于uart的函数分为了4个部分:

1、Initialization and Configuration functions(初始化配置功能)

2、IO operation functions(IO操作功能)

3、Peripheral Control functions(外围操作功能)

4、Peripheral State and Errors functions(外围状态和操作功能)

我们接下来会把重点放在1和2中,3和4都是关于外设的如单线模式、LIN和多处理器之间的通讯。

1是用作初始化功能的函数,在使用标准库时这些是我们自己要进行配置的,但是有了cubemx的帮助这部分我们几乎不用管。

2才是我们真正用来串口通讯的函数,在进行通讯前我们要选择以堵塞方式还是非堵塞方式进行通讯。关于阻塞和非阻塞我也并不能说是很懂,个人感觉阻塞就是传递信息这个过程没有结束那么接下来的任务都不会进行直到这个过程结束,非阻塞就是不管你信息有没有传递完毕都会返回一个值。

阻塞和非阻塞之间的区别给我最大的感受就是,非阻塞有中断功能而阻塞没有。。。。。。而且st官方也对阻塞和非阻塞进行了解释,但是这个解释感觉并没有把阻塞与非阻塞之间的区别给清晰的写出来,也有可能是我语文理解不大好。但是st官方把大篇幅都花在了非阻塞上,而且关于非阻塞的api比阻塞的api明显多了很多(阻塞的只有2个。。。),再根据我的实践得出能用非阻塞就用非阻塞,阻塞在处理信息时有很大的误差,而非阻塞由于有中断功能所以处理信息会高效的很多而且错误率也低,所以我在使用时遵循以下这个规则:由单片机发出的信息使用阻塞式,向单片机发送的信息使用非阻塞式。

在解决完阻塞与非阻塞的问题后让我们来看一下常用的api有哪些:

(+) HAL_UART_Transmit();//阻塞模式发送
(+) HAL_UART_Receive();//阻塞模式接收

(+) HAL_UART_Transmit_IT();//非阻塞中断模式下发送
(+) HAL_UART_Receive_IT();//非阻塞中断模式下接收
(+) HAL_UART_IRQHandler();//中断回调函数句柄

(+) HAL_UART_RxCpltCallback();//中断回调函数

以上这些api可以帮你完成大部分的数据收发功能,接下来我会编写一个实例,实例可以做到接收数据并进行判断,判断成功会发送数据。

uint8_t message_receive[50];

void User_transmite(){
  
  uint8_t message[]="hello,this is SAT8";
HAL_UART_Transmit(&huart1,message,sizeof(message),50);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
   if(huart == &huart1){
     	uint16_t num1=0,num2=0;
  
  uint8_t message1[]="hello,you are my wife,right?";
  uint8_t message2[]="who are you...";
  for(int i=0;i<=sizeof(message1);i++){
      if(message_receive[i]== message1[i]){
     				num1++;
  }
      else {
        break;
      }
  }
  for(int i=0;i<=14;i++){
     if(message_receive[i]==message2[i]){
      num2++;
  }
    else{
      break;
    }
  }
  if(num1==sizeof(message1)-1){
       uint8_t shy_word[]="yes...i am your wife";
     HAL_UART_Transmit(&huart1,shy_word,sizeof(shy_word),50);
     }
  if(num2==sizeof(message2)-1){
      uint8_t angry_word[]="you forget your wife?fxxk!";
    HAL_UART_Transmit(&huart1,angry_word,sizeof(angry_word),50);
  }
     
  }
}

void User_receive(){
  HAL_UART_Receive_IT(&huart1,message_receive,30);
  HAL_Delay(100);
}
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */
  

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  /* USER CODE BEGIN 2 */
  HAL_UART_IRQHandler(&huart1);
  User_transmite();
  /* USER CODE END 2 */
 
 

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    User_receive();
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

调试成功,有一点要说明一下,

void User_receive(){
  HAL_UART_Receive_IT(&huart1,message_receive,30);
  HAL_Delay(100);
}

在这个接收函数中我添加了一个延时,时间为100ms。具体为什么是因为如果不加,在两次发送讯息之间时间如果过短会导致数据接收不到,具体原因还不清楚,但是一加上去后在两次快速发送讯息时导致的数据接收问题就大大改善了,如果有人能够回答一下的话就太好了。

 

openCV:使用均值模糊和高斯模糊来对图像进行处理

模糊又称平滑,是一种去噪手段,接下来我们会介绍均值模糊和高斯模糊。

均值模糊

我们会先创建一个卷积核,这个卷积核可以是3*3或者任意的x*x,如下图:

在创建完一个3*3卷积核后我们会将黄色格子和红色格子的像素值给加起来然后除以9(也就是卷积核里有多少个像素点就除以多少),这样我们就得出了平均值,然后将平均值赋给红色格子,这样我们就对一个像素完成了模糊处理。

所谓模糊就是平滑,只要让所有的像素值都趋于一个平均值就能达到平滑的效果了。接着我们就对下一个像素点进行同样的处理直到整张图片都完成处理。

openCV给出了均值模糊的API:

void blur( InputArray src, OutputArray dst,
 Size ksize, Point anchor = Point(-1,-1),
 int borderType = BORDER_DEFAULT );

src:输入图像

dst:输出图像

ksize:卷积核大小

Point anchor:锚点,最后得出的平均值所赋给的像素点坐标也就是上文所提到的红色格子,建议不要轻易改变这里的数值,默认数值为(-1,-1)

borderType: 用于推断图像外部边缘像素,有默认值BORDER_DEFAULT,这个参数是什么意思呢?就是我们在使用卷积核时是从第2行第2列开始的,那么问题来了第一行和第一列怎么办?难道我们就不管了?当然不可能,我们对边缘处理有以下几种办法:

  • ① 对称处理,就是把已有的点拷贝到另一面的对应位置,模拟出完整的矩阵。
  • ② 赋0,想象图像是无限长的图像的一部分,除了我们给定值的部分,其他部分的像素值都是0
  • ③ 赋边界值,想象图像是无限制长,但是默认赋值的不是0而是对应边界点的值

示例

#include<opencv2/opencv.hpp>
#include<stdc++.h>
using namespace cv;
using namespace std;

int main() {
  Mat a, b;
  a = imread("D:\\壁纸\\带带大师兄.jpg");
  if (!a.data) {
    cout << " 没有找到图片";
    waitKey(0);
    return -1;
  }
  imshow("清晰的孙笑川", a);
  
  blur(a, b, Size(9, 9));
  imshow("模糊的孙笑川", b);
  
  waitKey(0);
  return 0;
}

运行结果如下:

高斯模糊

均值模糊就是通过求平均值来进行模糊操作,这样的结果会使我们最后得到的图像不够平滑,而且更加容易受到噪声的干扰。

而高斯模糊则是通过线性滤波方式进行去噪,通过权重的方式来进行平均值的计算,什么意思呢?让我们看下图:

图中的数字代表每个像素点的像素值,这是让我们引入一个公式:

这是二维高斯函数以此来表明在正态分布上每个像素点的权重,

通俗点讲就是越靠近锚点的权重越大,越远离锚点的权重越小。通过加权平均数我们可以得到更平滑的模糊图像。

openCV给出了高斯模糊的api:

void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
                                double sigmaX, double sigmaY = 0,
                                int borderType = BORDER_DEFAULT );

src:输入图像

dst:输出图像

ksize:卷积核大小,这里我们要注意一下在使用高斯模糊时卷积核大小只能是奇数*奇数

sigmax:在X方向的标准差,通常情况下sigmax与sigmay的值是一样的,而且openCV允许在这两个值中填写0,这样openCV会自动算出每个点的权重。

sigmay:在Y方向的标准差

borderTypr:用于推断图像外部边缘像素,有默认值BORDER_DEFAULT。

示例

#include<opencv2/opencv.hpp>
#include<stdc++.h>
using namespace cv;
using namespace std;

int main() {
  Mat a, b;
  int height,width;
  a = imread("D:\\壁纸\\带带大师兄.jpg");
  if (!a.data) {
    cout << " 没有找到图片";
    waitKey(0);
    return -1;
  }
  imshow("清晰的孙笑川", a);
  GaussianBlur(a, b, Size(9, 9), 0, 0);
  imshow("模糊的孙笑川", b);

  waitKey(0);
  return 0;
}

运行结果如下:

为了让各位对均值模糊和高斯模糊有个更直观的认识所以我展示一下均值模糊和高斯模糊的对比:

  影流之主

使用openCV来完成图像的叠加

在openCV中我们可以使用以下函数来进行图像的叠加:

addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst);

接下来让我们来好好分析一下这个函数:

src1:需要叠加的第一个图像

alpha:src1图像在叠加时所占的权重

src2:需要叠加的另一个图像

beta:src2图像在叠加式所占的权重

gamma:叠加完后加的常数,你可以增加gamma的值让图片更亮

dst:叠加完后输出的图像

示例

#include<opencv2/opencv.hpp>
#include<stdc++.h>
using namespace cv;
using namespace std;

int main() {

  Mat a,b,c;
  /*读取图片*/
  a = imread("D:\\壁纸\\带带大师兄.jpg");
  if (!a.data) {
    printf("没有找到图片");
    return -1;
  }
  b = imread("D:\\壁纸\\星空.jpg");
  if (!b.data) {
    printf("没有找到图片");
    return -1;
  }	
  /*当大小一致时直接进行叠加*/
  if (a.size() == b.size()) {
    addWeighted(a, 0.4, b, 0.6,0, c);
    imshow("jun", c);
  }
  /*当大小不一致时先改变大小至一致再进行叠加*/
  else 
  {
    Mat bnew;
    int row, col;
    row = a.rows;
    col = a.cols;
    resize(b, bnew, Size(col, row));			
    addWeighted(a, 0.2, bnew, 0.8, 0, c);
    imshow("jun", c);		
  }
  
  waitKey(0);
  return 0;
}

在使用addWeighted()函数时要注意两张图片的大小必须相等,所以我们在进行叠加图片前要判断两者大小是否相等。这里介绍下resize()函数:

resize( InputArray src, OutputArray dst,Size dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR )

我们来分析一下这个函数:

src:输入图像

dst:输出图像

dsize:改变后图像的大小,如果选择填写此参数那么我们就要填写精确的数值,如Size(30,30),这就代表改变后的图像大小为30*30。如果你不选择填写此参数你可以填写0,那么你就需要填写fx、fy参数。

fx、fy:如果你无法确定被改变大小的图像要改变的确切数值你可以填写此参数,此参数会根据你填写的数值进行放大或缩小,如:

resize(jun,bian,Size(),2,2,INTER_LINEAR);

这样我们就能把jun这个图像按x轴放到2倍,按y轴放大2倍,并得到新的图像bian。

interpolation:此参数是用来选择使用什么插值法来进行变换图片大小。不论是放大或是缩小图像都需要插值运算,缩小图像时,目标图像的像素会映射为源图像中的多个像素,放大图像时,目标图形上的像素可能无法在源图像中找到精确对应的像素,都需要进行插值运算。

当然了,你可以将此参数空着,这样就会选择默认的双线性插值法。

以下为你可以选择的插值法:

1)INTER_NEAREST – 最近邻插值法
2)INTER_LINEAR – 双线性插值法(默认)
3)INTER_AREA – 基于局部像素的重采样(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。
4)INTER_CUBIC – 基于4×4像素邻域的3次插值法
5)INTER_LANCZOS4 – 基于8×8像素邻域的Lanczos插值

使用resize()函数我们就能让a图像和b图像大小一致了,接下来就是使用addWeighted()函数进行叠加操作了。

让我们来运行一下试试:

我们可以看到两幅图片叠加到一起了!是不是觉得很儒雅随和呢?

 

openCV:Mat对象详解和使用

Mat对象

Mat是OpenCV中用来存储图像信息的内存对象,可以理解为一个包含所有强度值的像素点矩阵,另外包含其他信息(宽,高,类型,纬度,大小,深度等)。

当你使用Mat对象时会自动为你分配内存空间,不用再像openCV2.x.x版本之前一样使用IplImage还要自己分配内存空间。

Mat对象使用

首先是最简单的使用:

Mat a;
a = imread("...");

定义一个Mat对象a后使用imread函数读取一张图片。

我们还能这样使用Mat:

Mat a,b;
a=imread("...");
b=Mat(a.size(),a.type());

首先定义一个Mat对象a并读取一张图片后定义一个Mat对象b,并且这个对象b的大小和格式与a是一样的。

Mat a,b;
a=imread("...");
b=Mat(a.size(),a.type());
b=Scalar(255,255,255);

因为我们刚才只是定义了一个Mat对象b的框架所以我们可以使用Scalar函数来让b这个对象生成一张图片,图片的颜色为白色(因为三原色的值都为255所以是白色)。

Mat a,b;
a=imread("...");
b=a.clon();

如果我们想要完全复制一张照片要怎么办呢?这时我们就可以使用clon()这个函数了,clon()是全复制你不需要担心因为改变了对象a的数值而会导致对象b的数值随着a改变而改变。

Mat a,b;
a = imread("..");
a.copyTo(b);

copyTo()函数和clon()函数也是一样的效果,你可以根据自己的喜好去使用。

Mat a,b;
a = imread("..");
cvtColor(a,b,COLOR_BGR2GRAY);

cvtColor()函数可以将a复制到b并且以COLOR_BGR2GRAY的形式展现给你,在这里COLOR_BGR2GRAY是参数名称灰度图的意思。你可以根据需要使用其它参数。

Mat a(3,3,CV_8UC3,Scalar(50,100,200);
cout<<a<<endl;
namedWindow("test",CV_WINDOW_AUTOSIZE);
imshow("test",a);

我们同样可以使用Mat对象创建一个x*x的矩阵,并根据参数CV_8UC3来为它每个像素点来设置数值为(50,100,200)。需要解释一下的是C3代表的是Channel 3,意味着我们这张图片每个像素点有3个通道分别对应的是三原色(RGB),而后面的数值(50,100,200)就是三原色的数值(当然你可以根据需要自己变)。

除了三通道还有一通道,也就是灰度图(还记得上文哪有提到过🐎?)

Mat a = Mat::eye(5, 5, CV_8UC1);

我们可以通过上面这种形式来对对象a进行初始化,这里的eye代表最后出现的矩阵是以对角线形式出现的

你可以修改参数为zeros,这样它就会把矩阵通通初始化为0:

 

Mat对象差不多就讲到这儿了,光看没有用建议打开电脑自己也来操作一遍这样印象才深刻嗷!

如何在openCV中使用掩膜来完成对图像的处理

什么是掩膜?

在本篇文章开始前我们需要先来了解一下什么是掩膜,掩膜就是用选定的图像、图形或物体,对处理的图像(全部或局部)进行遮挡,来控制图像处理的区域或处理过程。用于覆盖的特定图像或物体称为掩模或模板。

掩膜通常是个n*n的矩阵,我们可以通过掩膜来更好地处理图像。

代码解析

首先贴上源码,关于代码的解析我已经写好了注释:

#include<opencv2/opencv.hpp>
#include<stdc++.h>

using namespace std;
using namespace cv;
int main() {
  Mat jun = imread("D:\\壁纸\\jun.png");//读取图片
  namedWindow("junbian", CV_WINDOW_AUTOSIZE);//创建窗口
  imshow("junbian", jun);//在窗口上显示图片

  Mat bian;
/* 创建一个掩膜 */
  Mat kernel = (Mat_<char>(3,3)<<0, -1,  0,
                              -1, 5, -1, 
                               0,-1, 0);
/* 使用filter2D来对图像进行处理 */
  filter2D(jun, bian, -1, kernel);

  namedWindow("cool jun", CV_WINDOW_AUTOSIZE);
  imshow("cool jun", bian);

  waitKey(0);
  return 0;
}

让我们来看看结果:

我们会发现处理过后的图像相较于原图对比度增强了。

接下来让我们来换一个掩膜试试看:

#include<opencv2/opencv.hpp>
#include<stdc++.h>

using namespace std;
using namespace cv;
int main() {
  Mat jun = imread("D:\\壁纸\\jun.png");//读取图片
  namedWindow("junbian", CV_WINDOW_AUTOSIZE);//创建窗口
  imshow("junbian", jun);//在窗口上显示图片

  Mat bian;
/* 创建一个掩膜 */
  Mat kernel = (Mat_<char>(3,3)<<-1, 0, -1,
                                  0, 4, 0, 
                                  -1,0,-1);
/* 使用filter2D来对图像进行处理 */
  filter2D(jun, bian, -1, kernel);

  namedWindow("cool jun", CV_WINDOW_AUTOSIZE);
  imshow("cool jun", bian);

  waitKey(0);
  return 0;
}

运行后的结果:

我们可以看到图像的轮廓被描绘了出来,在这里我们使用的掩膜是拉普拉斯滤波器,它可以将画面轮廓清晰地描绘出来。