C语言面试笔试||rtthread面试笔试全家桶

news2024/10/6 18:31:17

目录

1.rtthread内核框架与线程调度介绍

2.rtthread内核功能启动流程及汇编阶段

3.rtthread内存分布

4.内核对象管理系统

5.内核配置和剪裁

6.线程5种状态

7.进程与线程

8.CPU的内部结构

9.中断的优缺点

10.GPIO的输入输出模式

11.tcp为什么需要3次握手?第3次握手去掉可以不?

12.系统定时器初始化简介

13.同步机制

信号量同步实例

互斥量实例

事件集实现线程同步

14.IO设备模型及实例

15.中断接收方式分析及串口中断接收

16.应用层串口编程实例

17.串口DMA接收数据

18.ADC访问接口实例

19.I2C时序、协议、总线接口

20.线程间通信--邮箱/队列实例

21.队列来实现线程间的通信

c语言笔试题


1.rtthread内核框架与线程调度介绍

有内核库和实时内核(对象管理,实时调度器,线程管理,线程间通信,内存管理,时钟管理,设备管理)libcpu芯片移植,硬件(CPU/RAM/Flash/UART/EMMC等)

内存最小的资源占用情况是3kb rom 1.2kb ram

2.rtthread内核功能启动流程及汇编阶段

启动文件--——rtthread_startup()启动函数【——各种初始化(有系统有关的硬件,定时器,调度器,信号,创建main线程对各个模块依次初始化,启动调度器scheduler,根据规则选择就绪对列优先级最高的线程开始运行,启动定时器线程和空闲线程)】——main()

3.rtthread内存分布

rom{程序占用的flash空间}code代码段、 RO-data只读数据段【存放程序中定义的常量,像const类型】、RW-data读写数据段【存放初始化为非0的全局变量】 ZI-data【0数据段,存放未初始化的全局变量及初始化为0的变量】)

ram

keil文件编译完会生成.map文件——说明各个函数占用的尺寸和地址

烧写可执行程序到stm32后,内存分布会发生变化,stm32上电后默认从flash处启动,启动后把rom中的RW-data搬到RAM中,cpu执行代码从flash中读取,编译器根据给出的ZI地址和大小分配出ZI字段,并将RAM区域清0,这块会变为动态内存堆

4.内核对象管理系统

rtt内核对象有:线程,信号量,互斥量,事件,邮箱,消息队列,定时器,内存池,设备驱动等

对象容器收集了每类内核对象的信息【对象类型,大小等】,对象容器会给每类内核对象分配一个来链表,所有内核对象都会被链接到该链表上。

总结:rtt采用内核对象管理系统来访问/管理所有的内核对象

5.内核配置和剪裁

rtt:高度可裁剪性,在rtconfig.h中,打开/关闭宏定义来对条件进行编译,达到对内核进行精细调整,对组件进行灵活拆卸,达到系统配置和裁剪的目的

常见宏定义

RT_USED  RT_UNUSED  RT_WEAK【用于连接函数,编译器在链接函数时会优先链接没有该关键字前缀的函数】

ALIGN【作用是给对象分配地址空间时,将其存放的地址按照n字节对齐,n可取2的幂次方】

字节对齐:便于cpu快速访问,合理利用有效节省存储空间

6.线程5种状态

初始——就绪——运行——挂起——关闭

空闲线程:优先级最低,永远为就绪状态,不被挂起,作用【回收被删除的线程资源{僵尸线程}】

线程优先级相同,时间片轮转调度,单位一个时钟节拍

rt_thread_yield():当前线程被换出,相同优先级的下一个就绪线程将被执行。

rt_schedule():当前线程并不一定被换出,而是在系统中选取就绪的优先级最高的线程执行。

创建线程(静态)-----占用RAM空间(RW/ZI 空间),用户分配栈空间和线程句柄:

好处:运行时不需要动态分配内存,运行时效率较高,实时性较好,但内存不能被释放

参考:RTT内核介绍

rthread知识点总结

RT-Thread— 知识点总结(RTT认证+面试题汇总)

7.进程与线程

b,KB,MB

硬盘——抽屉    程序——菜谱  一个可执行程序就是进程

多个程序在内存是进程,进程里面有线程1、线程2 ....

线程是并行的最小单位

8.CPU的内部结构

ARM

控制单元(指令寄存器、指令译码器、操作控制器)

运算单元(算术逻辑单元)

存储单元(专用寄存器与通用寄存器) 

时钟

9.中断的优缺点

优点:实现CPU与I/O设备的并行,提高CPU的利用率和系统的性能

缺点:中断处理过程需要保护现场,恢复现场,整个过程需要一定的时间和空间开销,如果中断频率太高,会降低系统的性能

SRAM:静态的随机存储器 【加电下不需要刷新,数据不会丢失,CPU的缓存就是SRAM】

