宏和预处理器详述

9月 3, 2011 留下评论

Macro(宏)来源于希腊语,意思就是大,远.在计算机科学中指的是规则,这个规则指定了
输入和输出之间的关系(是不是优点类似于函数?).所有的宏都是一个概念,那就是输入
与输出之间的映射关系,这个和函数本质上是相同的.但是宏和函数处理方式基本上是不同
的,比如在C语言中,宏是在preprocess阶段被替换掉,而函数(讨论一般意义上的函数,不
讨论inline函数)在运行时调入栈中运行.而且不同的语言对宏的处理也是不同的,比如C
语言中,宏是无法区分作用域的(编译单元内的作用域),但是在metapost中,恰恰是可以
在宏体里定义局部变量的.
参看:

下面讨论C语言中的宏.
谈到宏,不得不提到preprocess directives(预处理指令).预处理指令是预处理器在
预处理阶段所要执行的操作.具体在宏中就是执行宏替换功能.基本的一个就是#define.

好,现在先谈谈宏的定义.
宏就是处理输入和输出的规则.所以更直观的一点就是带个输入参数.

#define ADD(foo)  foo+10

这样就定义了一个宏add,它的输入是foo,输出是foo+10,规则就是输入加上10.那么在
预处理阶段是如何处理的呢?比如,


bar.c

#define ADD(foo) foo+10

int main()
{
	ADD(3);
	return 0;
}

这个程序在预处理阶段会被替换成

int main()
{
	3+10;
	return 0;
}

可以查阅相关的编译器手册,指定相应的选项(options),来察看这个结果.在GCC中,使用
-E选项(其中会输出一些额外的信息,现在不必理会).这个宏,可以带参数,形式上和函数
很像,所以有的人会称它为宏函数,这是个非正式称呼.

更常见的定义宏的方式就是没有输入(即没有参数),比如,

#define PI 3.14

定义了宏PI, 没有输入,输出为3.14,规则就是得到3.14.处理方式同上.形式上,和常量很
相像,所以又有了宏常量的称呼.

#define FREEBSD

定义了宏FREEBSD,这个宏没有输入,没有输出,没有规则,仅仅是定义了这个宏.

但是请记住,宏和函数,在功能上是一样的,都是处理输入得到输出的过程.只不过语言或者
编译器对他们的处理方式不一样.

C语言中,一些内置的宏:
这些内置的宏,不需要自己再define了,直接拿来用就可以.
__LINE__ 表示当前行号.
__FILE__ 表示编译单元的文件名
__DATE__ 表示编译时的日期字符串
__TIME__ 表示编译时的时间字符串
__STDC__ 是否是符合标准,如果是结果为1,否则为0,这个主要是针对编译器编译时的选项.
更多的宏参考在线文档

注意,很多人在调试的时候使用__FUNCTION__(gcc扩展)或者__func__(C99标准),这两个都
不是宏,应为在预处理阶段根本无法确认函数.

宏定义需要注意的问题
1.宏是预处理阶段的替换(expansion),不进行计算,所以要时刻把()加上.
拿上面定义的一个例子来看:

bar.c

#define ADD(foo) foo+10

int i = 2 * ADD(3);

语句会被替换成:

int i = 2 * foo+10;

这样就错误了.本意是要3+10,然后再乘以2,结果为26,现在变成了30了.

#define ADD(foo) (foo+10)

最好把参数全部加上(),对于这个例子,没有必要,但是对其他的例子就很有必要了:

#define MUL(foo, egg) foo*egg
MUL(2+3,4+5);

期望的结果是(2+3)*(4+5)=45,结果却成了2+3*4+5=19.
合理的定义方式是:

#define MUL(foo, egg) ((foo) * (egg))

2.宏是否可以用来定义注释.

#define HEAD_COMMENT /*
#define END_COMMENT */

答案是否定的.注释的处理在预处理指令处理之前,也就是说在define指令被处理之前注释
已经不存在,如果在预处理指定处理过后又出来了定义的话,编译器一定会不认识.

Preprocessor(预处理器),在计算机科学中指的是一个处理代码输入并且把输出做为另外
一个程序输入的程序,它也是一个程序.参看:http://en.wikipedia.org/wiki/Preprocessor
GNU的预处理器可以处理C/C++/Objective-c,因为都和C沾边(或者就是从开始遗留下来的
习惯)所以就叫C PreProcessor(cpp 不是c plus plus:-) )

CPP处理预处理指令,有#include,#define(undef),#if(ifdef,ifndef,else if,elif,endif),#error
,#line, #pragma,#,##
,当然预处理也处理注释(comment),而且优先于预处理指令.

