在windows下使用批处理脚本时,遇到了这样一个问题,FOR循环内部的变量设置出错:当全局存在同名变量时,echo该变量得到全局变量的值,当全局不存在该变量时,echo显示出来该变量为空。以下述bat代码为例:
@echo off rem variable expansion problem FOR /L %%i in (1, 1, 3) do ( set /a square=%%i*%%i echo "square(%%i) = %square%" ) pause @echo on ------------------------------- Result: "square(1) = " "square(2) = " "square(3) = "
查阅资料Environment Variable Expansion发现,bat脚本执行过程会发生变量替换。cmd.exe每读取一条命令时,会先将变量替换成变量的值,然后再执行该命令。 然而吊诡的是,只有一整条IF/FOR语句才会被当成一条完整的语句,因而IF/FOR内部赋值操作在时间上后于echo语句中变量替换操作。
解决问题的方法是:变量延迟,即在FOR/IF语句前使用setlocal EnableDelayedExpansion
语句,并且在引用变量时采用!var_name!的形式,即可解决这个问题,如下代码所示。据说,这是新手面对bat脚本最坑爹的问题之一。
@echo off rem variable expansion problem setlocal EnableDelayedExpansion FOR /L %%i in (1, 1, 3) do ( set /a square=%%i*%%i echo "square(%%i) = !square!" ) pause @echo on ------------------------------- Result: "square(1) = 1" "square(2) = 4" "square(3) = 9"
unsigned long int为C/C++长整型,其长度至少为32bit。在32bit系统其长度均为32bit,但是在64bit系统中,其长度与编译器实现有关,有些编译器上其长度为64bit,有些编译器上其长度为32bit,故在使用时需要引起注意。我们使用如下代码测试在mingw64(win10系统)和gcc(linux系统)中,相关类型的长度:
#includeint main(void) { printf("sizeof(unsigned int):%d\n", sizeof(char *)); printf("sizeof(unsigned long int): %d\n", sizeof(unsigned long int)); printf("sizeof(unsigned long long): %d\n", sizeof(unsigned long long)); printf("sizeof(char *): %d\n", sizeof(char *)); return 0; }
结果表明:
因而在实际开发中,准确不同类型的长度能够避免不必要的bug出现,当然为了跨平台跨编译器的一致性,我们最好使用uint32_t,uint64_t类型表示长整型。
在高级数据库课程实验中使用c语言的文件读写接口时遇到过这样一个bug:程序在开始时创建一个初始数据库文件,通过fwrite接口按页大小写回页面,这些页面包含目录页和数据页,如下图所示。程序在windows上遇到这样的问题,创建好初始文件后,发现第一页目录页多出来4字节的数据,后续页面也有相应的额外数据,但是在linux系统下却没有这个问题。
FILE * dbf = fopen(filename.c_str(), "w+"); // open the database file, should open with mode "wb+" if(dbf == NULL) { cout << "Create file failed " << endl; } uint32_t data_page_cnt = 0; char * buf = new char[FRAMESIZE]; auto dir_page = (Page*) buf; //materialize all the directory page for(int i = 0; i < DIRSIZE; i++) { for(int j = 0; j < PAGE_DIR_ENTRYS; j++) { dir_page->tuples[j] = data_page_cnt >= MAXPAGES ? 0 : DIRSIZE + data_page_cnt++; } dir_page->tuple_num = PAGE_DIR_ENTRYS; fwrite(buf, 1, FRAMESIZE, dbf); }
    经过调试发现,是因为创建数据文件使用了"w+" mode,这表示文件以文本形式打开,故在window系统下,读写文件接口会额外做这样一件事情:将数据中的"0A"全部替换成"ODOA",即windows下的换行符"\cr\n",而在linux系统中不会插入额外数据。因此修改程序的打开mode为"wb+"之后,程序不在出现类似的bug。
OpenMP提供了一系列轻量的并行编程模型, 方便我们将局部的串行任务并行化。 最简单的并发编译制导语句有#pragma omp parallel for
,该语句下面可以接一段不存在值依赖for循环语句,openMP启动多个子线程切分上述循环,达到并行执行for循环的效果。 程序员可在串行代码处使用 omp_set_num_threads()接口
设置for循环执行的线程数量。
在某个项目开发过程中,本人发现了在一段代码中无论怎么设置openMP线程数量,for循环仍然是串行执行的,使用omp_get_num_threads()永远返回1。 最后通过调试定位到了问题所在: 项目编译分为两个部分——源文件编译和obj文件链接。 在obj文件链接时,使用了-fopenmp选项,但是源文件编译时忘了开启该选项,导致obj文件永远生成的是串行的版本。
因此本次bug得到的启示是,当使用openMP进行并行编程时,无论是编译还是链接过程,都需要显式指定-fopenmp选项