2012-03-29
利用Chrome零基础翻墙

这篇文章是转过来的,由于是下的DOC文档,所以不知道源地址,不过文章后面有原作者的联系方式

=======================邪恶的分割线===========================

开了tumblr后就想着要写篇翻墙教程,当时想法是只要你知道有墙的存在,即便完完全全在墙内,按照这个零基础教程也能完成翻墙教学,而且我这不会上传任何文件,所有资源均通过官方网站下载。

不幸的是,昨天发现tumblr疑似被墙了,至少我这上不去了(浙江网通),这篇放在墙外的翻墙教程多少显得有点尴尬,只能寄希望于传播了。

本教程主要涉及的工具为chrome+goagent

一、下载安装chrome,并注册gmail邮箱

1、google出的优秀浏览器,还没用上的速速下载。

http://www.google.cn/chrome/intl/zh-CN/landing_chrome.html

2、注册一个gmail邮箱

这一步很关键,后续会多次用到你的gmail邮箱账号。

https://mail.google.com/mail?hl=zh-CN

二、运行“hosts自动更新程序”

1、用安装好的chrome打开网址:

https://chrome.google.com/webstore/detail/bcomihljbnefaobillhnajpgompoelme?hl=zh-CN

点击右上角的“启动应用程序”,接受程序自动下载的以“.bat”结尾文件。

如果没有出现下载提示,在chrome里打开新标签页,浏览器正中下方选择“应用程序”,然后点击出现的“Hosts自动更新程序”图标,接受下载。

有时下载的网站不一定能访问,可以在这里直接点击下载脚本文件(这一步需要gmail账号):

https://sandbox.google.com/storage/fgqi/hosts/fgqi.bat

2、运行程序

双击下载好的脚本文件“fgqi.bat”

由于下述步骤要使用到早就被墙掉的google服务,这里需要先更改对应服务的hosts地址。

这一步我们需要更新google服务地址,输入数字“1”并回车。

题外话:输入数字“4”并回车后,能搞定twitter和facebook的基本访问。

三、在GAE里创建app

Google App Engine是一个开发、托管网络应用程序的平台,使用Google管理的数据中心。

1、登陆申请网址(这一步需要gmail账号):

https://appengine.google.com/start/createapp

如果上述步骤需要验证手机,输入+86前缀的大陆手机号码后会收到短信,在“Mobile Number”栏里输入你收到短信里的验证码code即可完成验证步骤。

2、创建app

完成验证后界面如下:

在Application Identifier栏输入你要创建的app名称(不支持中文),点击“Check Availability”以确认你要的名称还未被注册过。

为了方便起见,这里用“chaofannet”借代你创建的app名称。(请自行创建,勿对号入座)

下一栏“Application Title” 随便填,后期也可以随便改。

完成后点击页面下方的“Create Application”,如果前面有出现“Terms of Service”即使用条款,则需要在点击“Create Application”前,把使用条款下方的“I accept these terms”打钩。

至此,app创建成功,得到的AppID即chaofannet(借代用)。

四、下载goagent

goagent是一个使用Python和Google Appengine SDK编写的代理软件。

登陆goagent官网:

https://code.google.com/p/goagent/

主页最上方即给出了下载链接,可见目前最新版为1.6.3稳定版,下载链接为bit.ly短网址,若无法解析,这里给出解析后的完整下载网址,如下:

http://nodeload.github.com/phus/goagent/zipball/1.0

下载后得到的压缩包为“phus-goagent-ed8e710.zip”(对应目前最新的1.6.3稳定版),解压后会得到两个文件夹:“local”和“server”.

五、上传goagent服务端并配置客户端

1、上传服务端

双击打开之前解压后的“server”文件夹,找到并双击运行“uploader.bat”

这时界面提示“AppID:”

输入之前你在Application Identifier栏创建的app名称,如chaofannet

出现Email提示后输入你的gmail账号,然后是密码。