1.文件包含(file inclusion)

#include指令

#include <stdio.h>      从标准路径搜索stdio.h
#include "nids.h"
	从当前目录开始搜索nids.h如果找不到再到命令行参数指定的目录寻找
	最后是标准目录下寻找.

2.宏定义(macros defination)

前述.

3.条件编译(conditinal compilation)

#if __STDC__ == 1
#if defined FREEBSD
#if !defined FREEBSD
#endif

#ifdef FREEBSD
#else if
#define LINUX
#endif

4.错误产生(error generation)

#error this line shouldnt be reached  

5.行号控制(line control)

#line 299 "ip.c"  下一行是ip.c的299行
#line 299         下一行是本文件的299行

6 #,##
#和##常常用于宏定义里,这两个是预处理运算符,不是指令,因为预处理指令不能用于宏定义.就像C99添加的_Pragma操作符.

#define tempfile(dir) #dir "/%s"
tempfile(/usr/tmp)被扩展成  "/usr/tmp" "/%s",也就是 "/usr/tmp/%s"

#把宏参数字符串化(C语言表示的字符串),而且这个的”优先级”比较高.
看一个来自cpp手册的例子:

#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)       由于有#,表示参数首先被字符串化,所以不再有任何扩展.
	→ "foo"
xstr (foo)      没有#,所以参数被扩展.
	→ xstr (4)
	→ str (4)
	→ "4"

解释:

s is stringified when it is used in str, so it is not macro-expanded first. But
s is
an ordinary argument to xstr, so it is completely macro-expanded before xstr
itself is
expanded . Therefore, by the time str gets to its argument, it has already been macro-expanded.

#define cat(x,y) x##y
cat(2,3)   扩展为23
cat(wolf,python) 扩展为wlfpython

##把tokens连接起来(tokens concatenation)
通常##两边的tokens可以是标识符,数字或者运算符,不可以是C语言表示的字符串.
比如cat(“2″,”3”)是错误的.

7.#pragma
#pragma用于向编译器提供信息.(由于是指令,所以无法用于宏定义,C99定义了
_Pragma运算符)

#pragma once 可以用于防止头文件被重复包含(另外一种方式是使用条件编译)

8.空指令 #
#后面仅跟着一个换行符,就是一个空指令,什么也不做,在预处理阶段没有动它.

分类:C/C++

Gentoo编译内核

9月 2, 2011 留下评论

天启动Gentoo,发现x server无法启动,察看了一下log,是nvidia驱动没有安装,记得升级的时候,已经安装过了,不知道为什么出现这么个问题.

那就重新安装nvidia-drivers
#emerge nvidia-drivers

出现错误,找不到.config文件,察看了一下,内核2.6.37的/usr/src/linux-2.6.37-gentoo-r4里面已经没有内核源码了,察看一下gentoo最新的稳定内核是39了,就接着
#emerge gentoo-sources

先安装内核39吧,为了省事儿,采用genkernel编译,eselect过后,wk,无法编译啊,出现

ERROR: Could not find source tarball: /var/cache/genkernel/src/busybox-1.7.4.tar.bz2!

于是换用手动编译内核,编译完成后,kernel panic, 没有搞懂gentoo的加载内核的方式是怎么实现的,
genkernel是需要initrd的,但是手动编译的不需要,kernel panic的原因应该就在于此,应该是内核启动起来过后,没有相应的驱动(模块)来识别硬盘导致的.

还是暂时换用genkernel吧,
#etc-update
更新配置文件,这下genkernel正常了,接着安装nvidia的驱动,可以起来了.

这个问题有时间好好研究一下.

*******************************************************************************************************
原来在编译的时候,选择了Initial RAM filesystem and RAM disk support ,在这里(删掉了)

这个选上了,内核在启动过程中,会挂载这个小型的文件系统(主要成分为busybox),加载完磁盘驱动和其他驱动过后,开始挂载磁盘分区到根文件系统,系统就起来了.

而自己手动编译的时候,没有生成initrd,所以运行到这里就出错了.

有两种解决方案:
1.不使用initrd,把磁盘等驱动编译到内核里,并且不要编译Initial RAM filesystem and RAM disk support

2.使用initrd,可以手动生成initrd,也可以使用genkernl来生成initrd.

分类:C/C++, GCC, Gentoo

字符串操作函数实现

9月 1, 2011 留下评论

一直觉得自己眼高手低, 平时也懒得写代码, 下定决心要克服这个毛病,于是开始了重新拿起 the c programming languages 这本C语言圣经做做上面的练习题.就当是练手吧.

