目录
一、C语言中的 #if()和 #end if 用法
1. #if 表达式 + 程序段 + #endif 形式
2. #ifdef标示符 + 标识符 + #endif 形式
3. #if 0/ #if 1 + #endif 形式
4. \可用于一行的结尾,表示本行与下一行连接起来
二、xTaskCreate函数
三、指针相关
1. 解引用
编辑2. 野指针
3.两个指针相减
4.数组通过指针来访问
5.结构体传参与打印:
C语言中的 #if()和 #end if 用法-CSDN博客https://blog.csdn.net/qq_24096023/article/details/85253645
#if 表达式
程序段1
#else
程序段2
#endif
表示:如果表达式为真,则编译程序段1,否则编译程序段2
表示:如果标示符已经被#define命令定义过,则编译程序段。 eg:
#define a 100
此时,我们要检查a是否定义(假设我们已经记不着这点了),或者我们要给a一个不同的值,就加入如下句子
#if defined a
#undef a
#define a 200
#endif
上述语句检验a是否被定义,如果被定义,则用#undef语句解除定义,并重新定义a为200
首先这里的0和1可以当做普通表达式来看待,1为真,0为假。
其次使用#if 0 有个很实用的方法就是当做注释来用。 有时候比用 // 和 /*..........*/ 整洁美观
比如用在调试代码的时候,code中定义的是一些调试版本的代码,此时code完全被编译器忽略。如果想让code生效,只需把#if 0改成#if 1
eg:
#include
int main(void)
{
int a = 0;
#if 0
a = 1;
#endif
printf("%d\n",a);
return 0;
}
C语言中以 ; 作为语句的结束,不以行为单位结束,当一行的内容太长不方便卸载一行时可使用反斜杠"\"作为继续符,分为多行书写
例如:STM32官方库文件"stm32f30x_usart.h"有如下一段:
#define IS_USART_123_PERIPH(PERIPH) (((PERIPH) == USART1) || \
((PERIPH) == USART2) || \
((PERIPH) == USART3))
写成一行意义完全相同:
#define IS_USART_123_PERIPH(PERIPH) (((PERIPH) == USART1) || ((PERIPH) == USART2) || ((PERIPH) == USART3
C语言中反斜杠"\"的意义和用法-CSDN博客https://blog.csdn.net/Wind4study/article/details/43502255
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, // 函数指针, 任务函数
const char * const pcName, // 任务的名字
const configSTACK_DEPTH_TYPE usStackDepth, // 栈大小,单位为word,10表示40字节
void * const pvParameters, // 调用任务函数时传入的参数
UBaseType_t uxPriority, // 优先级
TaskHandle_t * const pxCreatedTask ); // 任务句柄, 以后使用它来操作这个任务
参数描述:
pvTaskCode
函数指针,可以简单地认为任务就是一个C函数。
它稍微特殊一点:永远不退出,或者退出时要调用"vTaskDelete(NULL)"
pcName
任务的名字,FreeRTOS内部不使用它,仅仅起调试作用。
长度为:configMAX_TASK_NAME_LEN
usStackDepth
每个任务都有自己的栈,这里指定栈大小。
单位是word,比如传入100,表示栈大小为100 word,也就是400字节。
最大值为uint16_t的最大值。
怎么确定栈的大小,并不容易,很多时候是估计。
精确的办法是看反汇编码。
pvParameters
调用pvTaskCode函数指针时用到:pvTaskCode(pvParameters)
uxPriority
优先级范围:0~(configMAX_PRIORITIES – 1)
数值越小优先级越低,:更高优先级的、或者后面创建的任务先运行。
如果传入过大的值,xTaskCreate会把它调整为(configMAX_PRIORITIES – 1)
pxCreatedTask
用来保存xTaskCreate的输出结果:task handle。
以后如果想操作这个任务,比如修改它的优先级,就需要这个handle。
如果不想使用该handle,可以传入NULL。
返回值
成功:pdPASS;
失败:errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY(失败原因只有内存不足)
注意:返回值是pdFAIL不对。
pdFAIL是0,errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY是-1。
多个任务可以使用同一个函数;
void vTaskFunction( void *pvParameters )
{
const char *pcTaskText = pvParameters;
volatile uint32_t ul; /* volatile用来避免被优化掉 */
/* 任务函数的主体一般都是无限循环 */
for( ;; )
{
/* 打印任务的信息 */
printf(pcTaskText);
/* 延迟一会(比较简单粗暴) */
for( ul = 0; ul < mainDELAY_LOOP_COUNT; ul++ )
{
}
}
}
static const char *pcTextForTask1 = "T1 run\r\n";
static const char *pcTextForTask2 = "T2 run\r\n";
int main( void )
{
prvSetupHardware();
xTaskCreate(vTaskFunction, "Task 1", 1000, (void *)pcTextForTask1, 1, NULL);
xTaskCreate(vTaskFunction, "Task 2", 1000, (void *)pcTextForTask2, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
return 0;
}
char* a = "good";
char b[20] = "good";
a是指向第一个字符’g’的指针
b是指向字符数组第一个元素’g’的指针
二者看似相同,然而并非如此
字符串常量是放在常量区的,只可读。而字符数组是存在于栈中的,可以修改其数据
因此:
char* a = “helloworld”; “helloworld”是存放于常量区,因此只可读不可写
char a[20] = “helloworld”; “helloworld”存在于栈内,可读可写
char a[20] = “helloworld”;由于存放于栈中,因此读取速度比char* a = “helloworld”快
char * a = “helloworld”在编译时便已经确定了值
char a[20] = “helloworld”则是在运行时确定的
当执行char a[] = “helloworld”;时,系统将会分配11个字节的空间,最后一个字节存放’\0’,当调用strlen(a)时得到的值为10,因此strlen()不会将’\0’计算进去
故上文pcTextForTask1和2就是指向'T'的指针,或者说将字符串的地址存放在该变量中,这个变量是一个指针变量。
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,所以不管你存储的是int指针、float指针、long指针,对于存储指针的内存来说都是分配同样大小的内存的,这也为使用void指针可以存储任意类型的指针打下了基础,但是注意在使用void指针,要将其强制转换为具体的指针类型。
不同类型的指针需要注意如下调试结果:
注意: 这样能存下a的地址,虽然指针类型不同。
但是通过修改只能修改第一个字节:
他们主要的不同是地址+1,跳过的地址也不同:
指针类型决定了指针+1操作的时候,跳过了几个字节,即决定了指针的步长。
注意:pa的大小与a的大小无关:
(a是局部变量,p虽然能接收a的地址,但是a除了作用域销毁了,p变成野指针)
虽然还能打印出来,但是他与可能不是10,因为地址还在,只是有可能会被占用。只是这块空间不属于我的程序了,还给了操作系统,我只是没有当前空间的使用权限,但是这块内存空间还在。
如果不传递地址,会在内存中再开辟一个大小相同的空间,造成空间和时间的浪费,而传地址只会开辟大小是4/8个字节的空间,通过指针所指向的空间来打印。