Showing posts with label C++. Show all posts
Showing posts with label C++. Show all posts

Sunday, June 20, 2010

Linux下各种Buffer的比较

原文发表于「桃源」: http://linux.cuit.edu.cn/?p=919

类型 默认大小[1] 存储位置 操作函数 备注
标准I/O流[2] File BUFSIZ[3] (8192) 或者 st_blksize[4] (4096) User Space stat(2), setvbuf(), fflush() 每一个标准I/O流都有一个buffer
stdin stdin->_IO_buf_end[5] - stdin->_IO_buf_base (1024)
stdout stdout->_IO_buf_end - stdout->_IO_buf_base (1024)
stderr 1
TCP Receive Buffer SO_RCVBUF[6] (87380) Kernel Space getsockopt(), setsockopt() 每一个socket都有两个buffer
Send Buffer SO_SNDBUF (16384)
UDP[7] Receive Buffer SO_RCVBUF (114688)
Send Buffer SO_SNDBUF (114688)

脚注:

  1. 括号中的数字为我电脑上的实际大小, 单位为字节. 测试环境: Ubuntu 9.10, 内核版本2.6.31, GNU C library版本2.10.1, 文件系统ext4.
  2. 标准I/O流buffer的默认大小是由具体的C函数库实现决定的, 比如GNU C library就使用st_blksize作为默认大小. 每个流的buffer是在创建好流之后, 第一次调用标准I/O库函数对流进行操作时通过malloc()函数分配的. (参见《APUE》5.4节与5.12节)
  3. BUFSIZ是定义在<stdio.h>头文件中的宏.
  4. st_blksize是"struct stat"中的成员, 通过stat(2)函数获得.
  5. _IO_buf_end以及_IO_buf_base是"struct _IO_FILE"中的成员, "struct _IO_FILE"的定义通常在<libio.h>中. 其实在<stdio.h>中可以看到"typedef struct _IO_FILE FILE;", 也就是我们经常使用的FILE指针指向的结构体了.
  6. SO_RCVBUF是socket的选项名, 可以通过getsockopt()函数获得大小, 以及setsockopt()设置大小. 后同. (参见《UNP》2.11节与7.5节)
  7. UDP类型的socket实际上是不存在buffer的, 这里的大小只是用来约束数据报的最大长度. (参见《UNP》2.11节)

参考资料:

  1. W. Richard Stevens and Stephen A. Rago. Advanced Programming in the UNIX Environment, 2/e. Addison-Wesley Professional, June 17, 2005, ISBN 0201433079.
  2. W. Richard Stevens, Bill Fenner, and Andrew M. Rudoff. UNIX Network Programming, Volume 1: The Sockets Networking API, 3/e. Addison-Wesley Professional, November 21, 2003, ISBN 0131411551.
  3. Helali Bhuiyan, Mark McGinley, Tao Li and Malathi Veeraraghavan. TCP Implementation in Linux: A Brief Tutorial. Available online from http://www.ece.virginia.edu/mv/research/DOE09/publications/TCPlinux.pdf

P.S. 如有不当之处, 还望指教.

Tuesday, May 11, 2010

split()函数的C、C++标准库实现

2010.5.11 22:52更新: 在大猫同学的强烈要求下, 增加了测试代码中动态分配空间的销毁.

2010.5.14 22:46再更新: 上次更新代码之后, 大猫同学指出还是存在内存泄漏问题, 各位同学一定要注意malloc()free()必须成对使用哦~

最近有这么一个需求, split()函数大家都用过, 将字符串按特定的分隔符 (delimiter) 分割成几个部分, 这在脚本语言中早已司空见惯. 但C/C++中是没有的, 于是就给了我这样闲得蛋疼的人消磨时间的机会, 下面分别是用C和C++的标准库实现的split()函数.

首先是C语言的实现, 这个是我自己写的, 函数返回一个指向char*数组 (也就是字符串数组) 的指针.
#include <string.h>
#include <stdlib.h>

/*
 * split - Break a string @s into an array of strings.
 * @s: The string to be split.
 * @delim: The delimiter.
 * @n: The length of the array of strings, created by splitting @s.
 */