DRAM:动态的随机存储器 【加电的情况下需要不断刷新才能保存数据,最常见的系统内存】

SDRAM:同步动态随机存储器【数据的存储需要时钟来同步,也可用作内存】

10.GPIO的输入输出模式

输入:浮空输入、带上拉输入、带下拉输入、模拟输入

输出:开露输出、推挽输出、开漏复用输出、推挽复用输出

UART、SPI是半双工类型

11.tcp为什么需要3次握手?第3次握手去掉可以不?

第三次握手是为了防止已经失效的连接请求报文突然传输到了服务器,从而建立错误的连接,浪费资源还能防止发生死锁。若服务器发出第二次握手而客户端没有收到,服务器开始传输数据此时客户端便不会理会,导致服务器以为丢包而源源不断地发送数据包,造成死锁。

线程辅助函数 调度器钩子函数

12.系统定时器初始化简介

rtthread定时器:单次触发定时器  周期触发定时器

根据超时函数所处的上下文环境(超时时间到,cpu运行),定时器又可以分为HARD_TIMER模式【默认:在中断的上下文中的执行方式决定了定时器的超时函数不应该调用任何会让当前上下文挂起的系统函数,也不能执行非常长的时间,否则会导致其它中断的响应时间加长或抢占了其它线程执行的时间】与SOFT_TIMER模式

定时器工作机制

当前系统经过tick时间rt_tick(当硬件定时器中断来临时,它将+1),系统创建并激活定时器按照以超时时间排序的方式插入到rt_timer_list链表中(定时器链表rt_timer_list)

13.同步机制

多个执行单元(线程,中断)同时执行临界区,操作临界资源,会导致竞态问题,rtthread提供了3种同步机制

信号量同步实例

//信号量 LED闪烁--信号量控制 信号量的一个应用
#define LED_PIN GET_PIN(F,9)
static rt_sem_t led_sem = RT_NULL;
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

static void sem_entry(void *parameter)
{
	while(1)
	{
		rt_sem_release(led_sem);
		rt_thread_mdelay(500);
	}
}

static void led_entry(void *parameter)
{
	static unsigned char cnt = 0;
	while(1)
	{
		
		if(rt_sem_take(led_sem,RT_WAITING_FOREVER)==RT_EOK)
		{
		if(cnt++ %2)
		{
			rt_pin_write(LED_PIN,PIN_HIGH);
		}else{
			rt_pin_write(LED_PIN,PIN_LOW);
		}
		rt_thread_mdelay(1000);
		rt_sem_release(led_sem);
		}
	}

}
int led_sample(void)
{//1表示初始计数值,1表示该信号量可用,当被用时会-1,为0,表示不可用,需等待释放为1后才能使用
	led_sem=rt_sem_create("led_sem",1,RT_IPC_FLAG_FIFO);
	if(led_sem == RT_NULL)
	{
		rt_kprintf("create led sem failed!\n");
		return -RT_ERROR;
	}
	//512线程zai空间,存储线程的局部变量,函数调用信息和其它运行时的数据
	//20线程的优先级,较高的优先值表示较高的优先级
	//0线程的标志位,指定线程的属性和行为,0表示没有设置任何特殊的标志位
	tid1 = rt_thread_create("ctl_sem",sem_entry,RT_NULL,512,20,0);
	if(tid1 != RT_NULL)
	{
		rt_thread_startup(tid1);
	}
	
	tid2 = rt_thread_create("ctl_sem",led_entry,RT_NULL,512,25,0);
	if(tid2 != RT_NULL)
	{
		rt_thread_startup(tid2);
	}
}
INIT_APP_EXPORT(led_sample,led dample)

互斥量实例

//互斥量实例
#include <rtthread.h>

/* 定义互斥量 */
static rt_mutex_t mutex;

/* 线程 1 */
static void thread1_entry(void* parameter)
{
    /* 获取互斥量 */
    rt_mutex_take(mutex, RT_WAITING_FOREVER);

    /* 在临界区执行相关操作 */
    rt_kprintf("Thread 1 is in critical section\n");

    /* 释放互斥量 */
    rt_mutex_release(mutex);
}

/* 线程 2 */
static void thread2_entry(void* parameter)
{
    /* 获取互斥量 */
    rt_mutex_take(mutex, RT_WAITING_FOREVER);

    /* 在临界区执行相关操作 */
    rt_kprintf("Thread 2 is in critical section\n");

    /* 释放互斥量 */
    rt_mutex_release(mutex);
}