注意:输入密码时,屏幕上不会出现任何符号,请不用担心,完整正确地输入密码后按回车即可。

上传完毕后会自动关闭。

2、配置客户端

双击打开之前解压后的“local”文件夹,找到并双击打开“proxy.ini”文件,

修改[gae]栏下的appid,将等号后面的“goagent”换成你的AppID,如将原来的“appid = goagent”换成“appid = chaofannet”,其余保持不变。

至此,绝大部分工作已经完成。

六、配置chrome

在chrome下安装Proxy SwitchySharp插件:

https://chrome.google.com/webstore/detail/dpplabbmogkhghncfbfdeeokoefdjegm

安装后打开Proxy SwitchySharp插件的选项:

在“导入/导出”栏目的最下行,“在线恢复备份”栏输入:

https://raw.github.com/phus/phus-config/master/SwitchyOptions.bak

至此,大功告成。

七、翻墙!

1、运行goagent.exe

位于之前解压后的“local”文件夹下

注意:第一次运行可能需要管理员权限。

题外话:我们还可以设置goagent程序开机自启动,除了最原始的手动拖到系统启动栏下,运行“local”文件夹下的“addto-startup.vbs”文件即可。

2、代理翻墙

打开chrom,在右上角Proxy SwitchySharp插件上点击选择GoAgent,如下:

题外话:请无视我的“Telex”代理协议,和本文无关。

至此,已经挂上代理,可以随意浏览墙外世界。

要想换回自己的ip,只需选择上图中的“直接连接”,即不用代理,回归墙内。

八、结束语

Google App Engine并非毫无限制,每个开发者只能拥有10个应用程序,即你最多只能创建并得到10个AppID。(貌似AppID创建了就不能删除)

Google App Engine提供给免费用户的流量是每天1GB.一般应用绝对够了。

登陆https://appengine.google.com/ 点击你创建的AppID,可以看到你的流量图,以及每天免费配额还剩多少,如下图,我已经用了1GB中的7%

每天的流量重新清零的时间好像是北京时间下午16点整,而非0点。

最后:

如还有疑惑需要咨询,请联系我的twitter账号:@chaofannet ;或者邮箱:[email protected]

We love Tumblr Stationery by Thijs

======================邪恶的分割线=====================

本人经过测试,按照上面的做,完全没问题,而且比较方便。

2012-02-12
XP安装office 2010 Error 1406

好久没写文章了,这篇文章就当除草了

安装office 2010时遇到错误1406的问题,安装几次都不行,网上搜了解决方案,说打开注册表(运行regedit)然后查找”Image File Execution Options”,记得搜索时选中全字匹配,不然可能会一直找不到你想要的项,具体位置在”HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion”

下面,貌似这还跟什么IFEO劫持有关。找到之后右击Image File Execution Options然后选中权限,

让system的权限变为完全控制,然后点击添加,输入你的用户名,把权限也变为完全控制。点击确定

就OK了,然后再回来安装就OK了。

参考http://hi.baidu.com/nightcateryta/blog/item/8c536bf351a5985d352acce7.html




2011-12-13
windows下MPI的环境搭建。

由于需要MPI来实现并行化,因此在windows下搭建了MPI的环境。MPI在windos下的环境搭建还算比较容易的。

首先去http://www-unix.mcs.anl.gov/mpi/mpich2/下载MPICH2,然后安装。

接下来在环境变量那的path设置C:\Program Files\MPICH2\bin,然后打开vc6-Tools–Options–Directories。下面的show directories for:选择include files,把%MPICH2%include加进去,然后在Library files下面把%MPICH2%/lib加进去。

然后再启动%MPICH2%/bin下面的wmpiregister.exe进行注册即可。