char** split(char* s, char delim, int* n)
{
    char* p;
    char* q;
    *n = 1;
    p = q = s;
    /*
     * Calculate the value of @n.
     */
    while ((p = strchr(q, delim)) != NULL)
    {
        ++*n;
        q = p + 1;  // skip the delimiter
    }
    p = q = s;

    char** sep = (char**) malloc(sizeof(char*) * (*n));  // the array of strings
    int i;
    for (i = 0; i != *n; ++i, q = p + 1)
    {
        /*
         * When there isn't delimiter, put `p' point to the end of @s.
         * Same like behavior of strchrnul() library function.
         */
        if ((p = strchr(q, delim)) == NULL)
            p = s + strlen(s);

        int len = p - q;
        sep[i] = (char*) malloc(sizeof(char) * (len + 1));
        strncpy(sep[i], q, len);
        sep[i][len] = '\0';
    }

    return sep;
}
可以使用下面的代码进行测试:
#include <stdio.h>
#include <stdlib.h>

char** split(char*, char, int*);

int main(int argc, char* argv[])
{
    char str[] = ":GFW::is:evil:";
    int i, n;
    char** sep = split(str, ':', &n);
    for (i = 0; i != n; ++i)
        printf("%s\n", sep[i]);

    for (i = 0; i != n; ++i)
    {
        free(sep[i]);
        sep[i] = NULL;
    }
    free(sep);
    sep = NULL;

    return 0;
}

C++实现来自Stack Overflow的一篇讨论, 我稍微进行了一些修改.
#include <sstream>
#include <vector>

using namespace std;

vector<string> split(const string s, char delim = ' ')
{
    vector<string> elems;
    istringstream ss(s);
    string item;
    while (getline(ss, item, delim))
    {
        elems.push_back(item);
    }
    if (item == "")
        elems.push_back(item);
    return elems;
}
代码很简洁, 测试代码就不写了, 大家应该都能看懂. 其实大名鼎鼎的Boost也实现了split(), 但毕竟不是任何时候都能使用Boost.

Monday, January 11, 2010

流言终结者: 使用位运算交换变量的值

在C语言中怎样交换两个变量的值? 我们通常的做法是:
tmp = a;
a = b;
b = tmp;
那怎样不使用中间变量交换两个变量的值? 也许你看过这样的方法:
a ^= b;
b ^= a;
a ^= b;
这是一个借助于位运算的小伎俩, 但一般在实际编程中是不推荐使用的, 原因在大多数人看来应该是降低了代码的可读性, 其实还有一个被忽视的地方. 在多数人的直觉中位运算绝对比普通的运算符要快, 不过刚才的例子是一个反例.

现在让我们来看一下上面两段代码对应的汇编代码, 这里使用gcc作为编译器. 首先是使用了中间变量的那几句:
movl    -4(%ebp), %eax
movl    %eax, -12(%ebp)
movl    -8(%ebp), %eax
movl    %eax, -4(%ebp)
movl    -12(%ebp), %eax
movl    %eax, -8(%ebp)
"-4(%ebp)"、"-8(%ebp)"、"-12(%ebp)"分别是变量abtmp在栈中的地址, 同时使用了寄存器"eax". 可以看出, 上面的代码一共对内存进行了3次读操作和3次写操作.
再来看看使用位运算的汇编代码:
movl    -8(%ebp), %eax
xorl    %eax, -4(%ebp)
movl    -4(%ebp), %eax
xorl    %eax, -8(%ebp)
movl    -8(%ebp), %eax
xorl    %eax, -4(%ebp)
一次"xorl"异或操作包含一次读和一次写, 因此一共是6次读操作和3次写操作.
通过比较可以看出位运算在I/O速度上已经不占优势, 再比较一下两种代码所需的内存空间, 下面这段汇编是公共的代码.
pushl   %ebp
movl    %esp, %ebp
subl    $16, %esp
两种方法都在栈中分配了16个字节的空间, 因此在空间上的比较是一样的. 这样看来, 使用位运算交换两个变量的值真是装B者必备的好东西.

Tuesday, September 1, 2009

ARM板烧写文件系统失败的总结