int main(void)
{
    /* 创建互斥量 */
    mutex = rt_mutex_create("my_mutex", RT_IPC_FLAG_FIFO);

    /* 创建线程 1 */
    rt_thread_t thread1 = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, 10, 5);
    if (thread1 != RT_NULL)
    {
        rt_thread_startup(thread1);
    }

    /* 创建线程 2 */
    rt_thread_t thread2 = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, 10, 5);
    if (thread2 != RT_NULL)
    {
        rt_thread_startup(thread2);
    }

    /* 启动 RT-Thread 调度器 */
    rt_thread_mdelay(100);
    rt_thread_scheduler_start();

    return 0;
}

事件集实现线程同步

//线程间通信--事件
#include <rtthread.h>
#define EVENT_FLAG_A (1<<0)  //1左移0位,既不进行位移操作,得到值1
#define EVENT_FLAG_B (1<<1)  //1左移1位,得到2,可用于表示不同的事件标志
static rt_event_t event;

void thread1_entry(void *parameter)
{
	//等待事件标志A  表示等待事件标志A触发  设置等待行为的标志,接收后清除已触发的
	//事件标志,RT_NULL表示接收事件标志指针,表示不需要接收触发的事件标志
	rt_event_recv(&event,EVENT_FLAG_A,RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,RT_NULL);
	
	//执行相应的操作
	rt_kprintf("thread1 : event A received.\n");
	rt_thread_mdelay(1000);
	
}

void thread2_entry(void *parameter)
{
	rt_event_recv(&event,EVENT_FLAG_B,RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,RT_NULL);
	rt_kprintf("thread2 : Event B received.\n");
	rt_thread_mdelay(1000);
}
void event_example(void)
{
	//创建事件
	event = rt_thread_create("event",RT_IPC_FLAG_FIFO);
	if(event==RT_NULL)
	{
		rt_kprintf("failed to create.\n");
		return;
	}
	//创建线程1
	rt_thread_t thread1=rt_thread_create("thread1",thread1_entry,RT_NULL,1024,10,10);
	if(thread1 !=RT_NULL)
	{
		rt_thread_startup(thread1);
	}
	
	rt_thread_t thread2=rt_thread_create("thread2",thread2_entry,RT_NULL,1024,20,10);
	if(thread2 !=RT_NULL)
	{
		rt_thread_startup(thread2);
	}
	while(1)
	{
		rt_event_send(&event,EVENT_FLAG_A);
		rt_thread_mdelay(2000);
		rt_event_send(&event,EVENT_FLAG_B);
		rt_thread_mdelay(2000);
	}

}

参考文章:

【玩转RT-Thread】线程间同步(一) 信号量

RT-Thread 内核学习

14.IO设备模型及实例

#include <rtthread.h>
#include <rtdevice.h>

int main(void)
{
    rt_device_t dev;

    /* 查找设备 */
    dev = rt_device_find("uart1");
    if (dev == RT_NULL)
    {
        rt_kprintf("Device not found!\n");
        return -1;
    }

    /* 打开设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
    {
        rt_kprintf("Failed to open device!\n");
        return -1;
    }

    /* 向设备写入数据 */
    char buffer[] = "Hello, RT-Thread!";
    rt_device_write(dev, 0, buffer, sizeof(buffer));

    /* 从设备读取数据 */
    char read_buffer[32];
    rt_device_read(dev, 0, read_buffer, sizeof(read_buffer));

    /* 关闭设备 */
    rt_device_close(dev);

    return 0;
}

15.中断接收方式分析及串口中断接收

#include <rtthread.h>
#include <rtdevice.h>

#define UART_NAME       "uart1"
#define BUFFER_SIZE     64

static char rx_buffer[BUFFER_SIZE];

static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
    /* 从串口接收数据到缓冲区 */
    rt_device_read(dev, 0, rx_buffer, size);

    /* 处理接收到的数据,例如打印到控制台 */
    rt_kprintf("Received data: %s\n", rx_buffer);

    return RT_EOK;
}

int main(void)
{
    rt_device_t dev;

    /* 查找串口设备 */
    dev = rt_device_find(UART_NAME);
    if (dev == RT_NULL)
    {
        rt_kprintf("UART device not found!\n");
        return -1;
    }

    /* 打开串口设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
    {
        rt_kprintf("Failed to open UART device!\n");
        return -1;
    }

    /* 注册串口中断回调函数 */
    rt_device_set_rx_indicate(dev, uart_rx_ind);

    /* 进入主循环 */
    while (1)
    {
        /* 在主循环中处理其他任务 */
        rt_thread_mdelay(1000);
    }

    return 0;
}

16.应用层串口编程实例

#include <rtthread.h>
#include <rtdevice.h>

#define UART_NAME       "uart1"
#define BUFFER_SIZE     64

static char tx_buffer[BUFFER_SIZE];
static char rx_buffer[BUFFER_SIZE];

