yunshichen

我相信人生是值得活的,尽管人在一生中必须遭受痛苦,卑劣,残酷,不幸和死亡的折磨,我依然深信如此.但我认为人生不一定要有意义,只是对一些人而言,他们可以使人生有意义. ---J 赫胥黎

实例教程:1小时学会Python

     摘要: 适合有经验的程序员快速学会Python  阅读全文

posted @ 2008-05-09 02:23 Chenyunshi 阅读(26685) | 评论 (7)编辑 收藏

C语言速记 附录B 资源引用

     摘要: 附录B 参考书和网络资源  阅读全文

posted @ 2008-05-04 17:27 Chenyunshi 阅读(353) | 评论 (0)编辑 收藏

C语言速记 第五章 标准库函数

     摘要: 标准库函数介绍和用法  阅读全文

posted @ 2008-05-04 17:17 Chenyunshi 阅读(930) | 评论 (1)编辑 收藏

C语言速记 附录A 格式速查

     摘要: 有用的查询  阅读全文

posted @ 2008-05-04 17:10 Chenyunshi 阅读(356) | 评论 (1)编辑 收藏

C语言速记 第四章 练习

     摘要: 一些有趣的练习.管中窥豹,可以大致推断C标准库的实现.  阅读全文

posted @ 2008-05-01 15:16 Chenyunshi 阅读(342) | 评论 (0)编辑 收藏

C语言速记 第三章 拾遗补阙

Chapter 3 拾遗补阙

3.1 标准输入的缓冲区   

    当你使用标准输入函数,如getchar,gets,scanf时,请注意缓冲区问题.简言之,当你使用如下函数时:
int c=getchar();
   
    编译器会将键盘输入的字符存储到系统的缓冲区,再从缓冲区返回一个字符给c变量.在这个语句,实际上你至少输入了两个字符(第二个字符是回车).这个回车符号保存在缓冲区里.当你再次使用getchar或者gets时,这些函数会检查缓冲区,发现缓冲区还有字符,于是就直接读取这个字符,而不是读入键盘输入.

    也就是说,这些函数实际上并不是读入键盘输入的字符,而是先检查缓冲区.请观察以下这个错误的例子:
    puts("Input a char:");
    
int c=getchar();

    puts(
"What char is it?");
    putchar(c);
    putchar(
'\n');

    c
=getchar();
    putchar(c);

    system(
"pause");

    这个程序的本意是从键盘输入两次字符并打印.但你会发觉控制台只让你输入一次字符就自动结束.原因是第二个getchar发现缓冲区还有字符(上一个getchar留下的回车符号),所以它不等键盘输入就直接返回.
   
    所以,为了确保程序运行正确,每次运行getchar,gets,scanf..等函数时,先清空标准输入的缓冲区,语法如下:
    fflush(stdin);

    这个例子会加深你的了解:
#include <stdio.h>
#include 
<stdlib.h>
#include 
<ctype.h>
#include 
<string.h>
#include 
<stdarg.h>
int main() {
    puts(
"Input a char:");
    
int c=getchar();//Experiment to input a line instead of a char , and comment fflush(stdin) to watch the reslut.
    puts("What char is it?");
    putchar(c);
    putchar(
'\n');

    fflush(stdin);

    
char s[20];
    puts(
"Input a line:");
    gets(s);

    puts(
"What line is it?");
    puts(s);

    fflush(stdin);

    system(
"pause");

    
return 0;
}





posted @ 2008-05-01 15:09 Chenyunshi 阅读(334) | 评论 (0)编辑 收藏

C语言速记 第二章 指针

Chapter 2 指针

2.1 变量与内存地址


    我们都很熟悉变量的声明赋值语句,例如:
    int a=5;
   
    在这个简单的语句中,编译器实际上做了如下大量的工作:
  1. 创建变量名 a
  2. 为变量分配存储空间.假设这段空间的地址是0xFF6600
  3. 在地址空间存入值"5"
    当然,以上描述是抽象性的,不涉及实际的技术细节.

    当我们使用这个变量,例如打印a的值时,编译器会做如下工作:
  1. 查找变量a
  2. 查找变量a的地址空间
  3. 从地址空间取值
    在许多"现代"的高级编程语言里,声明变量,取值,一切都显得很自然,因为编译器隐藏了变量关于地址的细节.而C将地址细节提供给程序员,鼓励程序员写出更快效率更高的程序.