这几天一直被一个很恼火的问题纠结着, 需要给块板子烧写文件系统, 但使用我前段时间介绍过的Linux下的DNW试了很多次都失败了, 又试了一个老外写的s3c2410_boot_usb (这个传输速度比DNW慢很多) 还是不行, 在SHE电脑上的XP下用Win版的DNW依旧不行, 可用某师兄的电脑烧的时候又每次都可以, 囧. 另一个师兄笑说这是人品问题, 呵呵~ 不过我就是不相信有那么离奇, 没道理只能在别人的电脑上才能成功, 一定会有解决办法的. 终于让我发现了一个传输上的小细节: 地址, 这时我才想到每次烧的时候我都没有指定地址, 而是使用的程序内部的默认地址. 于是试着修改了一下dnw2的源码, 把地址替换了, 结果, 嘿嘿, 肯定是成功了三, 哇哈哈~~~ 不过困扰了几天的问题居然是这么一个小细节...

为了以后的使用方便, 我又进一步完善了dnw2的源码, 使得可以通过命令行参数来指定地址, 最新打包好的程序: dnw2_linux_fixed_20090901.tar.gz. 同时我的修改版也得到了原作者Fox的认可, 并放到了他的SVN仓库里. 最后需要特别感谢Fox的贡献.

Thursday, March 5, 2009

typeof, ISO C, GCC, 没有空格的代码, 奇怪的比赛

这个故事的起源是Matrix67博客上的一篇文章, 一个叫做Time Limit Exceeded的创意编程比赛有一道很有意思的题: Compile Error, 这道题不允许代码中出现空格. 一个最简单的问题就是像 int a; 中的空格如何避免, Matrix67的文章中提到可以用typeof解决. 昨天Ray提醒我这个typeof在C语言里没见过, 然后我就一直想啊, 这个typeof是个什么东西啊. 查了一下才知道, 原来typeof不是标准C/C++中的关键字, 目前GCC提供了对typeof的支持, 这里有详细的解释, 里面有一个绝妙的例子, 可以在C中实现类似C++中Template的功能. 这是Wikipedia上关于typeof的解释: http://en.wikipedia.org/wiki/Typeof, 在C#、JS中也有这个关键字, 不过细节上有差别.

好了, 弄清了typeof的来龙去脉以后, 再回到那个编程比赛上. 现在我们就有了一种方法来避免空格了:
typeof(int)a;
本来以为这个已经够难想到的了, 不过又发现了一个更简单, 且很好理解的方法:
int(a);

没想到一个typeof引申了这么多东西出来, 真是神奇~

Monday, January 5, 2009

课程设计终于搞定 :)

这几天为了调试课程设计的程序已经达到可以不去吃饭了, 好在终于弄完, 今天老师检查的时候还因为没来得及生成Window$版本的程序, 而不得不直接在Linux上演示. 晚上把Window$的也弄出来了, 所有的代码也都svn到了Google Code上了. 长出一口气, 呼~

期间在两个系统之间有一个不兼容的地方, 就是Window$下<conio.h>文件中的getch()这个函数. 由于Linux下没有那个头文件, 自然也就没有这个函数了. 但这个函数又是比较重要的一个, 不好轻易改动, 于是在CSDN的某博上找到了很好的解决办法, 用着挺不错的~

他自己写了个getch()函数, 功能上和Window$下的一样:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

int getch()
{
  int c = 0;
  struct termios org_opts, new_opts;
  int res = 0;

  /*
   * Store old settings.
   */
  res = tcgetattr(STDIN_FILENO, &org_opts);
  assert(res == 0);

  /*
   * Set new terminal parms.
   */
  memcpy(&new_opts, &org_opts, sizeof(new_opts));
  new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
  tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
  c = getchar();

  /*
   * Restore old settings.
   */
  res = tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
  assert(res == 0);

  return c;
}

Window$版的本来想用VC的编译器来弄的, 结果果然VC的编译器比较高级, 在Linux下gcc没有一个错误和警告, 到了VC下就出现了几十个错误和警告(- -). 最后没办法, 就直接用MinGW编译了, 不过运行起来没有Linux下那么稳定.

Monday, December 22, 2008

Friday, May 9, 2008

一道关于共用体的题目

#include <stdio.h>

int main()
{
union l
{
int a[2];
char b[4];
};

union l c;
c.a[0] = 65;
c.a[1] = 66;

printf("%c\n", c.b[0]);

return 0;
}
不知道为什么那个输出语句会输出大写字母A, 如果按内存存放的结构来理解的话, 我觉得应该输出空, 也许对于共用体我还有什么没有理解的吧, 希望以后能找到关于这个的正确解释.