static void uart_thread_entry(void *parameter)
{
    rt_device_t dev;
    rt_err_t result;

    /* 查找串口设备 */
    dev = rt_device_find(UART_NAME);
    if (dev == RT_NULL)
    {
        rt_kprintf("UART device not found!\n");
        return;
    }

    /* 打开串口设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_RDWR) != RT_EOK)
    {
        rt_kprintf("Failed to open UART device!\n");
        return;
    }

    while (1)
    {
        /* 向串口发送数据 */
        rt_snprintf(tx_buffer, sizeof(tx_buffer), "Hello, RT-Thread!\n");
        result = rt_device_write(dev, 0, tx_buffer, rt_strlen(tx_buffer));
        if (result != rt_strlen(tx_buffer))
        {
            rt_kprintf("Failed to send data!\n");
        }

        /* 从串口接收数据 */
        result = rt_device_read(dev, 0, rx_buffer, sizeof(rx_buffer) - 1);
        if (result > 0)
        {
            rx_buffer[result] = '\0';
            rt_kprintf("Received data: %s\n", rx_buffer);
        }

        /* 延时一段时间 */
        rt_thread_mdelay(1000);
    }
}

int main(void)
{
    rt_thread_t thread;

    /* 创建串口线程 */
    thread = rt_thread_create("uart", uart_thread_entry, RT_NULL, 1024, 10, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        rt_kprintf("Failed to create uart thread!\n");
    }

    /* 进入主循环 */
    while (1)
    {
        /* 在主循环中处理其他任务 */
        rt_thread_mdelay(1000);
    }

    return 0;
}

17.串口DMA接收数据

#include <rtthread.h>
#include <rtdevice.h>

#define UART_NAME       "uart1"
#define BUFFER_SIZE     64

static char rx_buffer[BUFFER_SIZE];

static void uart_dma_rx_callback(rt_device_t dev, rt_size_t size)
{
    /* 处理接收到的数据,例如打印到控制台 */
    rt_kprintf("Received data: %s\n", rx_buffer);
}

int main(void)
{
    rt_device_t dev;
    rt_err_t result;

    /* 查找串口设备 */
    dev = rt_device_find(UART_NAME);
    if (dev == RT_NULL)
    {
        rt_kprintf("UART device not found!\n");
        return -1;
    }

    /* 打开串口设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_DMA_RX) != RT_EOK)
    {
        rt_kprintf("Failed to open UART device!\n");
        return -1;
    }

    /* 配置串口 DMA 接收参数 */
    result = rt_device_control(dev, RT_DEVICE_CTRL_UART_SET_RX_DMA, RT_NULL);
    if (result != RT_EOK)
    {
        rt_kprintf("Failed to configure UART DMA!\n");
        return -1;
    }

    /* 注册串口 DMA 接收回调函数 */
    rt_device_set_rx_complete(dev, uart_dma_rx_callback);

    /* 启动串口 DMA 接收 */
    result = rt_device_control(dev, RT_DEVICE_CTRL_UART_RX_DMA_START, RT_NULL);
    if (result != RT_EOK)
    {
        rt_kprintf("Failed to start UART DMA receive!\n");
        return -1;
    }

    /* 进入主循环 */
    while (1)
    {
        /* 在主循环中处理其他任务 */
        rt_thread_mdelay(1000);
    }

    return 0;
}

18.ADC访问接口实例

#include <rtthread.h>
#include <rtdevice.h>

#define ADC_NAME        "adc1"

int main(void)
{
    rt_device_t dev;
    rt_err_t result;
    rt_uint32_t adc_value;

    /* 查找 ADC 设备 */
    dev = rt_device_find(ADC_NAME);
    if (dev == RT_NULL)
    {
        rt_kprintf("ADC device not found!\n");
        return -1;
    }

    /* 打开 ADC 设备 */
    if (rt_device_open(dev, RT_DEVICE_FLAG_RDONLY) != RT_EOK)
    {
        rt_kprintf("Failed to open ADC device!\n");
        return -1;
    }

    /* 读取 ADC 值 */
    result = rt_device_read(dev, 0, &adc_value, sizeof(adc_value));
    if (result != sizeof(adc_value))
    {
        rt_kprintf("Failed to read ADC value!\n");
        return -1;
    }

    /* 打印 ADC 值 */
    rt_kprintf("ADC value: %u\n", adc_value);

    /* 关闭 ADC 设备 */
    rt_device_close(dev);

    return 0;
}

19.I2C时序、协议、总线接口

20.线程间通信--邮箱/队列实例


//邮箱
#define MAILBOX_SIZE 5
static rt_mailbox_t mailbox;

void sender_thread_entry(void *parameter)
{
    rt_uint32_t count = 0;
    while(1)
    {
        //创建消息并发送到邮箱中:邮箱句柄,发送数据的地址,发送数据的大小
        if(rt_mb_send(mailbox,&count,sizeof(count))==RT_EOK)
        {
            rt_kprintf("sender:sent data %d\n",count);
            count++;
        }
        rt_thread_mdelay(1000);
    }
}

