C/C++的一些杂项

来源:互联网 发布:思科arp绑定mac地址 编辑:程序博客网 时间:2024/06/06 01:36

fwrite 和 fread的参数问题

        有时,我们在阅读别人的代码时会看到这样的写法:

    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的支持。安装了支持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和strcspn函数

strspn:

        对于strspn,虽然有些文章进行了一些解说,但总是难于理解。自己写一个例子验证自己的理解,但结果并非自己预测的那样。所以我这里也来解说一下strspn,不过我是直接上strspn的实现代码。我就用glibc2.19中strspn的实现代码来解说。

size_tstrspn (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也返回}

        代码里面有两个循环,分别用来遍历两个字符串。


        我们看一下在什么时候该函数会返回。当*a == ‘\0’时,就会返回。也就是说当内循环遍历了整个accept字符都没有break时,就会发生*a == ‘\0’成立,也就返回了。没有break就说明:在整个accept字符串中都找不到一个字符等于*p。

        *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:

        函数strcspn和前面的strspn长得有点像,可以用strspn来帮助理解。下面给出它的实现代码。

size_tstrcspn (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




跨编译器include智能指针头文件:

        在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>



getopt和getsubopt命令行解析函数:

        有时候我们写的程序也想像Linux的一些命令一样,可以有很多参数,这时我们就需要在自己的程序里面解析这些参数。Linux提供了两个函数getopt和getsubopt可以帮助我们完成解析操作。


getopt:

        先来看一下命令行参数是怎么使用的。$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都没有冒号,说明这两个个选项都是没有参数的。


        函数getopt一般是和循环语句一起使用的,使用如下:
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:

        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类型的前置声明:

        如果某个结构体类型如下定义:

typedef struct CvScalar{    double val[4];}CvScalar;

        那么前置声明可以写成下面的形式:

struct CvScalar;typedef struct CvScalar CvScalar;


        但是,如果某个结构体在定义的时候根本就没有名字,只是用typedef声明了一个别名,如下:

typedef struct{    int width;    int height;}CvSize;

        此时,就无法进行前置声明了(我google了很久都没有找到能前置声明的方法。如果读者有解决方案,还请赐教)。


        虽然没有办法直接进行前置声明,但我还是进行了一个可行性尝试。不确保有可移植性。

        首先,这个结构体是定义在OpenCV的cv.h头文件的,并且cv.h使用宏__OPENCV_CORE_TYPES_H__防止重复包含。我就在自己的头文件里面加入

#ifndef __OPENCV_OLD_CV_H__typedef struct{    int width;    int height;}CvSize;#endif

        不错,就是在自己的头文件里面定义这个类型。为了避免重复定义,在对应的源文件还需要先包含OpenCV的cv.h头文件,然后才能包含自己的头文件。



数组的数组:

        独立定义几个数组,为了便于对这些独立数组统一在一个循环里面访问,需要将这些独立的数组作为另外一个数组的元素。可以使用下面的方式组装。

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;}


Source Insight识别函数前面的宏定义:

        最近通过Source Insight阅读libjpeg的源代码时,发现Source Insight无法识别一些函数,这些函数的共同特点是前面定义了宏函数。在Source Insight安装目录的C.tom文件中,添加该宏函数即可。比如libjpeg一些函数名前面为宏函数GLOBAL(void),那么在C.tom文件中另起一行,添加 GLOBAL(X)





0 0
原创粉丝点击