这次从字符串操作部分入手,实现自己的库,争取多写,多思考.而后看看一下coreutilities的GNU和NetBSD实现,接着看看(e)glibc,再以后,再打算吧.希望能有高手指出自己的不足,提高自己,一切以学习为目的.

/*
 * =====================================================================
 *
 *       Filename:  wolf_string.c
 *
 *    Description:  Defination for my str lib
 *
 *        Version:  1.0
 *        Created:  2011年09月01日 13时14分35秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  wolfpython(walter) (), wolfpythonlondon@gmail.com
 *        Company:  
 *
 * ====================================================================
 */
#include "wolf_string.h"
#include <assert.h>


int
wolf_strlen(char *src)
{
	int i = 0;

	while(*src != '\0') {
		src++;
		i++;
	}
	
	return i;
}

int
wolf_strlen_1(char *src)
{
	char *p = src;

	while(*src != '\0')
		src++;

	return src-p;
}

void
wolf_strcpy(char *des, char *src)
{
	/* if des is a null pointer, its not safe to modify the mem
	 * if the src is a null pointer, it doesnt make any sense
	 * */
	assert(des != NULL && src != NULL);

	while((*des = *src) != '\0') {
		des++;
		src++;
	}
}


void 
wolf_strcpy_1(char *des, char *src)
{
        /* if des is a null pointer, its not safe to modify the mem
	 * if the src is a null pointer, it doesnt make any sense
	 * */
	assert(des != NULL && src != NULL);

	while(*des++ = *src++)
		;
}


int 
wolf_strcmp(char *des, char *src)
{
	/* if des is a null pointer, its not safe to modify the mem
	 * if the src is a null pointer, it doesnt make any sense
	 * */
	assert(des != NULL && src != NULL);

	while(*des == *src) {
		if(*des == '\0') {
			return 0;
		}
		
		des++;
		src++;
	}

	return *des - *src;
}


void
wolf_strcat(char *des, char *src)
{
	/* it doesnt matter if the src is a null pointer */
	assert(des != NULL);

	while(*des != '\0')
		des++;
		
	while(*des++ = *src++)
		;
}

/* 
 *if t is at the end of s, return 1,
 *else return 0
 * */
int 
wolf_strend(char *s, char *t)
{
	int tsize;
	char *thead = t;             // the head of t
	
	/* it doesnt make any sense if t is a null pointer */
	assert(t != NULL);

	if( s == NULL) return 0;
	while(*s != '\0')
		s++;

	while(*t != '\0')
		t++;
	tsize = t - thead;
	
	while(tsize-- > 0) {
		if(*s != *t)
			return 0;
		s--;
		t--;
	}

	return 1;
}


分类:C/C++

Debian doc 问题解决

7月 18, 2011 留下评论

今天升级Wheezy(testing)的时候,出现了这样的错误:

Error in `/usr/share/doc-base/libpng12', line 25: all `Format' sections are invalid.
Note: `install-docs --verbose --check file_name' may give more details about the above error.

看来文件的某个section(段)出现问题了,按照提示的,敲命令:

root@debian:/home/wolf# install-docs --verbose --check /usr/share/doc-base/libpng12
Warning in `/usr/share/doc-base/libpng12', line 25: file mask `/usr/share/doc/libpng12-0/libpng-1.2.44.txt.gz' does not match any files.
Error in `/usr/share/doc-base/libpng12', line 25: all `Format' sections are invalid.
/usr/share/doc-base/libpng12: Fatal error found, the file won't be registered.

原来是/usr/share/doc-base/libpng12这个文件最后一行的file mask(文件掩码)不存在,看来Debian是通过这个机制进行register(注册)文档的.
来看看/usr/share/doc-base/libpng12的内容和/usr/share/doc/libpng12-0里面的是什么情况:

Document: libpng12
Title: A description on how to use and modify libpng (version 1.2.X)
Author: Glenn Randers-Pehrson
Abstract: This file describes how to use and modify the PNG reference library
 (known as libpng) for your own use.  There are five sections to this
 file: introduction, structures, reading, writing, and modification and
 configuration notes for various special platforms.  In addition to this
 file, example.c is a good starting point for using the library, as
 it is heavily commented and should include everything most people
 will need.  We assume that libpng is already installed; see the
 INSTALL file for instructions on how to install libpng.
 .
 Libpng was written as a companion to the PNG specification, as a way
 of reducing the amount of time and effort it takes to support the PNG
 file format in application programs.  The PNG specification is available
 as RFC 2083  and as a
 W3C Recommendation . Some
 additional chunks are described in the special-purpose public chunks
 documents at .  Other information
 about PNG, and the latest version of libpng, can be found at the PNG home
 page, .
Section: Programming