void receiver_thread_entry(void *parameter)
{
    rt_uint32_t data;
    while(1)
    {
        //从邮箱中接收消息 接收数据存放的地址,接收数据的大小
        if(rt_mb_recv(mailbox,&data,sizeof(data),RT_WAITING_FOREVER)==RT_EOK)
        {
            rt_kprintf("receiver:received data %d\n",data);
            //处理接收到的消息
        }
    }
}
int mailbox_example(void)
{
    mailbox = rt_mb_create("mailbox",MAILBOX_SIZE,RT_IPC_FLAG_FIFO);
    if(mailbox== RT_NULL)
    {
        rt_kprintf("failed to create mailbox.\n");
        return -1;
    }
    //最后一个10是线程的抢占式时间片,用于确定线程在多任务系统中的调度时间
    //线程在执行时被分配的时间片为10个周期,当线程的时间片用尽时,操作系统将中断线程的执行
    rt_thread_t sender_thread = rt_thread_create("sender",sender_thread_entry,RT_NULL,1024,10,10);
    if(sender_thread != RT_NULL)
    {
        rt_thread_startup(sender_thread);
    }
    
    rt_thread_t receiver_thread = rt_thread_create("receiver",receiver_thread_entry,RT_NULL,1024,20,10);
    if(receiver_thread != RT_NULL)
    {
        rt_thread_startup(receiver_thread);
    }
    return 0;
}

21.队列来实现线程间的通信

#include <rtthread.h>
#define QUEUE_SIZE 5
static rt_queue_t queue;
static rt_thread_t sender_thread;
static rt_thread_t receiver_thread;

void sender_thread_entry(void *parameter)
{
    rt_uint32_t count = 0;
    while(1)
    {    //创建消息并发送到队列中
        if(rt_queue_send(queue,&count,sizeof(count))==RT_EOK)
        {
            rt_kprintf("Sender:send data %d \n",count);
            count++;
        }
        rt_thread_mdelay(1000);
    }
}

void receiver_thread_entry(void *parameter)
{
    rt_uint32_t data;
    while(1)
    {
        if(rt_queue_recv(queue,&data,sizeof(data),RT_WAIYING_FOREVER)==RT_EOK)
        {
            rt_kprintf("Receiver:receiver data %d\n",data);
            //处理接收到的数据消息
        }
    }
}
int queue_example(void)
{   //队列的大小,队列中每个元素的大小
    queue = rt_queue_create("queue",QUEUE_SIZE,sizeof(rt_uint32_t),RT_IPC_FLAG_FIFO);
    if(queue == RT_NULL)
    {
        rt_kprintf("Failed to create queue.\n");
        return -1;
    }
    
    //创建发送线程
    sender_thread = rt_thread_create("sender",sender_thread_entry,RT_NULL,1024,10,10);
    if(sender_thread!=RT_NULL)
    {
        rt_thread_startup(sender_thread);
    }
    //创建接收线程
    receiver_thread = rt_thread_create("receiver",receiver_thread_entry,RT_NULL,1024,10,10);
    if(receiver_thread!=RT_NULL)
    {
        rt_thread_startup(receiver_thread);
    }
    return 0;
}

c语言笔试题

1.对于嵌入式系统来说,直接操作硬件寄存器代码,通常使用易失性(volatile)关键字
来声明寄存器地址映射的变量(对)

2.使用递归反向输出字符串的例子

//hello  olleh

#include "stdio.h"
#include "string.h"
char *reverse(char *str)   
{   
 if( !str )                     //判断输入字符串是否为空串
 {   
  return NULL;                  //若为空串则返回NULL
 }   
 
    int len = strlen(str);  
    if( len > 1 )   
    {   
        char ctemp =str[0];   
        str[0] = str[len-1];      
        str[len-1] = '\0';      // 最后一个字符在下次递归时不再处理   
        reverse(str+1);         // 递归调用   
        str[len-1] = ctemp;   
    }   
 
    return str;   
}
int main()
{
        char str[100];
        gets(str);
        reverse(str);
        puts(str);
        return 0;
}

3.(void *)ptr 和(*(void**))ptr


(void *)ptr 接受任何类型的指针变量,它仅仅接收的是变量的首地址
指针的类型是指明指针向后偏移多少个字节
*(void**)先解引用void*  ---(void *)ptr 所以它两表示的一样

4.在c语言中,结构体的成员是按照它们声明的顺序存储在内存中,内存对齐会导致结构体的成员在内存中的偏移量可能会增加(对)

5.

unsigned int a=6;
int b=-20;
a+b>6? 对的,-20 ----0xffffffec 默认会把有符合的数当成无符号的数计数,显然>6