接下来就可以写Hello World了。

  1. #include “stdafx.h”
  2. #define MPICH_SKIP_MPICXX
  3. #include “mpi.h”
  4. #include
  5. int main(int argc, char* argv[])
  6. {
  7. int rank,size;
  8. int tt;
  9. char processor_name[MPI_MAX_PROCESSOR_NAME];
  10. MPI_Init(argc,&argv);
  11. MPI_Comm_rank(MPI_COMM_WORLD,rank);
  12. MPI_Comm_size(MPI_COMM_WORLD,size);
  13. //printf(“
  14. MPI_Get_processor_name(processor_name,tt);
  15. printf(“Hello World! I am %d,in %d computer on %s\n“,rank,size,processor_name);
  16. MPI_Finalize();
  17. if(1==rank)
  18. {
  19. printf(“Press any key to continue\n”);
  20. //getch();
  21. }
  22. return 0
  23. }



写完之后再打开project-settings-link把mpi.lib加到Object/library modules中

#define MPICH_SKIP_MPICXX就是不让包含mpicxx.h.这个在mpi.h里面可以看到。然后其他的就是MPI的函数的应用

参考:

1.http://www.cppblog.com/wlwlxj/archive/2006/06/12/8463.aspx并行编程–MPI开发入门

2.http://blog.csdn.net/morewindows/article/details/6823436[Windows系统下搭建MPI环境]

2011-04-12
C和指针14章 预处理器

1.预定义符号:FILE进行编译的源文件名
LINE 文件当前的行号
DATE 文件编译的日期
TIME 文件被编译的时间
STDC如果编译器遵循ANSI C其值为1否则未定义
2.#define指令 #define name stuff
在文件中用stuff替换name,注意是单独的替换.所以有时会有些你意想不到的错误.注意
如果stuff是多行的话,那么每行的最后要加’\’
注意:你不应该在stuff后面加上分号.因为在调用的时候也会加一个分号,但是这里就出问题了.[两个分号]
宏与函数:
宏只是简单的替换,因此注意操作符的优先级.它的参数不管类型,这点很不错.宏里面用#value[value是参数]直接表示这个参数.##链接两个字符串.宏每次都会把代码加到相应位置,会加大代码量,如果宏的实现是比较长的,那么最好用函数[这个长是上面的stuff很长].注意宏的副作用.可能会出现你意料之外的事情.下面的宏:
#define EVENPARITY(ch) ((count_ont_bits(ch)1)?(ch)|PARITYBIT:(ch));
然后你通过下面的调用就有问题了.ch=EVENPARITY(getchar());//自己想,不会的可以留言
#undef移除一个宏定义,如果一个现存的名字需要重新定义,那么它的旧定义首先必须用#undef移除.
3.条件编译
#if constant-expression
statements
#elif constnt-expression
statements
#else
statements
#endif
上面的#elif可以多次出现[>=0]然后就相当于一般的if语句了.
是否被定义:
#if defined(symbol) #ifdef symbol//作用一样 是否已被定义
_#_if !defined(symbol) #ifndef symbol//是否没被定义
这些可以嵌套.
4.文件包含.包含库文件就直接用’<>’时间少点.包含本地文件用’ “ ‘.用’ “ ‘是首先从本地找,如果没找到再找库函数,’<>’是直接到库函数找.一个头文件如果被包含到10个源文件中,它实际上被编译了10次
可以嵌套包含,标准是编译器必须支持至少8层头文件嵌套.注意包含同一头文件多次的情况[不过这个暂时不知道为什么有错,是不是和编译器有关,我在本地试了下,没错 - -||,哪位看官知道的话,还请告知],下面的方法可以防止同一头文件被多次包含,就是在头文件里面加上一个
#ifndef _XXXX_H(这个名字随便你取)
#define _XXXX_H 1
/你的代码
/
#endif
5.其他指令:#error允许你生成错误信息.#line number “string”指定下一条指令(number是行数,string是文件名,string可以省略),这条指令会修改LINE FILE
#progma用于支持因编译器而异的特性,不过这个指令不可移植
提示:
1.避免用#define指令定义可以用函数实现的很长序列的代码
2.在那些对表达式求值的宏中,每个宏参数出现的地方应该加上括号,并且在整个宏定义的两边也加上括号
3.避免使用#define 宏创建一种新语言
4.采用命名约定,使程序员很容易看出某个标示符是否为#define宏[全大写]
5.只要合适就应该使用文件包含,不必担心它的额外开销
6.头文件只应该包含一组函数和(或)数据的申明
7.把不同集合的声明分离到不同的头文件中可以改善信息隐藏
8.嵌套的#include文件使我们很难判断源文件之间的依赖关系

