预处理

基础

        预处理以#开头,到其后第一个换行符为止。也就是说,预处理的指令仅限一行。但可以使用反斜杠(\)和换行符将多个物理行,合成一个逻辑行。如:

#define TEST "tetafa\
fdafa"
那么在实际使用中TEST的值是:tetafafdafa。

        预处理指令可出现在源文件的任何地方,指令定义的作用域从定义出现的位置开始一直到文件的结尾。

#define

基础

        每一个#define行(逻辑行)由三部分组成。第一部分#define自身,第二部分为定义的缩略语,这些缩略语被称为宏,宏名中不允许有空格;第三部分为主体,即宏要替换的部分。从宏变成最终的主体,称为宏展开。

        宏可以表示任何字符串,也可以表示整个表达式。但预处理器不进行计算,只是按照指令进行文字替换。如下:

#define TEST 2+2
int main(int argc, const char * argv[]) {
    printf("%d",TEST * 4);
}

        使用宏替换后的计算式为2+2*4=10,而不是(2+2)*4=16。记住:宏只负责进行替换,不会在替换过程中进行任何计算

宏函数

        通过使用参数,可以创建外形和作用都与函数相似的函数宏。宏的参数也使用括号括起来,可以用圆括号括起一个或多个参数,随后这些参数出现在宏的主体部分。

#define TEST(x,y) x+y
#define PRD(x) printf("x = %d\n",x)
int main(int argc, const char * argv[]) {
    int x = 5;
    PRD(TEST(x, 6));//x = 11
}

        会将PRD,TEST进行替换,也就相当于printf("x = %d\n",x+6);。而且在宏中不要使用自增或自减运算符。

        在上面中,PRD(x)并没有将字符串中的x进行替换。如果想在字符串在使用宏参数,可以使用#符号。例如x是一个宏参数,那么#x可以把参数名转化为相应的字符串。如下:

