有时,我们在阅读别人的代码时会看到这样的写法:
fwrite(buff, 1, 8912, fout); fread(buff, 1, 8912, fin);我就觉得奇怪,为什么不写成:
fwrite(buff, 8912, 1, fout); fread(buff, 8912, 1, fin);
感觉第二种写法会有效率一点。因为其一次就写完,而第一种需要写多次,每次只写一个字节。后来看多代码了,发现很多人都这样写的。我就觉得里面肯定有文章。用谷歌一搜,就发现已经有老外问到这个问题了。http://stackoverflow.com/questions/19410230/fread-fwrite-size-and-count 和 http://stackoverflow.com/questions/10564562/fwrite-effect-of-size-and-count-on-performance。
从回答中可以看到,第一种写完更合理一点。首先,实现fwrite和fread函数的人不是傻蛋,其不会实现为:每次只写一个字节,写8912次 。最重要的是,在第一种写法中可以知道写了/读了多少字节。特别是在读的时候,很有必要。如果是第二种写法,在读的时候,只能返回0或者1,根本就不知道究竟读了多少字节。
C++11越来越流行了,有必要学一下C++11。这里和这里可以看到各个编译器对C++11的支持。安装了支持C++11的编译器gcc,还要加上编译选项-std=c++11。编译命令如下:
g++ -std=c++11 test.cpp -o test
如果想在Windows下使用支持C++11的Mingw,可以到这里下载。
对于Qt Creator,这个编译选项是在.pro文件添加的,添加下面内容即可:
QMAKE_CXXFLAGS += -std=c++11
对于strspn,虽然有些文章进行了一些解说,但总是难于理解。自己写一个例子验证自己的理解,但结果并非自己预测的那样。所以我这里也来解说一下strspn,不过我是直接上strspn的实现代码。我就用glibc2.19中strspn的实现代码来解说。
size_t strspn (s, accept) const char *s; const char *accept; { const char *p; const char *a; size_t count = 0; for (p = s; *p != '\0'; ++p) { for (a = accept; *a != '\0'; ++a) if (*p == *a) break; if (*a == '\0') //遍历完accept就返回 return count; else ++count; } return count; //遍历完s也返回 }
代码里面有两个循环,分别用来遍历两个字符串。
*p是什么,*p是另外一个字符串s的一个字符。此时联合内外循环应该可以理解为:用*p从s的第一个字符开始遍历,如果在accept中能找到等于*p的字符,那么就++count, 测试s的下一个字符。如果在accept中找不的话,就直接return count。如果s的字符都能找accept中找到,那么就返回s的长度。
用一句简单的话来叙述,那就是:遍历字符串s,返回第一个只出现在s字符串的字符的下标值(在s中的下标)。
对于s=”126.com”,accept=”1234567890”,返回值为字符”.”的下标,即3
对于s=”126.com”,accept=”google.com”,返回值为字符”1”的下标,即0
strspn函数有什么用途呢?假如accept是一个删除字符集合,并且想删除字符串s前面的一些属于删除字符集合的字符。此时strspn函数返回值就是一个下标,在该下标之前的字符统统删掉。
函数strcspn和前面的strspn长得有点像,可以用strspn来帮助理解。下面给出它的实现代码。
size_t strcspn (s, reject) const char *s; const char *reject; { size_t count = 0; while (*s != '\0') if (strchr (reject, *s++) == NULL) ++count; else return count; return count; }
用一句简单的话来叙述,遍历字符串s,返回第一个既出现在s,也出现在reject字符的字符下标值(在s中的下标)。
对于s=”hao123.com”,reject=”1234567890”,返回值为字符”1”的下标,即3
在tr1中,C++加入了神器share_ptr,但不同的编译器把智能指针share_ptr 放在不同的头文件中。比如VS系列编译器把它放到memory中,而gcc(包括mingw)则放到tr1/memory头文件。由于mingw编译器在使用的时候,会自动加入WIN32这个宏,所以用这个宏来跨编译器使用share_ptr是不行的了。解决方案是使用宏_MSC_VER。这个宏是用来标明VS系统编译器版本的,当然在mingw中就不会存在的。所以可以像下面那样跨编译器使用include智能指针头文件。
#ifdef _MSC_VER #include<memory> #else #include<tr1/memory>
有时候我们写的程序也想像Linux的一些命令一样,可以有很多参数,这时我们就需要在自己的程序里面解析这些参数。Linux提供了两个函数getopt和getsubopt可以帮助我们完成解析操作。
先来看一下命令行参数是怎么使用的。$ps -e、$find -name main.c这两个命令都使用了命令行参数。减号-表示一个命令行参数的开始,紧挨着-的是选项。像上面的e和name就是一个选项。其中选项e是没有参数值的,而选项name是有参数值的,为main.c。有一些选项既可以有参数值,也是可以没有参数值的。上面的-name选项就必须要有参数值。
现在来看一下getopt函数的声明。
#include <unistd.h> extern char *optarg; int getopt(int argc, char * const argv[], const char *optstring);
参数argc和argv是main函数的两个参数,而参数optstring指明要在argv中查找哪些选项。假如optstring的值为"ab:c:de::",那么就说明会在argv中查找a、b、c、d、e这5个选项(不错,选项都是一个字符的)。其中b、c选项后面带有一个冒号:,说明b和c选项都要有一个参数。选项e后面有两个冒号,那么e选项既可以有参数也可以没有参数。选项a和d都没有冒号,说明这两个个选项都是没有参数的。
int main(int argc, char** argv) { int opt; while( (opt = getopt(argc, argv, "ab:c:de::")) != -1 ) { switch( opt ) { case 'a' : break; case 'b' : printf("parm = %s", optarg); break; case 'c' : break; case 'd' : break; case 'e' : if(optarg) { printf("option e has parm\n"); } break; default : //出现了不是选项的字符 } } }
可以看到,getopt函数的返回值就是对应选项的ASCII值。当查找完所有的命令行参数后就返回-1。
如果一个选项是有参数的,那么全局变量optarg就会指向这个参数字符串的开始位置。从上面的代码也可以看到,如果选项e有参数,那么optarg就不等于NULL,否则就等于NULL。
getsubopt函数一般是和getopt函数配合使用的,用来处理某一个选项的参数。当一个选项的参数比较复杂时,就可以使用getsubopt函数。比如命令./test -o ro,rw,name=main.c。-o表示一个选项。而后面的那些都是该选项的参数,挺复杂的。选项o有三个子选项ro、rw和name,而只有name有参数值main.c。这些子选项之间用逗号分开,不能用空格。
看完了使用,现在看一下getsubopt的原型
#include <stdlib.h> int getsubopt(char **optionp, char * const *tokens, char **valuep);
参数tokens的修饰符有点怪怪的,不管了。先看一个例子吧。
int main(int argc, char **argv) { enum { RO_OPT = 0, RW_OPT, NAME_OPT }; char *const token[] = { [RO_OPT] = "ro", [RW_OPT] = "rw", [NAME_OPT] = "name", NULL }; char *subopts; char *value; int err = 0; int opt; while ((opt = getopt(argc, argv, "aeo:")) != -1) { switch (opt) { case 'a': break; case 'e': break; case 'o': subopts = optarg;//optarg指向参数字符串的开始位置 while (*subopts != '\0' && !err) { //getsubopt会修改subopts的值 switch (getsubopt(&subopts, token, &value)) { case RO_OPT: if( value ) printf("ro parm = %s\n", value); else printf("ro\n"); break; case RW_OPT: if( value ) printf("rw parm = %s\n", value); else printf("rw\n"); break; break; case NAME_OPT: if( value ) printf("name parm = %s\n", value); else printf("name\n"); break; default: err = 1; break; } } } } return 0; }
从上面代码可以看到,要想使用getsubopt就需要使用定义一个二维字符数组。数组里面的字符串就是命令行参数里面的子选项值。如果查找到了一个子选项,那么getsubopt函数返回这个子选项在二维数组里面的下标值。而getsubopt的参数valuep则会指向对应子选项的参数值。
参考:http://man7.org/linux/man-pages/man3/getopt.3.html
http://man7.org/linux/man-pages/man3/getsubopt.3.html
如果某个结构体类型如下定义:
typedef struct CvScalar { double val[4]; } CvScalar;
struct CvScalar; typedef struct CvScalar CvScalar;
但是,如果某个结构体在定义的时候根本就没有名字,只是用typedef声明了一个别名,如下:
typedef struct { int width; int height; } CvSize;
虽然没有办法直接进行前置声明,但我还是进行了一个可行性尝试。不确保有可移植性。
首先,这个结构体是定义在OpenCV的cv.h头文件的,并且cv.h使用宏__OPENCV_CORE_TYPES_H__防止重复包含。我就在自己的头文件里面加入
#ifndef __OPENCV_OLD_CV_H__ typedef struct { int width; int height; }CvSize; #endif
独立定义几个数组,为了便于对这些独立数组统一在一个循环里面访问,需要将这些独立的数组作为另外一个数组的元素。可以使用下面的方式组装。
int main() { int a[2] = {1, 2}; int b[2] = {3, 4}; int c[2] = {5, 6}; int *d[3] = {a, b, c}; cout<<d[0][1]<<endl; int aa[2][2] = { {1, 2}, {3, 4} }; int bb[2][2] = { {5, 6}, {7, 8} }; int cc[2][2] = { {9, 10}, {11, 12} }; int (*dd[3])[2] = {aa, bb, cc}; cout<<dd[0][1][0]<<endl; return 0; }