2011-04-09
C和指针13章 高级指针话题

本章的东西比较少,主要是关于指针的高级应用.对于指针的应用有一点就必须要注意了.运算符的顺序,比如怎么写是指针数组,怎么写是指向数组的指针.
1.int f();//一个函数,返回值是int
int (f)();//一个函数指针,指向一个返回值是int的函数.注意这里我省略了参数
int
(f)();//一个函数指针,指向的函数的返回值是int
对于这些可以这么理解,int (f)();把f看成一个整体,是个函数,然后f就变成了指向这个整体的一个指针.
int (f[])();//f是一个数组,数组的每个元素是一个指针类型,指针指向返回值是int 的函数
在Unix系统中,可以用cdecl程序来帮助你解释这些声明.
2.函数指针的两个应用:I.回调函数,大致模板如下:int fun(int a,int (com)(void const a,void const b));这里的com就是一个回调函数,也就是用户需要用fun函数的时候,需要传一个函数的指针过来,而指向的这个函数是由用户来编写的.这里可以参照qsort的cmp函数.
什么时候用回调函数:编写的函数必须能够在不同的时候执行不同类型的工作或者执行只能由函数调用者定义的工作,你都可以使用这个技巧。
注意:回调函数传过来的是一个函数指针,而不是函数本身.这个函数参数必须是void const .在函数里面你必须保证转换成正确的类型.如果想和系统的一些函数保持兼容的话,那么相等返回0.不相等返回1.这个主要好似字符串的时候,这样可以表示3种情况.0:相等,-1:第一个小.1(或者只是一个大于0的书):第一个大.
II.转移表:就是一个数组的每个元素都是函数指针.然后通过下标来访问对应的函数.double (oper_func [])(double,double)={add,sub,mul,div};
转移表要非常注意下标的溢出,一点溢出就可能非常难于调试.
3.main函数的两个参数.这个没什么讲的,argc表示参数个数,argv是参数指针.argv的第一个是文件名.
4.字符串常量,是一个指针常量.”abc”[2]的值是’c’.这里的[2]可以看成是指针+2.这样就很好理解了,下面的函数打印出的’‘随n的不同而不同
void mystery(int n)
{//n有范围限制
n += 5;
n /= 10;
printf(“%s\n”,”**“+10-n);
}
本章编程提示总结:
1.如果并非必要,避免使用多层间接访问
2.cdecl程序可以帮助你分析复杂的声明
3.把void *强制转换为其他类型的指针时必须小心
4.使用转移表时,应该始终验证下标的有效性.[可以在调用函数的开始和结束输出一些有意义的话语]
5.破坏性的命令行参数处理方式使你以后无法再次进行处理
6.不寻常的代码始终应该加上一条注释,描述它的目的和原理

2011-04-06
C和指针12章 链表

1.链表分为单链表和双链表.其中单链表只能从表头到表尾操作,双链表可以从表头到表尾,也可以从表尾到表头,也可以一时从表头到表尾,一时从表尾到表头.链表包括数据域和指针域,指针域指向下一个节点.