6.单链表的建立,把'a'--'z'26个字母倒序插入到链表中,并且输出打印值
初始化一个链表,改变链表的指向,让z指向上一个
打印链表的值函数

typedef struct linklist
{
    char ff;
    struct linklist *next;
}Node;
typedef Node* Linklist;
int main()
{
    Listlist L,new;
    L = (Linklist)malloc(sizeof(Node));
    L->next =NULL;
    int i;
    for(i =0;i<26;i++) //L->next赋值给new->next,
        new->ff ='a'+i;  //
        new->next L->next;
        L->next =new;
    }
    while(L->next)
    {
        printf("%c ",L->next->ff);
        L =L->next;
    }
    printf("\n");
    return 0;
}

7.不用库函数,用c语言实现将一整型数字转化为字符串

123 -->"123"  -123  -->"-123" 
int a flag;
scanf("%d ",&a);
char str[100] = {0};
写一个函数实现该功能:

char inttostr(int num,char str)
{
    if(num<0) {num = -num;s[i++]='-';}
    if(num == 0) s[i++]='0';
    while(num>0) 
    {
        str[i++]=num%10+'0';
        n=n/10;
    }
    //反转字符串
    int start=0;int end =i-1;
    while(start<end)
    {
        char temp = str[start];
        str[start] = str[end];
        str[end] = temp;
        start++;
        end --;
    }
    str[i]='\0'    ;
}
int main(){
    
}


8.rtthread中环形缓冲区实例

//环形缓冲区
#include <rtthread.h>

#define BUFFER_SIZE 16

static rt_ringbuffer_t ringbuffer;
static char buffer[BUFFER_SIZE];

void producer_thread_entry(void *parameter)

{
    int count = 0;
    
    while (1)
    {
        /* 将数据写入环形缓冲区 */
        rt_ringbuffer_put(&ringbuffer, &count, sizeof(int));
        rt_kprintf("Producer: Write data %d\n", count);
        
        count++;
        
        /* 延时一段时间 */
        rt_thread_mdelay(1000);
    }
}

void consumer_thread_entry(void *parameter)
{
    int data;
    
    while (1)
    {
        /* 从环形缓冲区读取数据 */
        rt_ringbuffer_get(&ringbuffer, &data, sizeof(int));
        rt_kprintf("Consumer: Read data %d\n", data);
        
        /* 延时一段时间 */
        rt_thread_mdelay(2000);
    }
}

void ringbuffer_example(void)
{
    /* 创建环形缓冲区 */
    rt_ringbuffer_init(&ringbuffer, buffer, BUFFER_SIZE);
    
    /* 创建生产者线程 */
    rt_thread_t producer_thread = rt_thread_create("producer", producer_thread_entry, RT_NULL, 1024, 10, 10);
    if (producer_thread != RT_NULL)
    {
        rt_thread_startup(producer_thread);
    }
    
    /* 创建消费者线程 */
    rt_thread_t consumer_thread = rt_thread_create("consumer", consumer_thread_entry, RT_NULL, 1024, 20, 10);
    if (consumer_thread != RT_NULL)
    {
        rt_thread_startup(consumer_thread);
    }
}

9.rtthtread中定时器实例

#include <rtthread.h>
static rt_timer_t timer;
static void timeout(void *parameter)
{
	rt_kprintf("One-shot timer is timeout.\n");
}
int timer_example(void)
{
	timer = rt_timer_create("timer",timeout,RT_NULL,30,RT_TIMER_FLAG_ONE_SHOT|PERIODIC
	//periodic
	if(timer != RT_NULL)
	{
		rt_timer_start(timer);
	}
	else{
		LOG_E("rt_timer_create failed.\n");
		return -RT_ERROR;
	}
	return 0;
}

10.

scanf("%d",&num1) 输入语句
常量有哪些?
字面常量(100) const修饰的常变量(const float pai =3.14f;)
#define定义的标识符常量
枚举常量(enum 默认从0开始)

\\转义字符 ,用于表示一个反斜杠\,防止被解析为一个转义序列符
printf("%c\n",'\'');
printf("%s\n","\"");

c语言的死循环:
for(;;) while(1)

11.c语言字符类型


char short int long 
long long
float double
 

12.单链表的建立,把'a'--'z'26个字母倒序插入到链表中,并且输出打印值
初始化一个链表,改变链表的指向,让z指向上一个
打印链表的值函数
写法1:

typedef struct linklist
{
    char ff;
    struct linklist *next;
}Node;
typedef Node* Linklist;
int main()
{
    Listlist L,new;
    L = (Linklist)malloc(sizeof(Node));
    L->next =NULL;
    int i;
    for(i =0;i<26;i++) //L->next赋值给new->next,
        new->ff ='a'+i;  //
        new->next L->next;
        L->next =new;
    }
    while(L->next)
    {
        printf("%c ",L->next->ff);
        L =L->next;
    }
    printf("\n");
    return 0;
}