#define PRD(x) printf(#x" = %d\n",x)
int main(int argc, const char * argv[]) {
    int x = 5;
    int y = 3;
    PRD(x+y);//x+y = 8
}
        首先将参数名转成为字符串,即将x+y转换成"x+y",那么printf(#x" = %d\n",x)将被转换为printf("x+y"" = %d\n",x+y),最后就成了printf("x+y = %d\n",x+y),所以输出为x+y = 8。

##

        将两个符号组合成一个符号。例如:

#define PRD(n) printf("x"#n"=%d\n",x##n)
int main(int argc, const char * argv[]) {
    int x1 = 3;
    PRD(1);//x1=3
}
        首先因为参数为1,所以printf()的第一个参数将会变成x1 = %d\n。而##会将x与n组合成一个,这里的n是1,所以结合之后就是x1。故PRD(1)就相当于printf("x1=%d\n",x1);。

可变宏

        有些函数的参数个数是可变的,也可将宏函数的参数个数设置为可变的。方法为:宏定义中的参数列表的最后一个参数为省略号,主体部分用__VA_ARGS__代替。如下:

#define PRD(n,...) printf("%s,%d\n",__VA_ARGS__,n);
int main(int argc, const char * argv[]) {
    PRD(1,"main");//main,1
}

        定义宏参数列表时使用了...,在替换部分用的是__VA_ARGS__。

#include与头文件

        引用头文件,并将头文件中的内容替换源代码中的#include指令。有两种指令形式:<>告诉预处理器在一个或多个系统目录中寻找文件;""告诉预处理器先在当前目录中寻找文件,然后在标准系统目录中寻找。

        #include中可以指定路径:如include "/user/biff/bi.h"就是搜索/user/biff目录。

        头文件内容最常见的形式包括:

        1,常量。

        2,宏函数。

        3,函数声明。将函数声明写在头文件中,函数实现放在.c文件中。

        4,结构体的定义。

        5,类型定义。使用typedef将一些复杂类型进行定义别名。

        6,将各个文件共享的外部变量声明在头文件中,省去别的文件写extern。例如有A,B两个文件,都共享一个变量intC,并且一方修改对另一方也可见。可以将C定义在A或B中,然后在一头文件中定义extern int C,并在B或A中引入该头文件即可。如果还有别的文件需要使用变量C,可直接引用头文件。

        7,将多个文件具有,但被const修饰,内部链接和文本作用域定义在头文件中。比如BASE_URL是多个文件需要的变量,且不可修改。那么可以在一个头文件中将它定义成static const的变量,使用该变量的各个文件直接include这个头文件即可。因为使用了static修饰,每个包含该头文件的文件都获得了一份该常量的副本

#undef

        用于取消一个由#define定义的宏。如#define LIMIT此时LIMIT被定义了,不能再使用LIMIT指代别的。#undef LIMIT会取消LIMIT的定义,此时就可以使用LIMIT指代别的。

        #undef只能取消由#define定义的宏名,不能取消变量。如下:

#define CSIZE 4  //首先对CSIZE进行定义
#undef CSIZE     //取消对CSIZE的定义
#define CSIZE "HAHAHAH" //将CSIZE定义成别的值
void test() {
	printf("%s\n", CSIZE);//输出的就是:HAHAHAH
}
        从这里也可以看出:#define的作用域从定义行开始,到#undef或文件结尾结束(哪个先出现以哪个为准)。

#indef,#ifndef,#else和#endif

        这些指令告诉编译器,根据编译时的条件接受或忽略某些代码块。

        #ifdef:如果预处理器已经定义了后面的标识符(即使用了#define),那么就会编译执行#ifdef与#else或#endif(两者谁先出现为谁为准)之间的代码。

        #else:#ifdef不满足时会编译执行#else与#endif之间的代码。但它不是必须的。

        #endif:结束整个条件编译,它是必须存在的。

        这三个预指令类似于if else语句,但由于预处理器不能识别标识代码块的{},所以必须使用#endif和#esle来标识代码块。如:

#define CSIZE "#defind了"
//#undef CSIZE
void test() {
	int a = 1;
	//#ifdef a  此时a是没有定义的,因为#ifdef只识别#define定义的标识符
#ifdef CSIZE
	puts("defined");
#else
	puts("undef");
#endif
}
        上述代码中,会输出defined。如果前#undef CSIZE的注释给除掉,就会输出undef。如果将#ifdef CSIZE换成#ifdef a,也会输出undef。

        因为#ifdef只接受#define定义的标识符,并不能识别变量

        #ifndef:与#ifdef类似,但它用来判断后面的标识符是否为未定义的。同样可以与#else,#endif连用。使用该预处理指令可以防止对同一个宏进行重复定义。例如:

#ifndef CSIZE
#define CSIZE "no def"
#endif
        首先判断CSIZE是否未定义,如果未定义就将它定义成no def。

#if与#elif

        #if更类似于常规的if else语句(#ifdef #else #endif只是执行过程类似),它可以将常量表达式做为判断条件,不单单是判断这个标识符是否定义过,还可以判断这个标签符定义的值

        #elif:就是if else中的else。而且,它们也需要与#endif连用。如:

#define A 1//代码二
void test() {
#if A == 1   //代码一
	puts("a 1");
#elif A == 2
	puts("a 2");
#else
	puts("a default");
#endif
}
        因为A的值是1,所以在代码一处判断是成立的,就会输出a 1。如果将A的值改为2,那么输出的就会是a 2。如果a的值没有定义,或者值不是1、2则会输出a default。

        由上面可以看出#if与#ifdef的区别:#ifdef只判断标识符是否定义过,不管定义的值是多少。而#if可以根据标识符的值选择不同的操作

        由于它们都是预处理器进行处理的,所以只能识别由#define定义的宏,而不能识别变量

预定义宏

        __LINE__:当前语句在文件中的行号。

        __FILE__:当前文件的全路径。

        __TIME__:当前的时间。格式为13:23:12,即hh:mm:ss。

        __DATE__:当前的日期。格式为:Apr 20 2016

        __func_:当前的函数名。

#line

        重置由__LINE__和__FILE__指定的行号和文件名。其中指定行号时,是指定下一行的行号,而不是本行的行号。如:

#line 200 "xxx.txt"
#line 300
	printf("%d,%s",__LINE__,__FILE__);//300,xxx.txt
        第二行中将下一行的行号置为了300,所以__LINE__代表的值是300;第一行将名置为xxx.txt,所以输出__FILE__时值为xxx.txt。



脑电预处理是脑电信号分析中重要的一环,以下是关于脑电预处理的方法、技术和流程的相关信息: ### 脑电预处理方法 - **独立成分分析(ICA)**:用于去除脑电数据中的伪迹。研究使用ICA去除数据中的伪迹,执行了四种策略的测试,包括不进行ICA预处理步骤、仅去除眼动伪迹、去除所有可靠识别的生理/非生理伪迹、仅保留可靠识别的大脑独立成分(ICs)。跳过眼动伪迹去除步骤会影响微状态评估标准的稳定性、微状态地形,并大大降低EO/EC条件下微状态特征比较的统计功效,但使用更为严格的预处理方法时,这些差异并不显著[^1]。 ### 脑电预处理技术 - **伪迹去除技术**:不同研究中的伪迹去除策略可能会改变微状态的地形和特征,从而影响研究结果的可推广性和可比性。ICA是常用的伪迹去除技术,能有效去除眼动等生理/非生理伪迹,以提高数据质量[^1]。 ### 脑电预处理流程 以独立成分分析预处理脑电微状态的研究为例,其流程包括: 1. **数据采集**:使用常规的静息态EEG数据集,其中包括受试者交替进行睁眼(EO)和闭眼(EC)两种状态的实验数据[^1]。 2. **预处理策略选择**:执行四种ICA预处理策略,分别为不进行ICA预处理步骤、仅去除眼动伪迹、去除所有可靠识别的生理/非生理伪迹、仅保留可靠识别的大脑独立成分(ICs)[^1]。 3. **数据分析与结果评估**:研究结果显示,跳过眼动伪迹去除步骤会产生不良影响,而数据质量好且去除眼动伪迹时,微状态地形和特征在不同预处理步骤下具有稳健性[^1]。 ```python # 以下为一个简单示意的脑电预处理流程代码示例(仅为概念示意,非完整可用代码) import mne # 读取脑电数据 raw = mne.io.read_raw_eeglab('your_eeg_data.set') # 进行独立成分分析 ica = mne.preprocessing.ICA(n_components=0.99, method='fastica') ica.fit(raw) # 去除眼动伪迹(示例) eog_inds, eog_scores = ica.find_bads_eog(raw) ica.exclude = eog_inds # 应用ICA到数据 raw_clean = ica.apply(raw) # 保存预处理后的数据 raw_clean.save('preprocessed_eeg_data.fif', overwrite=True) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值