当前位置: 首页 > article >正文

C语言基础--#if与#endif

目录

一、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 用法

C语言中的 #if()和 #end if 用法-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_24096023/article/details/85253645

1.   #if 表达式 + 程序段 + #endif  形式 

#if 表达式

    程序段1

#else

    程序段2

#endif

    表示:如果表达式为真,则编译程序段1,否则编译程序段2

2.   #ifdef标示符 + 标识符 +  #endif  形式

表示:如果标示符已经被#define命令定义过,则编译程序段。 eg:

#define a 100 
此时,我们要检查a是否定义(假设我们已经记不着这点了),或者我们要给a一个不同的值,就加入如下句子 
#if defined a 
#undef a 
#define a 200 
#endif 
上述语句检验a是否被定义,如果被定义,则用#undef语句解除定义,并重新定义a为200 

3. #if   0/  #if 1     +    #endif  形式

        首先这里的0和1可以当做普通表达式来看待,1为真,0为假。

        其次使用#if 0 有个很实用的方法就是当做注释来用。 有时候比用 // 和 /*..........*/ 整洁美观

        比如用在调试代码的时候,code中定义的是一些调试版本的代码,此时code完全被编译器忽略。如果想让code生效,只需把#if 0改成#if 1

eg:

#include <iostream>
 
int main(void)
{
    int a = 0;
    #if 0
    a = 1;
    #endif
    
    printf("%d\n",a);
    return 0;
}

  4.  \可用于一行的结尾,表示本行与下一行连接起来

        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博客icon-default.png?t=N7T8https://blog.csdn.net/Wind4study/article/details/43502255

二、xTaskCreate函数

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指针,要将其强制转换为具体的指针类型。

不同类型的指针需要注意如下调试结果:

三、指针相关

 1. 解引用

注意: 这样能存下a的地址,虽然指针类型不同。

但是通过修改只能修改第一个字节:

他们主要的不同是地址+1,跳过的地址也不同:

         指针类型决定了指针+1操作的时候,跳过了几个字节,即决定了指针的步长。

注意:pa的大小与a的大小无关:

2. 野指针

(a是局部变量,p虽然能接收a的地址,但是a除了作用域销毁了,p变成野指针)

         虽然还能打印出来,但是他与可能不是10,因为地址还在,只是有可能会被占用。只是这块空间不属于我的程序了,还给了操作系统,我只是没有当前空间的使用权限,但是这块内存空间还在。

 

 3.两个指针相减

4.数组通过指针来访问

5.结构体传参与打印:

         如果不传递地址,会在内存中再开辟一个大小相同的空间,造成空间和时间的浪费,而传地址只会开辟大小是4/8个字节的空间,通过指针所指向的空间来打印。


http://www.kler.cn/a/149919.html

相关文章:

  • [代码随想录打卡Day8] 344.反转字符串 541. 反转字符串II 54. 替换数字
  • ArcGIS软件之“计算面积几何”地图制作
  • 在 WPF 中,绑定机制是如何工作的?WPF数据绑定机制解析
  • macOS开发环境配置与应用开发(详细讲解)
  • JavaWeb合集23-文件上传
  • 【计网不挂科】计算机网络期末考试——【选择题&填空题&判断题&简述题】试卷(3)
  • 深入了解Spring Boot中@Async注解的8大坑点
  • ISCTF2023 部分wp
  • 网络安全 | 使用人工智能阻止网络攻击
  • 微服务实战系列之Redis(cache)
  • 行情分析——加密货币市场大盘走势(11.29)
  • 七、Lua字符串
  • 工艺系统所管理数字化实践
  • spark-submit
  • 靡靡之音 天籁之声 ——Adobe Audition
  • stm32 计数模式
  • Django路由分发
  • 荣耀IPO站上新起点:市场望眼欲穿,发展未来可期
  • Redis-Day1基础篇(初识Redis, Redis常见命令, Redis的Java客户端)
  • Sass基础知识之【变量】
  • 【送书活动二期】Java和MySQL数据库中关于小数的保存问题
  • Fuzz进阶教学——人工智能在模糊测试中的应用
  • Linux使用宝塔面板+Discuz+cpolar内网穿透工具搭建可公网访问论坛
  • nodejs669在线图书借阅管理系统vue前端
  • 第20章 多线程
  • Vue框架学习笔记——Vue实例中el和data的两种写法