2.链表的结构清楚之后就是使用了,主要是插入,删除,改变,查找.这里给出插入的函数,首先我们插入的时候可能插入到表头,可能插入到表尾,也可能插入到表的中间.当然表的中间是最简单的,只需要改变两个指针值就行了,插入到表尾的时候也只要注意下查找时的判断条件就行了,同时是改变两个指针值,但是如果插入的是表头的话,我们呢就要特别处理了,有些人说我们可以单独加一个表头节点,这个节点没有数据域,不过这个有点问题,一是创建的时候要创建一个空表头[这个也好操作?],二是各种操作的时候要跳过这个空表头[这个还是好操作],三是会浪费空间.下面是单链表的插入函数,不过插入到表头的时候,得注意下一些小细节

int sll_insert(Node **linkp,int new_value)//这里是指向Node的指针的指针,因为会可能插入到表头

{

Node *current;

Node *new;

//寻找正确的插入位置,方法是按顺序访问链表,直到到达一个其值大于或等于

//新值的节点.这个函数会重复插入

while((current=*linkp)!=NULL && current->value<new_value)        linkp=&current->link;

//为新节点分配内存,并把新值存储到新节点中,如果内存分配失败,

//函数返回FALSE(FALSE表示0 TRUE表示1)

new = (Node *)malloc(sizeof(Node));

if(NULL == new)

  return FALSE;

new->value=new_value;

//在链表中插入新节点 并返回TRUE

new->link=current;

*linkp=new;

return TRUE;

}

这个函数考虑了插入位置是表头表尾表中的情况,不过这个函数有很大的隐患.主要是这里面的linkp=&current->link;这条语句.取地址存在很大的隐患,C语言取地址既威力巨大,又暗伏凶险.C语言的指针哲学:给你锤子,实际上你可以使用好几种锤子,祝你好运.

3.双链表可以单独拿两个指针来存表头和表尾 也可以单独拿出一个节点存.这样会浪费数据域.这里就看你的取舍了,当然你可以把指针域单独拿出来放在一个struct里面.然后Node这样struct里面包括上面那个struct和一个数据域.这里只需要一个结构标签的不完整声明就OK了.下面给出完整的插入函数

typedef struct a

{

struct a *fwd;

struct a *bwd;

int value;

}Node;下面用到的结构体

int dll_insert(register Node *rootp,int value)

{

register Node *this;

register Node *next;

register Node *newnode;

//查找value是否已经存在于链表中,如果是就返回(这个函数不会重复插入)

//否则,为新值创建一个新节点("newnode"将指向它)

//"this"将指向应该在新节点之前的那个节点

//"next"将指向应该在新节点之后的那个节点

for(this=rootp;(next=this->fwd)!=NULL;this=next)

   {

      if(next->value==value)

        return 0;

      if(next->value>value)

        break;

   }

newnode=(Node *)malloc(sizoef(Node));

if(NULL == newnode)

  return -1;

newnode->value=value;

//把新节点添加到链表中

newnode->fwd=next;

  this->fwd=newnode;

 if(this != rootp)

  newnode->bwd=this;

else

  newnode->bwd=NULL;

if(next != NULL)

  next->bwd=newnode;

else

  rootp->bwd=newnode;

return 1;

}

本章的警告总结:

1.落到链表尾部的外面

2.使用指针时应该格外小心,因为C并没有对它们的使用提供安全网

3.从if语句中提炼语句可能会改变测试结果

2011-04-02
C和指针11章 动态内存分配