写法2:

#include <stdio.h>
#include <stdlib.h>
struct Node{
    char data;
    struct Node* next;
}
void insertNode(struct Node** head,char data)
{
    struct Node* newNode =(struct Node*)mallac(sizeof(struct Node));
    newNode->data =data;
    newNode->next =*head; //将新节点的指针指向当前链表的头节点,将新节点指向原本的头节点,使新节点成为新的头节点
    *head =newNode; //将链表的头指针指向新节点,这样可以修改指针head所指向的值,将其更新为新节点的地址
}
void printList(struct Node* head){
    struct Node* curr=head;
    while(curr!=NULL){
        printf("%c ",curr->data);
        curr = curr->next;
    }
    printf("\n");
}
int main(){
    struct Node* head =NULL;
    //插入‘a’到‘z’的26个字母倒序
    for(char c = 'z';c>='a';c--)
    {
        insertNode(&head,c);
    }
    printList(head);
    
}

13.

大端存储:数据的地位保存在内存的高地址中
全局变量在静态存储区,局部变量在堆载中
悬空指针:没有指向正确内存位置
当删除或释放对象时,如果不修改指针的值或置为NULL,就会出现悬空指针
野指针:只声明没有初始化的指针,它可以指向任何内存
int *const p 指针常量,指针在前,常量在后,p只能指向一个位置,而不能指向其它位置
,指像的变量值可以改变

参考文章:rtt时钟管理

时间复杂度和空间复杂度

C语言链表超详解

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

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

相关文章

基于粒子群优化算法的图象聚类识别matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于粒子群优化算法的图象聚类识别。通过PSO优化方法&#xff0c;将数字图片的特征进行聚类&#xff0c;从而识别出数字0~9. 2.测试软件版本以及运行结果展示 M…

It is also possible that a host key has just been changed

问题&#xff1a;ssh失败&#xff0c;提示如上图 分析: ssh的key存在上图里的路径里。 解决&#xff1a;win10删这个文件C:\Users\admin\.ssh\known_hosts , linux删这个文件.ssh\known_hosts ,或者删除这个文件里的制定ip的那一行&#xff0c;例如“106.1.1.22 ecdsa-sha2-…

SpringCloud-用nacos做服务注册与调用

步骤1&#xff1a;下载和安装Nacos 首先&#xff0c;你需要从Nacos的官方网站上下载并安装Nacos Server。根据你的操作系统选择合适的版本&#xff0c;并按照官方文档中的说明进行安装和配置。 步骤2&#xff1a;创建Spring Boot项目 在你喜欢的IDE中创建一个新的Spring Boot项…

VIMA:多模态提示的通用机器人操纵

机器人任务的表述有三种形式&#xff0c;分别是模仿one-shot演示、跟随语言指令、以及实现视觉目标。然而&#xff0c;这三种方式处理的任务不同&#xff0c;且模型也不同。基于提示的学习在自然语言处理领域展现了通用能力&#xff0c;单个模型可以处理各种各样的任务。VIMA是…

2024高频前端面试题 Vue2 和 Vue3 篇

* Vue2 和 Vue3的区别&#xff1a; 1&#xff09;双向数据绑定原理的区别 2&#xff09;根节点的不同 Vue2只能一个根节点 Vue3在组件中可以放置多个根节点 3&#xff09;Vue3中采用composition API vue2:采用的选项型API(opsition API) vue3:采用的组合型API(composition A…

2024第二次培训:win11系统下使用nginx、JDK、mysql搭建基于vue2、java前后端分离的web应用运行环境

一.背景 公司安排了带徒弟的任务&#xff0c;给培训写点材料。前面分开介绍了mysql、jdk、nginx的安装&#xff0c;都只是零星的介绍&#xff0c;只能算零散的学习。学习了有什么用呢&#xff1f;能解决什么问题&#xff1f;能完成什么工作&#xff1f; 今天我们要用之前的几篇…

蓝桥杯(3.5)