2.2 指针(Pointer)的概念

    为了精确的理解指针,请区分变量值和变量的精确含义.变量a的值是字面量(literal text)5 ,这个"5"不能再改变. 但是变量a的值可以改变为6,7,8...等等.有时候你可以把变量理解为一个小仓库,里面的东西可以搬来搬去.

    指针正是这样一个小仓库.不同的是char类型变量存储字符串的值,int类型存储数值型值,而地址类型(指针)存储地址的值.例如如下我们声明一个指针pa,并用&符号取出a地址并赋给pa:
    int a=5;
    int* pa=&a;
   
    用一句简单的printf,你可以看到pa的值,即a的地址:
    printf("Value of pa is:%x",pa);
   
    当然,由于pa本身也是变量,根据2.1的描述,变量本身也有地址,我们可以试试打印pa的地址如下:
    printf("Address of pa is:%x",&pa);
   
    *用于指针前表示取值,即"取指针所存储地址所存储的值".例如a的值是5,a的地址是0x66ff00,pa的值是0x66ff00,则*pa表示0x66ff00上所存储的值,也就是5.所以*pa==5;为了避免这种拗口的叫法,通常*pa也称为"取pa所指向变量的值."

    为了帮助你更好的理解指针,请确保自己理解如下几个概念:
  • 地址:变量所分配到的存储空间.例如char型的存储空间是1字节,int型是4字节(在现代32位操作系统).
  • 变量的值:变量所被分配存储空间所存放的字面量.例如字符型的'a','b','c',数值型的1,2,3,地址型的0xFFCC00,0xFFCCFF 等.
  • 指针:地址的变量.指针的值不是普通的如1,2,3,'c','d','f' ... 等字面量,而是内存的地址.
  • & :从变量中取出地址.
  • * :取pa所指向变量的值.
    国内一些不太精确的教科书经常将"指针"和"指针变量"概念混用,让人瞠目不知所云.根据如上的精确定义,"指针"应指地址变量,"指针变量"应指地址变量的变量.所以"指针"并不等于"指针变量".请观察如下例子:

    int a=5;
    int* pa=&a;
    int* ppa=&pa;
   
    上例中,a是变量,pa是指针,ppa可称为指针变量,或指针的指针.有趣的是,当你声明指针的指针时,观察如下例子:

    int*************************************************** ppa=&pa;

    在我的gcc3.45中编译运行正确.

    这里的讨论并不仅仅为了咬文嚼字.回想过去学习指针的经历,许多国内教材的翻译水平让我抓狂.如果你实在厌烦了玩弄文字把戏,你可以到官方网上进行学习: www.cplusplus.com

    最后,请运行这个例子以加深巩固本章节的学习(为方便对比,我将地址值以10进制形式输出):

#include <stdio.h>
#include 
<stdlib.h>


int main() {

    
int a=5;
    
int* pa=&a;

    printf(
"Value of a is : %d\n",a);
    printf(
"Address of a is : %d\n",&a);
    
    printf(
"Value of pa is : %d\n",pa);
    printf(
"Value of which pa pointed to is : %d\n",*pa);
    printf(
"Address of pa itself is : %d\n",&pa);
    
    
int*************************************************** ppa=&pa;
    printf(
"Value of ppa is : %d\n",ppa);
    printf(
"Value of which ppa pointed to is : %d\n",*ppa);
    printf(
"Address of ppa itself is : %d\n",&ppa);
    
    
    
    
    
//system("pause");
    return 0;
}

   

2.3 指针和数组

   
    数组和指针的关系极其紧密.数组由一系列类型相同的元素组成,这些元素的地址是连续的.事实上,数组名本身就是一个指针,只不过该指针的值不能再更改(称为常量指针).

    以下这个例子会让你加深理解:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int arr[]= { 3, 7, 9, 11, 1, 6, 7, 5, 4, 2 };

    printf("1.What's arr? %d\n", arr);
    printf("2.What's &arr? %d\n", &arr);
    printf("3.What's &arr[0]? %d\n\n", &arr[0]);

    printf("4.What's arr[-2]? %d\n", arr[-2]);
    printf("5.What's arr[2]? %d\n\n", arr[2]);

    int* pa=&arr;
    pa+=4;
    printf("6.What's *pa? %d\n", *pa);
    printf("7.What's pa[2]? %d\n", pa[2]);
    printf("8.What's pa[-2]? %d\n", pa[-2]);

    return 0;
}


    有一个特性你必须知道,当数组作为函数的形参时,它实际上是一个指针.在紧接着数组声明后用sizeof函数,你可以得到数组的总地址空间,而在函数内,你用sizeof仅得到指针本身的大小(32位机器上是4字节). 例如:

#include <stdio.h>
#include <stdlib.h>

int strlen(char* a){
    printf("Size of a is:%d\n",sizeof(a));
    int c=0;
    while(*a++)c++;
    return c;
}

int main() {
    char arr[]="Hello,world!";
    printf("Size of arr is:%d\n",sizeof(arr));
   

    //This couldn't be compiled.
    //arr++;
   
    int l=strlen(&arr);
    printf("Length of array is:%d\n",l);

    return 0;
}





2.4 指针和字符串


    声明字符串可以用数组或指针方式.但这两种方式存在差异.例如:

    char a[20]="Hello,world";
    //a="hello";//This couldn't be compiled.
    a[0='s'//OK.

    char* str="Hello,world";
    str
="Another world!";
    str++;//OK.   

    //*str='s';//This couldn't be compiled.
    //str[0]='s';//This couldn't be compiled.
   
    总结数组方式和指针方式声明字符串的区别如下:
  • 数组方式有实际的空间,所以可以单独改变元素值.而指针方式不能.
  • 虽然都是指针变量.但指针方式的指针可以运算,而数组方式不能.
  • 指针方式中的指针可以指向另一个字符串,而数组方式的指针不能.所以要改变一个数组的值,只能逐个元素进行改变.
    以下这个例子演示了如何使用系统函数strcpy

#include <stdio.h>
#include 
<stdlib.h>
#include 
<string.h>

int main() {
    
char* a="I'm a big enough buffer from string copied";
    
char b[80];
    
    
char* s="This is a beautiful world.";
    
    
char* t=NULL;
    
//t = strcpy(a,s);//Couldn't be run!
    printf("t is : %s\n",t);
    
    t 
=strcpy(b,s);
    printf(
"t is : %s\n",t);    

    
return 0;
}



 

    技术上,指针形式定义的字符串变量实际上指向常量的字符串,该常量不能改变.有关常量和指针的关系,我们在下一节继续讨论.



2.5 指针和常量

   
    常量是什么就不多加讨论了.见如下例子:

#include <stdio.h>
#include 
<stdlib.h>

int main() {
    
const int a=5;
    printf(
"a is:%d\n",a);
    
//a=6;//This will not be compiled.
}


    常量(的)指针(pointer to constant )即指向常量的指针.也就是说,假设*p==5,进行*p=6的操作会失败.

    相反,指针(的)常量(constant pointer )表示指针的值不能改变,而指针指向的对象的值可以改变.

    数组名本身就是一个指针常量,所以数组名不能进行通常的指针运算.而用指针声明字符串时,该指针是指针常量,所以不能再改变各元素的值.

    如果你觉得这个中文意义的区分有点拗口,请牢记代码方式:

#include <stdio.h>
#include 
<stdlib.h>

int main() {
    
int a=5,b=7;
    
const int* c=&a;
    
    
//*c=8;//This couldn't be compiled.
    c=&b;//OK.
    
    
int* const d=&a;
    
*d=10;
    
//d=&a;    //This couldn't be compiled.
    
}


    如果const在int*之前,则该变量是常量指针.const(常量)int*(指针)
    如果const在int*之后,则该变量是指针常量.int*(指针)const(常量) .

    是不是很好记忆,呵呵.

    常量指针有着实际的实用意义.假设某个函数的形参为指针,在我们操作这个指针时,很容易把指针指向的对象值也改变,如果将该指针指向的对象声明为常量(即声明常量指针为形参),以下代码说明一切:

#include <stdio.h>
#include 
<stdlib.h>

int strlen(const int* str){
    
int c=0;
    
while(*str++){
        
//*s='t';//This couldn't be compiled.
        c++;
    }
    
return c;
}

int main() {
    
char s[]="Don't change this string!";
    
    
int l=strlen(s);
    
}



2.6 函数指针(Pointer to function)

    乍一看,C语言并没有"接口"的概念,习惯使用Java的程序员可能对此有点失望.其实,回忆一下"回调函数"的概念(callback function).无论我们学哪种语言,都会被教导我们要将代码写在哪里,才能被编译器编译进而运行.最著名的回调函数就是main函数,我们将代码写在main里,编译器就会运行我们的代码(如果没有错误的话).

    所以,回调函数其实就是接口.在C语言,这通过函数指针来实现.

    先看看函数指针的语法:

int* someFunc1(int a);//一个普通的函数声明
int* (*someFunc2)(int a);//声明函数指针

    必须通过观察代码才能更好理解函数指针的实际作用

#include <stdio.h>
#include 
<stdlib.h>

typedef 
char* (*YOUR_NAME)();

char* fun1(){
    
return "Diego";
}
char* fun2(){
    
return "Chen";
}

void handleName(YOUR_NAME y){
    
char* s=y();
    
    printf(
"Welcome,%s\n",s);    
}

int main() {
    YOUR_NAME y1 
= fun1;
    handleName(y1);
    handleName(fun2);
    
    
return 0;
}



 

    这段代码的知识点有:
  • 如何声明函数指针
  • 如何为函数指针变量赋值.
  • 实际的应用
    粗略地看,以上例子似乎平平无奇,其实不然.稍具Java Servlet编程经验的程序员都知道,处理web请求是一个相当简单的事情----实现Servlet的doGet或者doPost函数则可.也许我们羡慕Servlet面向程序员的简易友好的接口并且想用C语言实现,于是我们考虑如下伪码:
typedef struct REQUEST_STRUCT
typedef 
void (*HANDLE_REQUEST_PROC)(REQUEST_STRUCT req)

void doGet(){
    REQUEST_STRUCT req;
    
//Initializes req.
    
    
//Gets function from implementation by programmer.
    HANDLE_REQUEST_PROC proc = ..//Gets from somewhere.
    
    
//Executes it.
    proc();
    
    
//Continue other operations 

}
   
    如果没有函数指针的帮助,这简直不可能.

    简单的说,函数指针的重要作用在于允许代码在运行时才进行连接.对于一些框架设计工作来说,预定义的供给程序员实现的回调函数是必不可少的,只有函数指针才能达到这个目的.

    函数指针的语法总结如下:
  • 如何声明(对比普通函数的声明).
  • 如何赋值(仅需要函数名,不需要函数参数表)
void (* FUNCTIONS) (int a,int b);//声明函数指针
void func1(int a,int b);//符合该函数指针声明的函数
void func2(int a,int b);//符合该函数指针声明的函数

FUNCTIONS y 
= func1;//赋值
y=func2;//赋值


2.7 指针和动态内存分配

    动态分配的内存地址空间是连续的,分配完的空间会返回起始地址的指针:

char* pstr = (char*)malloc(100*sizeof(char));

    所以,在这个场合你还是会用到指针.关于动态内存的使用会有专题章节进行总结.

2.8 指针用法总结

    C语言里关于指针的应用场合总结如下:
  • 遍历数组元素
  • 引用字符串
  • 提供面向程序员的接口,技术上以函数指针实现.
  • 分配动态内存


posted @ 2008-04-25 18:13 Chenyunshi 阅读(357) | 评论 (0)编辑 收藏

C语言速记 第一章 快速入门

     摘要: Chapter 1 快速入门 1.1 尽快Hello world     这个简单的例子有助于你快速进入C的世界:#include <stdio.h>#include <stdlib.h>#define PI 3.1415926float circle(float r);int main(int argc, char* argv) ...{    float r...  阅读全文

posted @ 2008-04-24 16:26 Chenyunshi 阅读(451) | 评论 (0)编辑 收藏

java2html 用法

一个将JAVA源码转为HTML的小东东.网址和介绍如下:http://www.java2html.de/

ant的定义如下:

<taskdef name="java2html"
  classname="de.java2html.anttasks.Java2HtmlTask"
  classpath="${lib.dir}"
 />
 <java2html
       srcdir="${src.dir}"
       destdir="htmlsource"
       includes="**/*.java"
       style="Kawa"
       showLineNumbers="true"
       showFileName="true"
    overwrite="true"
       showTableBorder="true"
    addLineAnchors="true"
 />

生成结果:

注意:

1 你必须把java2html.jar放到ant的lib包内

2 我把生成的html文件放到htmlsource文件夹,你可以自己更改

下面是生成结果:

007  /**
008    @author  Diegoyun
009    @version  1.0
010    */
011  public class  XMLParser  {
012       private  XMLReader xmlReader =  null ;
013       private  String rootNodeName =  null ;
014 
015       public  XMLParser ( String url throws  XMLParserException  {
016 
017           this .xmlReader =  new  XMLReader ( url ) ;
018           this .rootNodeName = xmlReader.getRootNodeName () ;
019 
020       }

感谢该作者的无私分享.



posted @ 2008-04-11 13:59 Chenyunshi 阅读(304) | 评论 (0)编辑 收藏

仅列出标题
共6页: 1 2 3 4 5 6 
<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(7)

随笔分类

随笔档案

文章分类

相册

搜索

最新评论

阅读排行榜

评论排行榜