1.动态分配和数组的比较:数组方便,简单,但是不灵活会造成很大的浪费,还有就是如果数组开的太小了 没办法容纳后面的数据,这些缺点可以通过动态分配内存来搞定,不过动态分配也有一定的麻烦,易错,而且是那种可能你找好久都不能找出来的错误,因为有指针.
2.相关函数:malloc,free,realloc,calloc.
void malloc(size_t size);//size_t是无符号型
void free(void
point);
void realloc(void ptr,size_t new_size);
void calloc(size_t num_element,size_t element_size);
malloc是分配size个字节连续空间,如果不能分配返回NULL 这些内容不会被初始化
realloc改变prt的大小,改变为new_size个字节,如果是增大的话,保留原先的数据内容,然后增加,如果时间小的话,那么留下的那一段数据内容不变,这里减小的话,只能是释放掉后面的空间.如果不能在原来的内存上操作的话,就会新分配一个new_size的内存,把数据复制过去,这样的话用了realloc之后ptr就不能再用了,得用返回的这个指针了
calloc:分配num_element个数据所需的内存,每个数据的字节是 element_size. 这些内容会被初始化为0
free是释放传入指针所分配的内存,不过这块内存必须是上面三个函数之一分配的,还有就是不能释放一部分,如果想释放一部分可以用realloc函数
分配内存是最好用sizeof,这样移植性好一点,比如malloc(100
sizeof(int));
3.一般错误:I.对NULL指针的解引用(分配之后没有判断是否返回的指针为NULL) II.对分配的内存操作时越界 III释放并非动态分配的内存,试图释放动态分配的内存的一部分 IV.释放的内存继续使用.
4.内存泄露就是该释放的内存没有释放,会导致内存一点点被榨干,直到再次重启程序或计算机.这样的后果可能会导致当前已经完成的工作统统丢失
5.字符串动态分配内存是千万别忘了考虑结尾NUL字符
下面给出一个书上的例子,可以防止那些不检测malloc返回指针是否为NULL的情况
alloc.h
#include
#define malloc //不直接使用malloc
#define MALLOC(num,type) (type )alloc((num)(sizeof(type)))
extern void *alloc(size_t size);
alloc.c
#include

#include”alloc.h”

#undef malloc//注意顺序我一开始把这句放在上面一句的前面结果很诡异的错误.
void alloc(size_t size)
{
void new_mem;
new_mem=malloc(size);
if(new_mem==NULL)
{
printf(“fjkds;afkjds;af”);//自己改成需要的
exit(1);
}
return new_mem;
}

2011-03-30
C和指针 第十章 结构和联合

1.数组和结构:数组是相同类型的元素的集合,每个元素通过下标引用或指针间接访问来选择.结构也是一些值的集合,这些值是它的成员,但成员可以是不同的类型.
2警惕下面的陷阱:
typedef struct
{
int a;
SELF_REF3 b;
int c;
}SELF_REF3;
这个声明的目的是为这个结构创建类型名SELF_REF3.但是,它失败了.类型名直到声明的末尾才定义,所以在结构声明的内部它尚未定义
3.如果把一个结构体当做参数传到函数里面或者当做返回值传出来的话,你可以选择使用结构体指针来提升效率,因为传值和返回值的话会先拷贝一份,这样的话,如果结构体的字节数大的话,这样速度会提升不少.当然也可以声明为寄存器类型.
4.位段,I.声明的时候如果最好别声明为int 二是直接写成signed和unsigned.因为int会根据具体的机器来决定是有符号还是无符号.II.位段中位的最大数目,许多编译器把位段成员的长度限制在一个整形的长度之内,所以一个能够运行于32位整形的机器上的位段声明可能在16位的整数机器上无法运行.III.位段的成员在内存中可能从右到左分配,也可能从左到右分配.IV.当一个声明指定了两个位段,第2个位段比较大,无法容纳于第1个位段剩余的位时,编译器有可能把第2个位段放在内存的下一个字,也可能直接放在第1个位段后面,从而在两个内存位置的边界上形成重叠.V.位段能够把长度为奇数的数据包装在一起,节省存储空间.当程序需要使用成千上万的这类结构时,这种节省方法就会变得相当重要.
5.联合:长度和最长的那个变量的长度一样.联合的初始化,必须是联合的第一个成员的类型,而且它必须位于一对花括号里面.
下面给出一个比较易错的东西.
1.sizeof(结构体)到底是多少.
比如strcut{char a;int b;char c}A;struct{int a,char b,char c}B;sizeof(A)和sizeof(B)是多少,下面是一个简单分析