789. 数的范围 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int q sc.nextInt();int[] res new int[n];for(int i0;i<n;i)res[i] sc.nextInt();while(q-- ! 0) {int…

鱼哥赠书活动第⑩期:一本书讲透ChatGPT,实现从理论到实践的跨越!大模型技术工程师必读

鱼哥赠书活动第⑩期&#xff1a; 内容简介&#xff1a;作者简介&#xff1a;读者对象&#xff1a;直播预告&#xff1a;购书链接&#xff1a;赠书抽奖规则:往期赠书福利&#xff1a; OpenAI 在 2022 年 11 月推出了人工智能聊天应用—ChatGPT。它具有广泛的应用场景&#xff0c…

#QT(串口助手-实现)

1.IDE&#xff1a;QTCreator 2.实验 3.记录 &#xff08;1&#xff09;在widget.h中加入必要文件&#xff0c;并且定义一个类指针 &#xff08;2&#xff09;如果有类的成员不知道怎么写&#xff0c;可以通过以下途径搜索 &#xff08;2&#xff09;设置串口数据 void Widget…

【RISC-V 指令集】RISC-V 向量V扩展指令集介绍(一)-向量扩展编程模型

1. 引言 以下是《riscv-v-spec-1.0.pdf》文档的关键内容&#xff1a; 这是一份关于向量扩展的详细技术文档&#xff0c;内容覆盖了向量指令集的多个关键方面&#xff0c;如向量寄存器状态映射、向量指令格式、向量加载和存储操作、向量内存对齐约束、向量内存一致性模型、向量…

CAN总线位时序的介绍

CAN控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平&#xff0c;二者必居其一。发送方通过使总线电平发生变化&#xff0c;将消息发送给接收方。 显性电平对应逻辑 0&#xff0c;CAN_H 和 CAN_L 之差为 2.5V 左右。而隐性电平对应逻辑 1&#xff0c…

【Datawhale组队学习:Sora原理与技术实战】AIGC技术基础知识

AIGC是什么 AIGC全称叫做AI generated content&#xff0c;AlGC (Al-Generated Content&#xff0c;人工智能生产内容)&#xff0c;是利用AlI自动生产内容的生产方式。 在传统的内容创作领域中&#xff0c;PGC&#xff08;Professionally-generated Content&#xff0c;专业生…

HTTP协议与HTTPS协议

HTTP协议 HTTP协议是一个无状态的协议&#xff0c; 服务器不维护任何有关客户端之前所发请求的消息。 是一种懒政&#xff0c;有状态协议就会更加复杂&#xff0c;需要维护状态&#xff08;历史信息&#xff09;,要是客户或者服务器失效,会产生状态不一致(状态前后不对称),解决…

安装RabbitMQ及配置Centos7 方式(2)

1、背景需求 自行搭建学习参考使用&#xff0c;这里采用的Centos7 方式&#xff0c;这已经是多年前的方式了&#xff0c;现在主流方式是容器化安装、部署&#xff0c;docker、ks8&#xff0c;同学们可自行去学习参考。 2、搭建环境 环境&#xff1a;centos7 、otp_src_21.3、…

Redis中的单线程高性能原因和其他高级命令

单线程 Redis是单线程吗&#xff1f; Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程来完成的&#xff0c;这也是 Redis对外提供键值存储的主要流程。但Redis的其他功能&#xff0c;比如持久化、异步删除、 集群数据同步等&#xff0c;其实是由额外的线程执行的…

已经连接过github远程库,如何再次推送及删除远程库的内容

基于上次将文件推送到已经建好的github远程库上&#xff0c;此篇文章主要介绍如何再次推送文件去直接已经连接过的远程库&#xff0c;以此如何删除远程库中不想要的文件。 一、推送文件到远程库 1.将所需推送的文件拉入本地库所建的文件夹下&#xff1a;{ex&#xff1a;JVM相…

【剑指offer】C++ 翻转字符串里面的单词

目录 题目&#xff1a; 思路&#xff1a; 代码出现 结果 题目&#xff1a; 给定一个字符串&#xff0c;逐个翻转字符串中的每个单词。 示例 1&#xff1a; 输入: "the sky is blue" 输出: "blue is sky the" 示例 2&#xff1a; 输入: " hello…

电磁兼容(EMC):单、双面PCB板设计要点

目录 1 产品设计原则&#xff1a;性价比为第一要素 2 布局设计要点 3 布线设计要点 4 完整地平面不是最优方案 1 产品设计原则&#xff1a;性价比为第一要素 PCB在电磁兼容设计中通常是要求有完整的地和电源平面。但多层价格让对价格敏感的产品望而却步&#xff0c;只能采…

android开发者工具,最新整理

一 Java相关 1.重载函数的签名(区别是否是重载函数) 答&#xff1a;方法名参数类型参数顺序(返回值不是) 2.finalize的工作原理 答&#xff1a;一旦垃圾收集器准备好释放对象占用的存储空间&#xff0c;它首先调用finalize()&#xff0c;而且只有在下一次垃圾收集过程中&#…

Linux系统:内核参数调优

目录 1、/proc目录 2、sysctl命令 3.1 控制源路由验证 3.2 控制内核的系统请求调试功能 3.3 控制核心转储是否将PID附加到核心文件名 3.4 控制TCP同步cookie的使用 3.5 在网桥上禁用netfilter 3.6 控制消息队列的默认最大大小 3.7 调试TCP内核参数 3.8 调试套…