Format: text
Files: /usr/share/doc/libpng12-0/libpng-1.2.46.txt.gz
root@debian:/home/wolf# ls /usr/share/doc/libpng12-0/
ANNOUNCE              changelog.gz          KNOWNBUG              README.Debian         TODO                  
changelog.Debian.gz   copyright             libpng-1.2.46.txt.gz  README.gz             

这下问题清楚了,就是/usr/share/doc-base/libpng12里面的file mask,和真正的版本(libpng-1.2.46.txt.gz)不相符合,
导致系统无法注册这个文档,所以会报错,手动修复一下即可.

这个也是个bug,有很多人已经file了它了.

分类:Debian GNU/Linux

我使用的自由软件

7月 14, 2011 留下评论

多媒体:
音乐:—> mpg123 :一个优秀的CLI音乐播放器。我最常用的音乐播放器。
和个人的习惯有关,因为我是不大在意听到什么的,
只要是工作的时候,耳边有声音就足够了,所以常常是
把十几首音乐放在一个目录一下,然后放在一个列表中,
播放,也可以自由控制。
$mpg123 美丽的回忆.mp3
————————-
|$ls *mp3 > list
|$mpg123 –list list -ZC #Z 是完全随机播放,C是开启命令行下键控制。
————————————————————————

—>Rhythmbox:一个非常优秀的GUI音乐播放器,也可以收听电台和播客。
但是某些发行版要求手动安装解码器,尤其是桌面发行版。
$rhythmbox&  #从命令行启动,丢到后台工作
VOA电台:rtsp://a247.r.akareal.net/live/D/247/2110/v001/reflector:48984
BBC music:http://www.bbc.co.uk/6music/ram/6music_hi.asx
CRI:mms://livexwb.cri.com.cn/fm915
—>一个在线的电台大杂烩:http://www.mediayou.net/web/search.asp?lan=chn#

影视:—> 电影播放机:发行版默认的电影播放器。
MPlayer:万能影音播放器(unix-like OS)

刻录:—> cdrecord : CLI的刻录软件,不是很常用。
—> brasero: 非常方便的GUI刻录软件,如果有了她,刻录方便不少,但是占用
的资源,相对较大。

办公:
文档:—> Vim : 处理日常的文档的书写,代码的编辑。
—> Emacs:同上。
—> openoffice.org:安装了,但是很少用,启动慢。
—> evince: gnome
DE标准的PDF阅读器,可以阅读PDF,DVI,PS格式的文件,甚至可以读取jpg图片.
—> TeX/LaTeX/XeTeX:非常优秀的排版系统.我使用它翻译了(和别人合作)一本书.
在这里 https://github.com/wolfpython/thinkpython

图像处理: gimp:优秀的图像处理软件,功能及其强大。
—> dia: 用来处理流程图基本图形的画写。
—> freemind: 思维导图(这个是GF推荐给我的)

字典: —> stardict:很优秀的字典框架,但是最近貌似辞典不好下载。
—> sdcv: stardict的命令行版本。
—> goldendict:很优秀的字典,默认的抓取wikipedia的内容。
可以使用stardict的字典文件.

编码:
编辑器(IDE): Vim:
—-> Emacs:
—-> geany : 轻型的IDE,处理基本的编程,代码一般不超过1000行。
—-> Anjuta: 朴素的IDE,可以处理绝大多数的编程语言。
—->QtCreator: 用来对QT进行编码。
—->Eric:在pyQt编程时,会用到,支持python,和ruby。
—->IDLE: 官方的PYTHON IDE,
在交互界面使用非常舒服,但是在Debian上有问题,经常在输入完后无法感知回车键也就。
无法执行语句了.而且Mandriva/Mageia并没有显式的把IDLE呈现给用户.
调试器:–>gdb
–>nemiver :gdb的前端,图形化调试器
–>DDD:

互联网:
即时聊天: pidgin: 支持很多协议的聊天框架,最喜欢的还是google talk。
Empathy: 和pdigin类似的软件
—->xchat-IRC: 严格说来不能算是即时聊天软件。
—->Eva :我正在开发中的linux QQ。

邮件客户端:evolutin:非常亲切的邮件收发客户端,GUI,但是我基本不使用.
—> mutt:

feed阅读: lifeera: rss阅读器

新闻组客户端: tin

浏览器: Iceweasel(firefox):
links:
w3m:
lynx:在线阅读官方文档时经常会使用这个浏览器.

本文的最新文档在:https://github.com/wolfpython/notes/blob/master/software

开始使用wordpress博客

7月 9, 2011 留下评论

今天注册了这个帐号,使用wordpress博客!

分类:生活