答案是第一个是12,第二个是8.知道为什么吗?首先你要知道整型占4个字节.起始存储位置必须能够被4整除,然后第一个的话,首先a占了1个,然后b只能从下一个字开始,也就是会浪费三个字节,同理c也会浪费3个字节.但是第二个的话,首先a是占4个字节,然后b和c各占一个字节,这样的话,就只占了2个字.

还有一个就是要注意’
‘ ‘.’和’->’的先后顺序,比如nodes.a和(nodes).a的区别.

2011-03-30
C和指针第9章[字符串 字符 字节]

1字符串是一串0个和多个字符,以NUL字符结尾的
2.strlen函数的返回值是一个无符号整形,也就是说这个if(strlen(x)-strlen(y)>=0)一定是成立的,因为左边是无符号整形的操作,不可能小于0
3.strcpy,strcat会在新字符串后面自动加上一个’\0’结尾符.但是这些函数需要保证有足够的空间.这两个函数的返回值是第一个参数的一份拷贝.
4.strcmp,返回不一定是 1 -1 0,标准只是说返回大于0的和小于0的,不一定是1和-1.也不要写成if(strcmp(a,b))这样的,因为当a和b相等的时候这个返回的是0,所以有时你会很郁闷
5.strncpy(dst,src,len),如果strlen(src)=len的话,那么就不会以NUL字符结尾,这个切记.
6.strncat(dst,src,len)和strncpy不一样,它不管dst的空间够不够,总是会把len个字符连接在dst后面[如果strlen(str)= ‘A’ & ch <= ‘Z’)但是可以用if(isupper(ch))
11.memcpy(void dst,void cosnt src,size_t len);//dst和src不可重叠
memmove(void dst,void const str,size_t len);//dst和src可重叠,速度没上一个快
memcmp(void const dst,void const src,size_t len);
memchr(void const a,int ch,size_t len)
memset(void
a,int ch,size_t len);
前4个和str开头的功能类似,不过这里不适以NUL结尾,而是len个字节,最后一个是把len字节置为ch.
这里有一个区分差别的.
memchr(buff,0,SIZE)-buffer
strlen(buff)
关于这两个的区别.答案如下

如果buff以NUL字符结尾,那么没区别,都是得到buff的长度,不过strlen返回的是无符号数,但是减法得到的是有符号的.
如果buff不以NUL字符结尾的话,memchr返回一个NULL指针,减去buff将产生一个无意义的结果,strlen则会一直找下去,知道发现一个NUL字符位置.
还有一个就是如果你要找的是NUL字符的话,就得用内存操作函数了,因为字符串函数不能找到NUL字节.

2011-03-30
C字符串二维指针的一个问题

看C和指针的时候看到一个这样的函数:

int find_char(char string,int val)
{
assert(string != NULL);
while(string != NULL)
{
while((string)!= ‘\0’)
{
if((string)++ == val)
return 1
}
string++
}
return 0
}

书上说这个函数有副作用,只适合一次查询的情况,由于本人愚笨,一时没看出来到底是怎么搞的,于是今天上机一试,才知道原来是((string)++ == val)这句的问题,这句会把找到之前的那些字符都给过滤掉,之后就再也找不回来了,就好像被大灰狼叼走之后一样~~~.我的想法是string是一个一维指针,是由string这个二维指针通过间接访问得到的,所以在函数里面改过之后,在原数组也是会有效果的,就好像一个一维数组,然后*num=5这样的语句一样,不过这里的间接访问之后还是一个指针而已.如果有什么错误,还请看官指出.这里附上我做试验的string和输出
string = {“abc”,”cb”,”de”}//当然你不能直接这么赋值
然后我输出整个string,格式如下:

while(string != NULL)
{
printf(“%s\n,string);
string++
}2

结果如下

e//上面的下划线表示没有输出.