笔记 时间2012年2月15日14:35:30

时间:2021-04-01 14:18:26
win  孙鑫笔记
第一节        windows 程序原理介绍
windowsapi   application  programming interface
关于消息和队列  MSDN  微软提供的帮助文件  专业全 MSDN开发帮助ty文档  
typedef   struct  tagMSG   //MSG 的宏定义  
            {
               HWND                 hwnd//资源的标识;  
               UINT //无符号的整型 message;  
               WPRAM               wParam;//char code
               LPARAM              lParam;//kebord 
               DWORD               time;//消息传递的时间
               POINT               pt;//光标位置       
            }
windows  主函数

 intWINAPI WinMain{

                             HINSTANCE   hInstance;
                             HINSTANCE   HprevInstance
                             LPARAM      IpcmdLine;
                             int         nCmdShow; 
                          }
class style
{ 
  WNDCLASS wndclass;
 
 wndclass.hh
 
 
 
 
 
 
 
 
 
 
 
 
 

点点滴滴

strcpy(s1,s2)字符串复制  将2考到1   s1<--s2

strcat(s1,s2)字符串连接  s

strlen(s)字符串长度计算  不算NULL的长度

strcmp(s1,s2)字符串比较!!!

 

strchar(s1,ch)?

strstr?

 

 

字符串的读写

 

Getchar(str)      

Gets(str)        到NULL停止

Putchar(str)

Puts(str)

 

 

Printf(“%s”,str)

Scanf(“%s”,str)

 

指针相当于定以后的数组 数组不需要初始化就可以去使用

字符串 要使用放在const区

 

 

 

 

 

 

  字符串比较  是比的内容不是地址strcmp()

数组字符串定义后面不带NULL

Char【20】=“he”是占三个字符长度

Char 【20】=“h”“e”占两个字节

字符长度跟数组的长度不一样字符数组占用的空间是有的

 

Sizeof计算其内部占用的内存空间

但是里面存在的值未知

Strlen计算的值是字符串的长度

 

 

 

指针定义跟数组定义是一样的 都是指向一定长度单位的数组

引用地址应当非常的小心 因为改变内存的东西 很恐怖

 

返回值是1 的时候表示继续 函数的返回值 还有 任何的返回值

返回值是 0表示结束

 

数组名字本身就是地址

所以在printf的时候不需要再对数组名字(即首地址再取地址)

 

 

在定义函数的时候一定要给这么写 int a,b;不要忘记

两个变量之间有逗号;

 

在接收的时候一定要给接收变量分配一个地址

而在输出的时候需要的是这个变量的名字!

Int a = 5;????????????5占用了a存放的是5

 

 

每个 变量都有一个名字  还有内存空间所存的值  还有就是这个变量的地址

这几个属性!!!!!!!!!!!!!!!!!!!!!

 

用数组接收字符串的时候注意其格式

chara[10]= “CHONGMINGREN”

chara[10]={“cuowude”}

用strlen测试长度的时候并没有包括到NULL所占字符长度

而且NULL还是不只占用一个字节

 

Strlen  准确测出字符的长度

Sizeof  严格测出占用的空间

 

时间 2012年1月7日0:15:49 

 

 

 

 

 

 

 

 

指针本身无类型  当有指向的时候才有类型有意义 才可以被调用

 

 

 

 

时间2012年1月7日8:21:09

 

指针

 

 

 

指针的优点

1.       灵活修改实参

2.       支持动态内存的分配

3.       提高效率

4.       实现缓冲方式文件存取

 

 

指针的意义  不定义是不存在   定以后才存在

 

内存冯诺一曼结构 线性结构

 

 

指针是数据的小名在另外一个空间中存储

指针也是个变量在内存中有一个地方保存他

 

 

Int * P;指向整型值的指针变量!

P++

 

 

指针的强制类型转换

 

 

Printf(“%p”,p)

 

 

强制类型转换-------------------

Void * p;

(int*)(*p)强奸*p

 

 

只有积类型指针才可以转化

 

基类型

Void * f(*p)

         {void * p;

Return p;}

 

 

Void *可以接受任何类型的指针参数  不报失败 void*f(void *p){returnp;}

 

 

 

时间 2012年1月7日13:06:47

 

指针

 

Void *   f(*p){return p;}  只有在函数中才可以定义指针类型;

typedef void SHOW(void *);        定义函数指针 引用直接调用 SHOW;

typedefvoid SHOW(void *);   

show(void*datap){  void   *newdatap =(int*)datap      printf(“show theis :”,*newdatap}   

先宏定义函数指针  用一个类似结构体的方式调用函数指针。

在show函数中的指针式空类型  这样的好处是可以以多种方式调用输出 只不过要先进行强制类型装换  intnewa = (faloat a);

                              Void np = (int ) p;     括号先运算表示结构体转换

 

 

 

时间  2012年1月7日20:25:28

 

 

字符串的接收

 

Gets   puts

Getchar

 

 

指针在使用的时候必须先初始化  定义只能随机给个值

但是随着程序的增大 的时候内存空间先指向安全的位置  到程序变大的时候

会出现异常;

 

数组名是一个指针常量  而指针则是变量 这就是指针对数组的优势

 

指针数组

 

Char  p[10]=”kjasjdlk”  等价于Char   *p=”safkjlk” /*完全等价*/

 

Char  *p[]=”kjsafj”   指针数组

 

      引用函数指针   SHOW *show;

                   定义空指针 

 

void * F(void *f );return; f}

                   引用函数指针  ff(show f)

 

 

 

 

 

 

 

 

 

Main.c

 

#include "darray.h"

#include <stdio.h>

 

void show(void *d)

{

         int *p = (int*)d;

         printf("%d",*p);          // 输出地址d所储存的数据  让 *d 以int型号访问

}

 

int main()

{DARRAY *arr = NULL;     //在整个函数调用之前初始化

         inta;

         arr = init_darray(4); //返回一个指向结构体的指针  返回值是个结构体的首地址;

                             返回这个arr  就可以调用 结构体中的任何内容!!!

                                                                 在这个函数中对结构体进行定义 数据的个数 和

                                                                           每个数据的大小  非常的有用 非常实惠

 

         a=5;insert_darray(arr,&a);  //传递进去结构体的首地址  和  输入数据临时地址

         a=6;insert_darray(arr,&a);     //将临时地址内容插入到结构体中

         a=7;sd       -----*insert_darray(arr,&a);

travsel(arr,show);putchar('\n');

         return0;

 

}

 

 

 

darry.h

 

 

#ifndef _DARRAY_H_

#define _DARRAY_H_

 

#include <stdlib.h>

#include <string.h>

typedef struct darray_st DARRAY;

typedef void SHOW(void *);          //定义函数指针 以SHOW调用

struct darray_st {

         intsize;

         intcount;

         void*datap;

};

DARRAY* init_darray(int);

int insert_darray(DARRAY*,void*);

void travsel(DARRAY *,SHOW *);

#endif

 

 

Darry.c

#include "darray.h"

 

DARRAY* init_darray(int size)   /接收每个data的大小

{

         DARRAY*tmp = malloc(sizeof(DARRAY));

         tmp->size= size;

         tmp->count= 0;

         tmp->datap= malloc(size);

         returntmp;

}

//  返回一个指针 指向结构体的指针

int insert_darray(DARRAY *arr,int *data)

{

         arr->datap= realloc(arr->datap,arr->size*(arr->count+1));

         memcpy(arr->datap+arr->count,data,arr->size);

         arr->count++;  

         return0;

}

 

void travsel(DARRAY *arr,SHOW *show)

{

         int i;

         for(i=0;i<arr->count;i++)

         {

                   show(arr->datap+arr->size*i);

         }

 

 

 

 

 

 

 

 

 

 

 

 

在 定义函数的时候一定要确定这个函数对数据做了什么 需要什么数据 穿进去 

什么数据返回来 才有用

 

 

采用malloc  realloc 可以将数据永久的保存在  堆中

这两个函数是有返回值的

A=malloc(用数字表示的  字节数) 给a分配  的字节数

A是个地址 

Malloc是和指针配合使用 构成数组的

 

Memcpy(要拷贝到的地址,从哪拷贝,拷贝几个)

Memcpy( 新地址,源地址,长度)

Realloc()  给已分配的地址再分配地址

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

BOOK . h          

#ifndef _BOOK_H_
#define _BOOK_H_
#include "darray.h"                
struct book_st{
        int bid;
        char bname[20];
        float price;
};
 
typedef struct book_st BOOK;
 
void print(void *);
 
int cmp_book(void *,void *);
 
int insert_book(DARRAY *,BOOK);
 
void display(DARRAY *);
 
void menu();
 
static BOOK input();
 
int delete_book(DARRAY *,int);
 
void loadFile(DARRAY *);
void saveFile(DARRAY *);
#endif

 

 

Book.c

#include "book.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char filename[100]="data.txt";
 
void print(void *p)
{
         BOOK *b = (BOOK*)p;
         printf("%d\t%s\t%5.2f\n",b->bid,b->bname,b->price);
}
 
int insert_book(DARRAY *lib,BOOK b)
{
         return insert_darray(lib,&b);
}
 
void display(DARRAY *lib)
{
         travsel(lib,print);
}
 
static BOOK input()
{
         BOOK book;
         printf("book info:");
         scanf("%d %s %f",&book.bid,book.bname,&book.price);
         return book;
}
 
int cmp_book(void *p1,void *p2)
{
         BOOK *l = (BOOK*)p1;
         int *b = (int*)p2;
         return l->bid - *b;
}
 
void menu()
{
         char cmd[10];
         int bid=-1;
         DARRAY *darr=init_darray(sizeof(BOOK));
         BOOK book;
         loadFile(darr);
         while(1)
         {
                 printf("please input (add,show,del,exit) command:");
                 fflush(stdin);
                 scanf("%s",cmd);
                 
                 if(!strcmp(cmd,"add"))
                 {
                          book=input();
                          insert_book(darr,book);
                 }
                 else if(!strcmp(cmd,"show"))
                 {
                          display(darr);
                 }
                 else if(!strcmp(cmd,"exit"))
                 {
                          saveFile(darr);
                          destory(darr);
                          exit(0);
                 }
                 else if(!strcmp(cmd,"del"))
                 {
                          printf("please book id:");
                          scanf("%d",&bid);
                          delete_book(darr,bid);
                 }
         }
}
void loadFile(DARRAY *darr)
{
         BOOK book;
         int ret=0;
         FILE *fp=fopen(filename,"r");
         while(1)
         {
                 if(-1!=fscanf(fp,"%d %s %f\n",&book.bid,book.bname,&book.price))
                 {
                          insert_darray(darr,&book);
                 }
                 else
                 {
                          break;
                 }
         }
         fclose(fp);      
}
FILE *wfp;
void sf(void *p)
{
         BOOK *b = (BOOK*)p;
         fprintf(wfp,"%d %s %5.2f\n",b->bid,b->bname,b->price);
}
 
void saveFile(DARRAY *darr)
{
         wfp = fopen(filename,"w");
         travsel(darr,sf);
         fclose(wfp);
}
int delete_book(DARRAY *darr,int id)
{
         delete_darray(darr,&id,cmp_book);
}
 
 
Main。C
 
#include "book.h"
 
int main()
{
         menu();
         
         return 0;
}
Carry。H
 
#ifndef _DARRAY_H_   //定义头文件
#define _DARRAY_H_    //定义头文件
 
struct darray_st{
         int size;
         int count;
         char *datap;    //定义结构体中数据的指针
};                         
 
typedef void (*SHOW)(void *); //定义函数指针  
                                   指针是用SHOW对函数进行调用
typedef int (*CMP)(void *,void *);//定义一个指针
typedef struct darray_st DARRAY;  //定义一个结构体
 
DARRAY* init_darray(int);  定义一个结构体指针 并且 返回值是一个结构
                                            返回值是一个结构体指针
 
int insert_darray(DARRAY*,void*); 定义一个没有返回值的函数
 
void travsel(const DARRAY *,SHOW);   定义一个遍历函数 
 
void destory(DARRAY*);         销毁一个malloc
 
void delete_darray(DARRAY *,void *,CMP);
 
#endif
 
 
Carry.c
#include "darray.h"
#include <stdlib.h>
#include <string.h>
DARRAY * init_darray(int size) //定义一个结构体
{
         DARRAY *tmp = malloc(sizeof(*tmp));//给结构体分配内存空间
                      Malloc不仅给分配大小 返回值还表达了指向
         if(!tmp)          ///如果不是空
         {
                 return NULL;
         }
         tmp->size = size;
         tmp->count = 0;
         tmp->datap = (char *)malloc(size);  强制转换为字符流
         return tmp;        指针初始化(类型 大小)malloc来分配大小
                                  通常把返回值赋给别人以便方便的使用
}
 
int insert_darray(DARRAY *arr,void *data)     //插入新数据 
{
         arr->datap = realloc(arr->datap,arr->size*(arr->count+1));
         if(!arr->datap)           //????
         {
                          return 1; //????
         }
         memcpy(arr->datap+(arr->size*arr->count),data,arr->size);
        将新数据考到新地址
         arr->count++;
         每拷贝一次 次数加1
         return 0;
}
 
void travsel(const DARRAY *arr,SHOW s)   //数据不变
 

// const int *A=num;A=bum;*A=5; //修饰指向的对象,A可变,A指向的变量的值不可变

int const *A; //修饰指向的对象,A可变,A指向的变量的值不可变

int *const A=&num;A=bum;//修饰指针A, A不可变,A指向的地址可变

const int *const A; //指针A和A指向的对象都不可变

 
{
        int i;
        for(i = 0; i < arr->count; i++ )
        {
               s(arr->datap+arr->size*i);
        }
}
 
void destory(DARRAY *darr)
{
        free(darr->datap);
        free(darr);
}
void delete_darray(DARRAY *darr,void *data,CMP cmp)
{
        int i,j;
        for( i = 0; i < darr->count; i++ )
        {
               if(!cmp(darr->datap + (darr->size*i),data))
               {
                       break;
               }
        }
        
        for(j=i;j<darr->count;j++)
        {
               memcpy( darr->datap + darr->size * i,darr->datap + darr->size * (i+1),darr->size );
        }
        
        darr->count--;
        realloc(darr->datap, darr->size*darr->count );
 
}

Standard input stream           stdin  标准输入字符流

Flash stdard  刷新数据
Strcmp  字符串比较
 
Fopen (“文件名”,“读写类型”)!
 
 
在于处理的时候都是以字符串形式存在的;
宏定义求运算表达式  
而函数调运 占用存储空间 不好弄啊;
 
条件编译
保证  函数的调用只有一次
只包含一次
不是无数次包涵
 
 
结构体可以整体赋值  即  book1 =book2
Scanf 交互函数  停留在此的
 
Output
 
指针变量没赋值 就开始赋值救是错的  先初始化 并且
 
 
Malloc  告诉你哪里有你用的地址 
              哪里可以用
(*arr).count   =====  arr->count     都是引用指向值
 
结构体首地址 +1是表示指向 结构体中下一个单元???
 
 
 
 
 
 
 
基础知识回顾
Const  int  a; 表示a 的值只是可读  不可以改变更不可以赋值
 
Static  静态变量  限定调用的范围
Extern 声明全局可调用
 
寄存器变量由cpu直接调用速度很快  一个cpu有十六个寄存器变量
指针必须初始化    由于 指向不明比较危险  可能 给 cpu改乱了 
 
Sizeof(数据类型)
 
 
 
Gcc  -E   文件
Gcc   -S  text.i  -O  test.s   看汇编
Gcc  -C   test.c  -O  test.o  看二进制文件
Gcc  -O    test.o  生成运行的文件名
 
 
基础的知识 
 
 
  Char = ‘a’;不加双引号容易产生歧义
  
 
Gdb调试程序

尚观课堂笔记重点内容

 
 
 
 
 
 
 
 
 
 
 
 
 

char 占 1个字节 (0~255)  。一字节是 256位变量是一种使用方便的占位符,用于引用计算机内存地址。变量是相对于常量来说的,常量是本身就有数值的,不能被赋值或修改,而变量就是在编程过程中可以被赋值的量,就象我们小学的未知数x,y一样。源文件--------.i文件---------.O文件--------可执行文件

CPU MMU(内存管理单元)

内存 如何存储数值

 
Static  静态变量 是  ?????

源文件--------.i文件---------.O文件--------可执行文件

CPU MMU(内存管理单元)

内存 如何存储数值

变量是一种使用方便的占位符,用于引用计算机内存地址。变量是相对于常量来说的,常量是本身就有数值的,不能被赋值或修改,而变量就是在编程过程中可以被赋值的量,就象我们小学的未知数x,y一样。char 占 1个字节 (0~255)  。一字节是 256位源文件--------.i文件---------.O文件--------可执行文件

CPU MMU(内存管理单元)

内存 如何存储数值

Long  与 int的 区别 int 在16位的计算机是2个字节 16位 long 还是 4个字节 32位

Do{

}while();

//一般情况下用于某些常处于运行状态的应用程序

3.静态变量 全局变量(static)

变量前边加个“static”,就摇身变成了静态变量,其存储方式为静态存储,静态变量一定是静态存储形式,但是静态存储形式的不一定就是静态局部变量,例如全局变量也是静态存储形式。

静态变量分为全局静态变量和局部静态变量。

全局静态变量,与全局变量有区别,虽然同为静态存储方式,但是全局静态变量失去了全局的“普遍含义”,它所指的“全局”仅限制在本文件里,而全局变量却是各个文件可见的。关于全局静态变量,我认为也没什么可吸引人注意的了。

静态局部变量,与局部变量是有区别的:

 

1.存储方式不同,前者为静态存储方式,后者为动态存储方式;

2.作用域是一致的,只局限于“模块”或者“代码段”;

 

静态局部变量的最大特点就是作用类似于全局变量,而作用域类似于局部变量,在出了某个函数或者代码段后,生命周期继续延续,与应用程序共生死,当再次回到这个函数或者代码段时,上次走出时的值仍然保存到了现在,所以一般用它来做计数器。

4.自动变量(auto)和寄存器变量(register)、外部变量(extern)

C语言中对变量的说明包括两方面的内容:变量类型以及变量的存储类型。变量类型如:int(整形),char(字符型)是用来说明变量所占用的内存空间的大小。变量存储类型用来说明变量的作用范围。C语言的变量存储类型有:自动类型、寄存器类型、静态类型和外部类型。

Auto修饰符

关键字auto加在变量名及其类型前,用来说明它是自动变量。局部变量是指在函数内部说明的变量(有时也称为自动变量)。用关键字auto进行说明, 当auto省略时,所有的非全程变量都被认为是局部变量, 所以auto实际上从来不用。 局部变量在函数调`

用时自动产生, 但不会自动初始化, 随函数调用的结束, 这个变量也就自动消失了, 下次调用此函数时再自动产生, 还要再赋值, 退出时又自动消失。

register修饰符

寄存器变量常在执行速度很重要的情况下使用。其思想是告诉编译程序把该变量放在一个CPU寄存器中。因为数据在寄存器中操作比在内存中快,这样就提高了程序代码的执行速度。寄存器变量的说明是在变量名及类型之前加上关键字register。值得注意的是取地址运算符&不能作用于寄存器变量。它只能用于整型和字符型变量。

extern修饰符

extern称为外部变量。为了使变量除了在定义它的源文件中可以使用外, 还要 被其它文件使用。因此, 必须将全程变量通知每一个程序模块文件, 此时可用 extern来说明。

例子

通过以上的程序可以理解不同的存储类型的作用范围不同,在程序设计中如何灵活的使用各种不同的存储类型是能够使程序更灵活。

5.Const修饰符 

C语言中的const一直是C语言初学者最头疼的问题,这是因为const在不同位置有不同作用,在不同情况下有不同用法。这让初学者摸不清头脑。今天,和大家一起研究一下const,让它的每个情况都“深入人心”!

3.静态变量 全局变量(static)

变量前边加个“static”,就摇身变成了静态变量,其存储方式为静态存储,静态变量一定是静态存储形式,但是静态存储形式的不一定就是静态局部变量,例如全局变量也是静态存储形式。

静态变量分为全局静态变量和局部静态变量。

全局静态变量,与全局变量有区别,虽然同为静态存储方式,但是全局静态变量失去了全局的“普遍含义”,它所指的“全局”仅限制在本文件里,而全局变量却是各个文件可见的。关于全局静态变量,我认为也没什么可吸引人注意的了。

静态局部变量,与局部变量是有区别的:

 

1.存储方式不同,前者为静态存储方式,后者为动态存储方式;

2.作用域是一致的,只局限于“模块”或者“代码段”;

 

静态局部变量的最大特点就是作用类似于全局变量,而作用域类似于局部变量,在出了某个函数或者代码段后,生命周期继续延续,与应用程序共生死,当再次回到这个函数或者代码段时,上次走出时的值仍然保存到了现在,所以一般用它来做计数器。

4.自动变量(auto)和寄存器变量(register)、外部变量(extern)

C语言中对变量的说明包括两方面的内容:变量类型以及变量的存储类型。变量类型如:int(整形),char(字符型)是用来说明变量所占用的内存空间的大小。变量存储类型用来说明变量的作用范围。C语言的变量存储类型有:自动类型、寄存器类型、静态类型和外部类型。

Auto修饰符

关键字auto加在变量名及其类型前,用来说明它是自动变量。局部变量是指在函数内部说明的变量(有时也称为自动变量)。用关键字auto进行说明, 当auto省略时,所有的非全程变量都被认为是局部变量, 所以auto实际上从来不用。 局部变量在函数调`

用时自动产生, 但不会自动初始化, 随函数调用的结束, 这个变量也就自动消失了, 下次调用此函数时再自动产生, 还要再赋值, 退出时又自动消失。

register修饰符

寄存器变量常在执行速度很重要的情况下使用。其思想是告诉编译程序把该变量放在一个CPU寄存器中。因为数据在寄存器中操作比在内存中快,这样就提高了程序代码的执行速度。寄存器变量的说明是在变量名及类型之前加上关键字register。值得注意的是取地址运算符&不能作用于寄存器变量。它只能用于整型和字符型变量。

extern修饰符

extern称为外部变量。为了使变量除了在定义它的源文件中可以使用外, 还要 被其它文件使用。因此, 必须将全程变量通知每一个程序模块文件, 此时可用 extern来说明。

例子

通过以上的程序可以理解不同的存储类型的作用范围不同,在程序设计中如何灵活的使用各种不同的存储类型是能够使程序更灵活。

5.Const修饰符 

C语言中的const一直是C语言初学者最头疼的问题,这是因为const在不同位置有不同作用,在不同情况下有不同用法。这让初学者摸不清头脑。今天,和大家一起研究一下const,让它的每个情况都“深入人心”!

 
 
 

volatile修饰符

volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,加volatile关键字的变量有关的运算,将不进行编译优化)。

例如:

volatile int i=10;

int j = i;

...

int k = i;

volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。

而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。可变参数列表指针就是保存内存地址的变量指针就是保存内存地址的变量空指针

         我们知道指针就是用来保存内存地址的变量,因此我们定义一个指针后一定要用它来保存一个内存地址,假如我们不那么做,该指针就是一个失控的指针,它可以指向任何地址,并且对该地址的数值进行修改或者删除,后果是非常可怕的

解决的办法是将该指针初始化为0。Int *p=NULL;

 

         3.寄存器区:用来保存栈顶指针和指令指针。

全局区(静态区 static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。全局区(静态区 static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。5.执行函数时的不同

(一)栈:在函数调用时,第一个进栈的是被调用函数下一行的内存地址。其次是函数的参数,假如参数多于一个,那么次序是从右往左。最后才是函数的局部变量。

4.函数指针(基本上作函数的参数)

定义:函数指针是指向函数的变量

Int   (*cal)(inta)         //函数指针

Int   add(int a)

Cal = add;             //将函数指针指向add函数

Int   *cal(inta)         //指针函数

Typedef int  fun(int,int)

Int max(int a,fun *p)

 

 这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下:

typedef struct {

int level; /* fill/emptylevel of buffer */

unsigned flags; /* Filestatus flags */

char fd; /* File descriptor*/

unsigned char hold; /*Ungetc char if no buffer */

int bsize; /* Buffer size */

unsigned char _FAR *buffer;/* Data transfer buffer */

unsigned char _FAR *curp; /*Current active pointer */

unsigned istemp; /*Temporary file indicator */

short token; /* Used forvalidity checking */

} FILE; /* This is the FILE object */

  FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件操作常用的函数见下表函数 功能

fopen() 打开流

fclose() 关闭流

fputc() 写一个字符到流中

fgetc() 从流中读一个字符

fseek() 在流中定位到指定的字符

fputs() 写字符串到流

fgets() 从流中读一行或指定个字符

fprintf() 按格式输出到流

fscanf() 从流中按格式读取

feof() 到达文件尾时返回真值

ferror() 发生错误时返回其值

rewind() 复位文件定位器到文件开始处

remove() 删除文件

fread() 从流中读指定个数的字符

fwrite() 向流中写指定个数的字符

tmpfile() 生成一个临时文件流

tmpnam() 生成一个唯一的文件名

 bExtern  与  static  一内一外
 
 
 
 
 
 
 
 
 
 
 
 
 

4.函数指针(基本上作函数的参数)

定义:函数指针是指向函数的变量

Int   (*cal)(inta)         //函数指针

Int   add(int a)

Cal = add;              //将函数指针指向add函数

Int   *cal(inta)         //指针函数

Typedef int  fun(int,int)

Int max(int a,fun *p)

 

 这种方式的文件操作有一个重要的结构FILE,FILE在stdio.h中定义如下:

typedef struct {

int level; /* fill/emptylevel of buffer */

unsigned flags; /* Filestatus flags */

char fd; /* File descriptor*/

unsigned char hold; /*Ungetc char if no buffer */

int bsize; /* Buffer size */

unsigned char _FAR *buffer;/* Data transfer buffer */

unsigned char _FAR *curp; /*Current active pointer */

unsigned istemp; /*Temporary file indicator */

short token; /* Used forvalidity checking */

} FILE; /* This is the FILE object */

  FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行,此种文件操作常用的函数见下表函数 功能

fopen() 打开流

fclose() 关闭流

fputc() 写一个字符到流中

fgetc() 从流中读一个字符

fseek() 在流中定位到指定的字符

fputs() 写字符串到流

fgets() 从流中读一行或指定个字符

fprintf() 按格式输出到流

fscanf() 从流中按格式读取

feof() 到达文件尾时返回真值

ferror() 发生错误时返回其值

rewind() 复位文件定位器到文件开始处

remove() 删除文件

fread() 从流中读指定个数的字符

fwrite() 向流中写指定个数的字符

tmpfile() 生成一个临时文件流

tmpnam() 生成一个唯一的文件名

 crystal clear  

清晰明了的; 显而易见的

今天装了Ubuntu,但是发现不能上网,开始排查问题:
 
1、首先确定网络连接是否正确,所用的网线是否可以正常工作
 
2、查看网卡是否能正常工作,检测的方法如下:
 
a、ping 127.0.0.1
 
b、ping 静态网关地址
 
c、ping 主机名(hostname就可以看到主机名)
 
3、看网卡地址是否配置正确(ifconfig命令看本地主机的网卡信息,和DNS、Default getway、Sever地址进行比对,看网卡地址是否正确)
 
我的设备问题出在第三个方面,然后修改网卡的配置信息,使用命令如下:(当然利用图形窗口完全可以,我这里只是为了学习Linux而用的)
 
一、使用命令设置ubuntu的ip地址
 
1.修改配置文件blacklist.conf禁用IPV6:
sudo vi /etc/modprobe.d/blacklist.conf
2.在文档最后添加 blacklist ipv6,然后查看修改结果:
cat /etc/modprobe.d/blacklist.conf
3.设置IP(设置网卡eth0的IP地址和子网掩码)
sudo ifconfig eth0 192.168.2.1 netmask 255.255.255.0
4.设置网关
sudo route add default gw 192.168.2.254
5.设置DNS 修改/etc/resolv.conf,在其中加入nameserver DNS的地址1 和 nameserver DNS的地址2 完成。
6.重启网络服务(若不行,请重启ubuntu:sudo reboot):
sudo /etc/init.d/networking restart
7.查看当前IP:
ifconfig
 
二、直接修改系统Ubuntu Linux配置文件
 
网络Ubuntu Linux配置文件是:/etc/network/interfaces
打开后里面可设置DHCP或手动设置静态ip。前面auto eth0,让网卡开机自动挂载。
 
1. 以DHCP方式配置网卡
编辑文件/etc/network/interfaces:
sudo vi /etc/network/interfaces
并用下面的行来替换有关eth0的行:
# The primary network interface - use DHCP to find our address
auto eth0
iface eth0 inet dhcp
用下面的命令使网络设置生效:
sudo /etc/init.d/networking restart
也可以在命令行下直接输入下面的命令来获取地址
sudo dhclient eth0
 
2. 为网卡配置静态IP地址
编辑文件/etc/network/interfaces:
sudo vi /etc/network/interfaces
并用下面的行来替换有关eth0的行:
# The primary network interface
auto eth0
iface eth0 inet static
address 192.168.2.1
gateway 192.168.2.254
netmask 255.255.255.0
#network 192.168.2.0
#broadcast 192.168.2.255
将上面的ip地址等信息换成你自己就可以了.用下面的命令使网络设置生效:
sudo /etc/init.d/networking restart
 
3. 设定第二个IP地址(虚拟IP地址)
编辑文件/etc/network/interfaces:
sudo vi /etc/network/interfaces
在该文件中添加如下的行:
auto eth0:1
iface eth0:1 inet static
address x.x.x.x
netmask x.x.x.x
network x.x.x.x
broadcast x.x.x.x
gateway x.x.x.x
根据你的情况填上所有诸如address,netmask,network,broadcast和gateways等信息:
用下面的命令使网络设置生效:
sudo /etc/init.d/networking restart
 
4. 设置主机名称(hostname)
使用下面的命令来查看当前主机的主机名称:
sudo /bin/hostname
使用下面的命令来设置当前主机的主机名称:
sudo /bin/hostname newname
系统启动时,它会从/etc/hostname来读取主机的名称。
 
5. Ubuntu Linux配置DNS
首先,你可以在/etc/hosts中加入一些主机名称和这些主机名称对应的IP地址,这是简单使用本机的静态查询。要访问DNS 服务器来进行查询,需要设置/etc/resolv.conf文件,假设DNS服务器的IP地址是192.168.2.2, 那么/etc/resolv.conf文件的内容应为:
search chotim.com
nameserver 192.168.2.2
 
6.手动重启网络服务:
sudo /etc/init.d/networking restart
 
 
当然ubuntu是带图形界面的,所以上面关于一些地址的设置也可以像windows一样通过设置来完成,在窗口上方System—>Preference下面找相应的配置选项;另外用命令行修改配置文件用到vi命令时如果不熟悉命令的操作修改起来会很不方便,所以可以选择采用gedit来编辑配置文件。

indent -npro -kr -i8 -ts8-sob -l80 -ss -ncs -cp1 源文件.c

indent -npro -kr -i8  源文件.c

3.Makefile编写

/gcc main.c –ihead

先写个makefile文件

Make(编译)  //执行命令第一行的目标可以从省略

.PHONY:mian

Main(目标规则):main.o(依赖关系)

         Gccmain.o –o main

Main.o:main.s

         Gcc-C  main.s –o main.o

Main.s:main.i

         Gcc–S  main.i  –o main.s

Main.i:main.c

         Gcc–E main.c –o main.i

.PHONY:clean

Clean:

         Rm–rf main main.o main.s main.i

//可以用变量替换的方式

$^(指代依赖文件)  $@(指代目标文件)

 

存放Makefile文件变量

Varoable文件:

CC=gcc

PARAM=-o

TARGETS=test

SOURCE=*.c

CFLAGS=-I include

ECHO=echo

Bulider .mak文件:

SRC_DIR=src

Makefile文件:

include ./variable.mak

include ./bulider.mak

 

$(TARGETS): $(shellpwd)/$(SRC_DIR)/$(SOURCE)

         @$(ECHO)creating... $(TARGETS) please waiting..

         #@$(ECHO)$(shell pwd)

         @$(CC)$^ $(PARAM) $(shell pwd)/bin/$(TARGETS) $(CFLAGS)

 
 
 
 
 
 
 
 
 
 
 
 
GDB调试
 
Gdb 文件名
S
S
S
Bt
Info local
 
 
 
 
 
 
 
 
 
 
 
 
 
声明常量 inconst   int a=5;声明常量等于5;
 
 
 
 
 
 
 
 

1.void 类型

1.概述

许多初学者对C/C++语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说,并详述void及void指针类型的使用方法与技巧。

 

2.void的含义

void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义:

void a;

这行语句编译时会出错,提示“illegal useof type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。

void真正发挥的作用在于:(1)对函数返回的限定;(2) 对函数参数的限定。

众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。例如:

float *p1;

int *p2;

p1 = p2;

其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:

p1 = (float *)p2;

而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:

void *p1;

int *p2;

p1 = p2;

但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错:

void *p1;

int *p2;

p2 = p1;

提示“'=' : cannot convert from 'void *' to 'int *'”。

3.void的使用

下面给出void关键字的使用规则:

规则一 如果函数没有返回值,那么应声明为void类型

 

在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如:

add ( int a, int b ){

return a + b;

}

int main(int argc, char* argv[]){

printf ( "2 + 3 = %d", add ( 2, 3));

}

程序运行的结果为输出:

2 + 3 = 5

这说明不加返回值说明的函数的确为int函数。

林锐博士《高质量C/C++编程》中提到:“C++语言有很严格的类型安全检查,不允许上述情况(指函数不加类型声明)发生”。可是编译器并不一定这么认定,譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确,所以不能寄希望于编译器会做严格的类型检查。

因此,为了避免混乱,我们在编写C/C++程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。

规则二 如果函数无参数,那么应声明其参数为void

在C++语言中声明一个这样的函数:

int function(void){

return 1;

}

则进行下面的调用是不合法的:

function(2);

因为在C++中,函数参数为void的意思是这个函数不接受任何参数。

我们在Turbo C 2.0中编译:

#include "stdio.h"

fun(){

return 1;

}

main(){

printf("%d",fun(2));

}

编译正确且输出1,这说明,在C语言中,可以给无参数的函数传送任意类型的参数,但是在C++编译器中编译同样的代码则会出错。在C++中,不能向无参数的函数传送任何参数,出错提示“'fun' : function does not take 1 parameters”。

所以,无论在C还是C++中,若函数不接受任何参数,一定要指明参数为void。

规则三 小心使用void指针类型

按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:

void * pvoid;

void++; //ANSI:错误

void += 1; //ANSI:错误

//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。

//例如:

int *pint;

int++; //ANSI:正确

int++的结果是使其增大sizeof(int)。

但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致。

因此下列语句在GNU编译器中皆正确:

void++; //GNU:正确

void += 1; //GNU:正确

void++的执行结果是其增大了1。

在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:

void * pvoid;

(char *)pvoid++; //ANSI:正确;GNU:正确

(char *)pvoid += 1; //ANSI:错误;GNU:正确

GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。

规则四 如果函数的参数可以是任意类型指针,那么应声明其参数为void *

典型的如内存操作函数memcpy和memset的函数原型分别为:

void * memcpy(void *dest, const void *src, size_t len);

void * memset ( void * buffer, int c, size_t num );

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void*,而是char *,那才叫真的奇怪了!这样的memcpy和memset明显不是一个“纯粹的,脱离低级趣味的”函数!

下面的代码执行正确:

//示例:memset接受任意类型指针

int intarray[100];

memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0

//示例:memcpy接受任意类型指针

int intarray1[100], intarray2[100];

memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1

有趣的是,memcpy和memset函数返回的也是void *类型,标准库函数的编写者是多么地富有学问啊!

规则五 void不能代表一个真实的变量

下面代码都企图让void代表一个真实的变量,因此都是错误的代码:

void a; //错误

function(void a); //错误

void体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(还有人妖?)。

void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。

4.总结

小小的void蕴藏着很丰富的设计哲学,作为一名程序设计人员,对问题进行深一个层次的思考必然使我们受益匪浅。

 

2.内联函数

#include <stdio.h>

Int inline Func(int a,int b)

{

Return a+b;

}

int main()

{

    Printf("%d\n",func(1,2));

    return 0;

}

//inline只是写在定义的时候就行,声明的时候不用写

 

8c语言实现面向对象

什么事面向对象语言?

在面向对象的程序设计语言中,把所有的事物都看成一个对象。

对象的特点   继承 封装多

 
 
 
 
 
 
 
 
 
 
计算机中为什么要用补码表示一个数 ??
 
 
 
Iden –kr –i8
 
 
1.修饰符
  1.1修饰符const  区别static  预编译是访问不到的!!  
   1.2         Extern  和   auto
 
 
 
 
 
 
 
 
World 简单的编辑用到的快捷键 和插入符号
 
 
 
 
 
 

汇编  语言 

 
 

哎哟,哥们儿,还捣鼓汇编呢?那东西没用,兄弟用VB"钓"一个API就够你忙活个十天半月的,还不一定搞出来。”此君之言倒也不虚,那吾等还有无必要研他一究呢?(废话,当然有啦!要不然你写这篇文章干嘛。)别急,别急,让我把这个中原委慢慢道来:一、所有电脑语言写出的程序运行时在内存中都以机器码方式存储,机器码可以被比较准确的翻译成汇编语言,这是因为汇编语言兼容性最好,故几乎所有跟踪、调试工具(包括WIN95/98下)都是以汇编示人的,如果阁下对CRACK颇感兴趣……;二、汇编直接与硬件打交道,如果你想搞通程序在执行时在电脑中的来龙去脉,也就是搞清电脑每个组成部分究竟在干什么、究竟怎么干?一个真正的硬件发烧友,不懂这些可不行。三、如今玩DOS的多是“高手”,如能像吾一样混入(我不是高手)“高手”内部,不仅可以从“高手”朋友那儿套些黑客级“机密”,还可以自诩“高手”尽情享受强烈的虚荣感--#$%& “醒醒!”

 

  对初学者而言,汇编的许多命令太复杂,往往学习很长时间也写不出一个漂漂亮亮的程序,以致妨碍了我们学习汇编的兴趣,不少人就此放弃。所以我个人看法学汇编,不一定要写程序,写程序确实不是汇编的强项,大家不妨玩玩DEBUG,有时CRACK出一个小软件比完成一个程序更有成就感(就像学电脑先玩游戏一样)。某些高深的指令事实上只对有经验的汇编程序员有用,对我们而言,太过高深了。为了使学习汇编语言有个好的开始,你必须要先排除那些华丽复杂的命令,将注意力集中在最重要的几个指令上(CMP LOOP MOV JNZ……)。但是想在啰里吧嗦的教科书中完成上述目标,谈何容易,所以本人整理了这篇超浓缩(用WINZIP、WINRAR…依次压迫,嘿嘿!)教程。大言不惭的说,看通本文,你完全可以“不经意”间在前辈或是后生卖弄一下DEBUG,很有成就感的,试试看!那么――这个接下来呢?――Here we go!(阅读时看不懂不要紧,下文必有分解)

 

  因为汇编是通过CPU和内存跟硬件对话的,所以我们不得不先了解一下CPU和内存:(关于数的进制问题在此不提)

 

  CPU是可以执行电脑所有算术╱逻辑运算与基本 I/O控制功能的一块芯片。一种汇编语言只能用于特定的CPU。也就是说,不同的CPU其汇编语言的指令语法亦不相同。个人电脑由1981年推出至今,其CPU发展过程为:8086→80286→80386→80486→PENTIUM →……,还有AMD、CYRIX等旁支。后面兼容前面CPU的功能,只不过多了些指令(如多能奔腾的MMX指令集)、增大了寄存器(如386的32位EAX)、增多了寄存器(如486的FS)。为确保汇编程序可以适用于各种机型,所以推荐使用8086汇编语言,其兼容性最佳。本文所提均为8086汇编语言。寄存器(Register)是CPU内部的元件,所以在寄存器之间的数据传送非常快。用途:1.可将寄存器内的数据执行算术及逻辑运算。2.存于寄存器内的地址可用来指向内存的某个位置,即寻址。3.可以用来读写数据到电脑的周边设备。8086 有8个8位数据寄存器,这些8位寄存器可分别组成16位寄存器:AH&AL=AX:累加寄存器,常用于运算;BH&BL=BX:基址寄存器,常用于地址索引;CH&CL=CX:计数寄存器,常用于计数;DH&DL=DX:数据寄存器,常用于数据传递。为了运用所有的内存空间,8086设定了四个段寄存器,专门用来保存段地址:CS(Code Segment):代码段寄存器;DS(DataSegment):数据段寄存器;SS(Stack Segment):堆栈段寄存器;ES(ExtraSegment):附加段寄存器。当一个程序要执行时,就要决定程序代码、数据和堆栈各要用到内存的哪些位置,通过设定段寄存器 CS,DS,SS 来指向这些起始位置。通常是将DS固定,而根据需要修改CS。所以,程序可以在可寻址空间小于64K的情况下被写成任意大小。 所以,程序和其数据组合起来的大小,限制在DS 所指的64K内,这就是COM文件不得大于64K的原因。8086以内存做为战场,用寄存器做为军事基地,以加速工作。除了前面所提的寄存器外,还有一些特殊功能的寄存器:IP(IntructionPointer):指令指针寄存器,与CS配合使用,可跟踪程序的执行过程;SP(Stack Pointer):堆栈指针,与SS配合使用,可指向目前的堆栈位置。BP(BasePointer):基址指针寄存器,可用作SS的一个相对基址位置;SI(Source Index):源变址寄存器可用来存放相对于DS段之源变址指针;DI(DestinationIndex):目的变址寄存器,可用来存放相对于 ES 段之目的变址指针。还有一个标志寄存器FR(Flag Register),有九个有意义的标志,将在下文用到时详细说明。

 

  内存是电脑运作中的关键部分,也是电脑在工作中储存信息的地方。内存组织有许多可存放数值的储存位置,叫“地址”。8086地址总线有20位,所以CPU拥有达1M的寻址空间,这也是DOS的有效控制范围,而8086能做的运算仅限于处理16位数据,即只有0到64K,所以,必须用分段寻址才能控制整个内存地址。完整的20位地址可分成两部份:1.段基址(Segment):16位二进制数后面加上四个二进制0,即一个16进制0,变成20位二进制数,可设定1M中任何一个64K段,通常记做16位二进制数;2.偏移量(Offset):直接使用16位二进制数,指向段基址中的任何一个地址。如:2222(段基址):3333(偏移量),其实际的20位地址值为:25553。除了上述营养要充分吸收外,你还要知道什么是DOS、BIOS功能调用,简单的说,功能调用类似于WIN95 API,相当于子程序。汇编写程序已经够要命了,如果不用MS、IBM的子程序,这日子真是没法过了(关于功能调用详见《电脑爱好者》98年11期)。

 

  编写汇编语言有两种主要的方法:1.使用MASM或TASM等编译器;2.使用除错程序DEBUG.COM。DEBUG其实并不能算是一个编译器,它的主要用途在于除错,即修正汇编程序中的错误。不过,也可以用来写短的汇编程序,尤其对初学者而言,DEBUG 更是最佳的入门工具。因为DEBUG操作容易:只要键入DEBUG回车,A回车即可进行汇编,过程简单,而使用编译器时,必须用到文本编辑器、编译器本身、LINK以及EXE2BIN等程序,其中每一个程序都必须用到一系列相当复杂的命令才能工作,而且用编译器处理源程序,必须加入许多与指令语句无关的指示性语句,以供编译器识别,使用 DEBUG 可以避免一开始就碰到许多难以理解的程序行。DEBUG 除了能够汇编程序之外,还可用来检查和修改内存位置、载入储存和执行程序、以及检查和修改寄存器,换句话说,DEBUG是为了让我们接触硬件而设计的。(8086常用指令用法将在每个汇编程序中讲解,限于篇幅,不可能将所有指令列出)。

 

  DEBUG的的A命令可以汇编出简单的COM文件,所以DEBUG编写的程序一定要由地址 100h(COM文件要求)开始才合法。FOLLOWME,SETP BY SETP(步步回车):

 

  输入 A100 ;从DS:100开始汇编

   2.输入 MOV DL,1 ; 将数值 01h 装入 DL 寄存器

   3.输入 MOV AH,2 ; 将数值 02h 装入 DL 寄存器

   4.输入 INT 21 ; 调用DOS 21号中断2号功能,用来逐个显示装入DL的字符

   5.输入 INT 20 ; 调用DOS 20号中断,终止程序,将控制权交回给 DEBUG

   6.请按 Enter 键

   7.现在已将汇编语言程序放入内存中了,输入 G(运行)

   8.出现结果:输出一个符号。

   ㄖ ←输出结果其实不是它,因WORD97无法显示原结果,故找一赝品将就着。

   Programterminated normally

 

  我们可以用U命令将十六进制的机器码反汇编(Unassemble)成汇编指令。你将发现每一行右边的汇编指令就是被汇编成相应的机器码,而8086实际上就是以机器码来执行程序。

   1.输入 U100,106

  1FED:0100 B201 MOV DL,01

  1FED:0102 B402 MOV AH,02

  1FED:0104 CD21 INT 21

  1FED:0106 CD20 INT 20

   DEBUG可以用R命令来查看、改变寄存器内容。CS:IP寄存器,保存了将执行指令地址。

   1.输入R

   AX=0000BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000

   DS=1FEDES=1FED SS=1FED CS=1FED IP=0100 NV UP EI PL NZ NA PONC

  1FED:0100 B201 MOV DL,01

 

  当程序由DS:100开始执行,那么终止程序时,DEBUG会自动将IP内容重新设定为100。当你要将此程序做成一个独立的可执行文件,则可以用N命令对该程序命名。但一定要为COM文件,否则无法以DEBUG载入。

   输入N SMILE.COM ;我们得告诉DEBUG程序长度:程序从100开始到106,故占用7

   ;字节。我们利用BX存放长度值高位部分,而以CX存放低位部分。

   2.输入RBX ;查看 BX 寄存器的内容,本程序只有7个字节,故本步可省略

   3.输入 RCX  ;查看 CX 寄存器的内容

   4.输入 7  ;程序的字节数

   5.输入 W ;用W命令将该程序写入(Write)磁盘中

 

  修行至此,我们便可以真正接触8086汇编指令了。 当我们写汇编语言程序的时候,通常不会直接将机器码放入内存中,而是打入一串助记符号(Mnemonic Symbols),这些符号比十六进制机器码更容易记住,此之谓汇编指令。助记符号,告诉CPU应执行何种运算。 也就是说,助忆符号所构成的汇编语言是为人设计的,而机器语言是对PC设计的。

 

  现在,我们再来剖析一个可以将所有ASCII码显示出来的程序。

   1. 输入 DEBUG

   2. 输入 A100

   3.输入 MOV CX,0100 ;装入循环次数

   MOVDL,00 ;装入第一个ASCII码,随后每次循环装入新码

   MOVAH,02

   INT 21

   INC DL ;INC:递增指令,每次将数据寄存器 DL 内的数值加 1

   LOOP0105 ;LOOP:循环指令,每执行一次LOOP,CX值减1,并跳

  ;到循环的起始地址105,直到CX为0,循环停止

   INT 20

   4.输入 G即可显示所有ASCII码

   当我们想任意显示字符串,如:UNDERSTAND?,则可以使用DOS21H号中断9H号功能。输入下行程序,存盘并执行看看:

   1.输入 A100

   MOVDX,109 ;DS:DX = 字符串的起始地址

   MOV AH,9;DOS的09h功能调用

   INT 21 ;字符串输出

   INT 20

   DB'UNDERSTAND?$';定义字符串

 

  在汇编语言中,有两种不同的指令:1.正规指令:如 MOV 等,是属于CPU的指令,用来告诉CPU在程序执行时应做些什么,所以它会以运算码(OP-code)的方式存入内存中;2.伪指令:如DB等,是属于DEBUG等编译器的指令,用来告诉编译器在编译时应做些什么。DB(Define Byte)指令用来告诉DEBUG将单引号内的所有ASCII 码放入内存中。使用 9H 功能的字符串必须以$结尾。用D命令可用来查看DB伪指令将那些内容放入内存。

   6.输入 D100

  1975:0100 BA 09 01 B4 09 CD 21 CD-20 75 6E 64 65 72 73 74 ......!. underst

  1975:0110 61 6E 64 24 8B 46 F8 89-45 04 8B 46 34 00 64 19 and$.F..E..F4.d.

  1975:0120 89 45 02 33 C0 5E 5F C9-C3 00 C8 04 00 00 57 56 .E.3.^_.......WV

  1975:0130 6B F8 0E 81 C7 FE 53 8B-DF 8B C2 E8 32 FE 0B C0 k.....S.....2...

  1975:0140 74 05 33 C0 99 EB 17 8B-45 0C E8 D4 97 8B F0 89 t.3.....E.......

  1975:0150 56 FE 0B D0 74 EC 8B 45-08 03 C6 8B 56 FE 5E 5F V...t..E....V.^_

  1975:0160 C9 C3 C8 02 00 00 6B D8-0E 81 C3 FE 53 89 5E FE ......k.....S.^.

  1975:0170 8B C2 E8 FB FD 0B C0 75-09 8B 5E FE 8B 47 0C E8 .......u..^..G..

 

  现在,我们来剖析另一个程序:由键盘输入任意字符串,然后显示出来。db 20指示DEBUG保留20h个未用的内存空间供缓冲区使用。

   输入A100

   MOVDX,0116 ;DS:DX = 缓冲区地址,由DB伪指令确定缓冲区地址

   MOVAH,0A ;0Ah 号功能调用

   INT 21 ;键盘输入缓冲区

   MOVDL,0A ;由于功能Ah在每个字符串最后加一个归位码(0Dh由 Enter

   MOVAH,02 ;产生),使光标自动回到输入行的最前端,为了使新输出的

   INT 21 ;字符串不会盖掉原来输入的字符串,所以利用功能2h加一

   ;个换行码(OAh),使得光标移到下一行的的最前端。

   MOVDX,0118 ;装入字符串的起始位置

   MOVAH,09 ;9h功能遇到$符号才会停止输出,故字符串最后必须加上

   INT 21 ;$,否则9h功能会继续将内存中的无用数据胡乱显示出来

   INT 20

   DB 20 ;定义缓冲区

   送你一句话:学汇编切忌心浮气燥。

 

  客套话就不讲了。工欲善其事,必先利其器。与其说DEBUG 是编译器,倒不如说它是“直译器”,DEBUG的A命令只可将一行汇编指令转成机器语言,且立刻执行。真正编译器(MASM)的运作是利用文本编辑器(EDIT等)将汇编指令建成一个独立且附加名为.ASM的文本文件,称源程序。它是MASM 程序的输入部分。MASM将输入的ASM文件,编译成.OBJ文件,称为目标程序。OBJ文件仅包含有关程序各部份要载入何处及如何与其他程序合并的信息,无法直接载入内存执行。链结程序LINK则可将OBJ文件转换成可载入内存执行(EXEcute)的EXE文件。还可以用EXE2BIN,将符合条件的EXE文件转成COM文件(COM 文件不但占用的内存最少,而且运行速度最快)。

   下面我们用MASM写一个与用DEBUG写的第一个程序功能一样的程序。

   用EDIT编辑一个SMILE.ASM的源程序文件。

   源程序 DEBUG 程序

   prognamsegment

   assumecs:prognam

   org 100hA100

   mov dl,1mov dl,1

   mov ah,2mov ah,2

   int 21hint 21

   int 20hint 20

   prognamends

   end

 

  比较一下:1.因为MASM会将所有的数值假设为十进制,而DEBUG则只使用十六进制,所以在源程序中,我们必须在有关数字后加上代表进制的字母,如H代表十六进制,D代表十进制。若是以字母开头的十六进制数字,还必须在字母前加个0,以表示它是数,如0AH。2.源程序增加五行叙述:prognam segment 与 prognam ends 是成对的,用来告诉MASM 及LINK,此程序将放在一个称为PROGNAM(PROGramNAMe)的程序段内,其中段名(PROGNAM)可以任取,但其位置必须固定。assume cs:prognam 必须在程序的开头,用来告诉编译器此程序所在段的位置放在CS寄存器中。end用来告诉MASM,程序到此结束, ORG 100H作用相当于DEBUG的A100,从偏移量100开始汇编。COM 文件的所有源程序都必须包含这五行,且必须依相同的次序及位置出现,这点东西记下就行,千篇一律。接着,我们用MASM编译SMILE.ASM。

   输入 MASM SMILE ←不用打入附加名.ASM。

  Microsoft (R) Macro Assembler Version 5.10

  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.

   Objectfilename [SMILE.OBJ]: ←是否改动输出OBJ文件名,如不改就ENTER

   Sourcelisting [NUL.LST]: ← 是否需要列表文件(LST),不需要就ENTER

  Cross-reference [NUL.CRF]: ←是否需要对照文件(CRF),不需要则ENTER

   50162 + 403867 Bytes symbol space free

   0Warning Errors ←警告错误,表示编译器对某些语句不理解,通常是输入错误。

   0 SevereErrors ←严重错误,会造成程序无法执行,通常是语法结构错误。

 

  如果没有一个错误存在,即可生成OBJ文件。OBJ中包含的是编译后的二进制结果,它还无法被 DOS载入内存中加以执行,必须加以链结(Linking)。以LINK将OBJ文件(SMILE.OBJ)链结成 EXE 文件(SMILE.EXE)时,。

   1.输入 LINK SMILE ←不用附加名OBJ

  Microsoft (R) Overlay Linker Version 3.64

  Copyright (C) Microsoft Corp 1981, 1988. All rights reserved.

   Run File[SMILE.EXE]: ← 是否改动输出EXE文件名,如不改就ENTER

   ListFile [NUL.MAP]: ← 是否需要列表文件(MAP),不需要则ENTER

  Libraries [.LIB]: ←是否需要库文件,要就键入文件名,不要则ENTER

   LINK :warning L4021: no stack segment← 由于COM文件不使用堆栈段,所以错误信息

  ←"no stack segment"并不影响程序正常执行

 

  至此已经生成EXE文件,我们还须使用EXE2BIN 将EXE文件(SMILE.EXE),转换成COM文件(SMILE.COM)。输入EXE2BINSMILE产生 BIN 文件(SMILE.BIN)。其实 BIN 文件与 COM 文件是完全相同的,但由于DOS只认COM、EXE及BAT文件,所以BIN文件无法被正确执行,改名或直接输入 EXE2BIN SMILE SMILE.COM即可。现在,磁盘上应该有 SMILE.COM 文件了,你只要在提示符号C:>下,直接输入文件名称 SMILE ,就可以执行这个程序了。

 

  你是否觉得用编译器产生程序的方法,比 DEBUG 麻烦多了!以小程序而言,的确是如此,但对于较大的程序,你就会发现其优点了。我们再将ASCII程序以编译器方式再做一次,看看有无差异。首先,用EDIT.COM建立ASCII.ASM 文件。

   prognamsegment ;定义段

   assumecs:prognam ;把上面定义段的段基址放入 CS

   movcx,100h ; 装入循环次数

   mov dl,0; 装入第一个ASCII码,随后每次循环装入新码

   next:mov ah,2

   int 21h

   inc dl;INC:递增指令,每次将数据寄存器 DL 内的数值加 1

   loopnext ; 循环指令,执行一次,CX减1,直到CX为0,循环停止

   int 20h

   prognamends ;段终止

   end ;汇编终止

   在汇编语言的源程序中,每一个程序行都包含三项元素:

    start:mov dl,1 ;装入第一个ASCII码,随后每次循环装入新码

    标识符表达式注解

 

  在原始文件中加上注解可使程序更易理解,便于以后参考。每行注解以“;”与程序行分离。编译器对注解不予理会,注解的数据不会出现在OBJ、EXE或COM文件中。由于我们在写源程序时,并不知道每一程序行的地址,所以必须以符号名称来代表相对地址,称为“标识符”。我们通常在适当行的适当位置上,键入标识符。标识符(label)最长可达31 个字节,因此我们在程序中,尽量以简洁的文字做为标识符。现在,你可以将此ASCII.ASM 文件编译成ASCII.COM 了。1.MASM ASCII,2.LINKASCII,3.EXE2BIN ASCII ASCII.COM。

 

  注意:当你以编译器汇编你设计的程序时,常会发生打字错误、标识符名称拼错、十六进制数少了h、逻辑错误等。汇编老手常给新人的忠告是:最好料到自己所写的程序一定会有些错误(别人告诉我的);如果第一次执行程序后,就得到期望的结果,你最好还是在检查一遍,因为它可能是错的。原则上,只要大体的逻辑架构正确,查找程序中错误的过程,与写程序本身相比甚至更有意思。写大程序时,最好能分成许多模块,如此可使程序本身的目的较单纯,易于撰写与查错,另外也可让程序中不同部份之间的界限较清楚,节省编译的时间。如果读程序有读不懂的地方最好用纸笔记下有关寄存器、内存等内容,在纸上慢慢比划,就豁然开朗了。   下面我们将写一个能从键盘取得一个十进制的数值,并将其转换成十六进制数值而显示于屏幕上的“大程序”。前言:要让8086执行这样的功能,我们必须先将此问题分解成一连串的步骤,称为程序规划。首先,以流程图的方式,来确保整个程序在逻辑上没有问题(不用说了吧!什么语言都要有此步骤)。这种模块化的规划方式,称之为“由上而下的程序规划”。而在真正写程序时,却是从最小的单位模块(子程序)开始,当每个模块都完成之后,再合并成大程序;这种大处著眼,小处著手的方式称为“由下而上的程序设计”。

 

  我们的第一个模块是BINIHEX,其主要用途是从8086的BX寄存器中取出二进制数,并以十六进制方式显示在屏幕上。注意:子程序如不能独立运行,实属正常。

   binihexsegment

   assumecs:binihex

   mov ch,4;记录转换后的十六进制位数(四位)

   rotate:mov cl,4 ;利用CL当计数器,记录寄存器数位移动次数

   rolbx,cl ;循环寄存器BX的内容,以便依序处理4个十六进制数

   moval,bl ;把bx低八位bl内数据转移至al

   andal,0fh ;把无用位清零

   addal,30h ;把AL内数据加30H,并存入al

   cmpal,3ah ;与3ah比较

   jlprintit ;小于3ah则转移

   addal,7h ;把AL内数据加30H,并存入al

  printit:mov dl,al ;把ASCII码装入DL

   mov ah,2

   int21h

   dec ch;ch减一,减到零时,零标志置1

   jnzrotate ;JNZ:当零标志未置1,则跳到指定地址。即:不等,则转移

   int 20h;从子程序退回主程序

   binihexends

   end

 

  利用循环左移指令ROL循环寄存器BX(BX内容将由第二个子程序提供)的内容,以便依序处理4个十六进制数:1. 利用CL当计数器,记录寄存器移位的次数。2.将BX的第一个十六进制值移到最右边。利用 AND (逻辑“与”运算:对应位都为1时,其结果为1,其余情况为零)把不要的部份清零,得到结果:先将BL值存入AL中,再利用AND以0Fh(00001111)将AL的左边四位清零。由于0到9的ASCII码为30h到39h,而A到F之ASCII码为41h到46h,间断了7h,所以得到结果:若AL之内容小于3Ah,则AL值只加30h,否则AL再加7h。ADD指令会将两个表达式相加,其结果存于左边表达式内。标志寄存器(Flag Register)是一个单独的十六位寄存器,有9个标志位,某些汇编指令(大部份是涉及比较、算术或逻辑运算的指令)执行时,会将相关标志位置1或清0,常碰到的标志位有零标志(ZF)、符号标志(SF)、溢出标志(OF)和进位标志(CF)。 标志位保存了某个指令执行后对它的影响,可用其他相关指令,查出标志的状态,根据状态产生动作。CMP指令很像减法,是将两个表达式的值相减,但寄存器或内存的内容并未改变,只是相对的标志位发生改变而已:若 AL 值小于 3Ah,则正负号标志位会置0,反之则置1。 JL指令可解释为:小于就转移到指定位置,大于、等于则向下执行。CMP和JG 、JL等条件转移指令一起使用,可以形成程序的分支结构,是写汇编程序常用技巧。

 

  第二个模块DECIBIN用来接收键盘打入的十进制数,并将它转换成二进制数放于BX 寄存器中,供模块1BINIHEX使用。

   decibinsegment

   assumecs:decibin

   mov bx,0;BX清零

  newchar:mov ah,1 ;

   int 21h;读一个键盘输入符号入al,并显示

   subal,30h ;al减去30H,结果存于al中,完成ASCII码转二进制码

   jl exit;小于零则转移

   cmpal,9d

   jg exit ;左>右则转移

   cbw ;8位al转换成16位ax

   xchgax,bx ;互换ax和bx内数据

   movcx,10d ;十进制数10入cx

   mul cx ;表达式的值与ax内容相乘,并将结果存于ax

   xchgax,bx

   addbx,ax

   jmpnewchar ;无条件转移

   exit:int 20 ;回主程序

   decibinends

   end

   CBW 实际结果是:若AL中的值为正,则AH填入00h;反之,则AH填入FFh。XCHG常用于需要暂时保留某个寄存器中的内容时。

   当然,还得一个子程序(CRLF)使后显示的十六进制数不会盖掉先输入的十进制数。

   crlfsegment

   assumecs:crlf

   movdl,0dh ;回车的ASCII码0DH入DL

   mov ah,2

   int 21h

   movdl,0ah ;换行的ASSII码0AH入AH

   mov ah,2

   int 21h

   int 20 ;回主程序

   crlfends

   end

 

  现在我们就可以将BINIHEX、DECIBIN及CRLF等模块合并成一个大程序了。首先,我们要将这三个模块子程序略加改动。然后,再写一段程序来调用每一个子程序。

   crlfproc near;

   movdl,0dh

   mov ah,2

   int 21h

   movdl,0ah

   mov ah,2

   int 21h

   ret

   crlfendp

 

  类似SEGMENT与ENDS的伪指令,PROC与ENDP也是成对出现,用来识别并定义一个程序。其实,PROC 真正的作用只是告诉编译器:所调用的程序是属于近程(NEAR)或远程(FAR)。 一般的程序是由 DEBUG 直接调用的,所以用INT 20 返回,用 CALL 指令所调用的程序则改用返回指令RET,RET会把控制权转移到栈顶所指的地址,而该地址是由调用此程序的 CALL指令所放入的。

   各模块都搞定了,然后我们把子程序组合起来就大功告成

   decihexsegment ;主程序

   assumecs:decihex

   org 100h

   mov cx,4;循环次数入cx;由于子程序要用到cx,故子程序要将cx入栈

   repeat:call decibin;调用十进制转二进制子程序

   callcrlf ;调用添加回、换行符子程序

   callbinihex ;调用二进制转十六进制并显示子程序

   callcrlf

   looprepeat ;循环4次,可连续运算4次

   movah,4ch ; 调用DOS21号中断4c号功能,退出程序,作用跟INT 20H

   int 21H; 一样,但适用面更广,INT20H退不出时,试一下它

   decibinproc near push cx ;将cx压入堆栈,;

   ┇ exit: pop cx ;将cx还原; retdecibin endp binihex proc near push cx

   ┇ pop cx retbinihex endp crlf proc near

   push cx

   ┇ pop cx retcrlf endpdecihex ends end

 

  CALL指令用来调用子程序,并将控制权转移到子程序地址,同时将CALL的下行一指令地址定为返回地址,并压入堆栈中。CALL 可分为近程(NEAR)及远程(FAR)两种:1.NEAR:IP的内容被压入堆栈中,用于程序与程序在同一段中。2.FAR:CS 、IP寄存器的内容依次压入堆栈中,用于程序与程序在不同段中。PUSH、POP又是一对指令用于将寄存器内容压入、弹出,用来保护寄存器数据,子程序调用中运用较多。堆栈指针有个“后进先出”原则,像PUSH AX,PUSHBX…POP BX,POP AX这样才能作到保护数据丝毫不差。

 

汇编语言超浓缩教程到这要告一段落了,希望能奠定你独立设计的基础。而更多更好的技巧则全依赖你平时的积累了。祝你成功
 
 
 
 
 
 
 
 
 
 
 
 
 
                                

制作MP3中的mciSendstring字符串

               
 
 

          1.打开:

Dim mName as string

mName ="f:""mpeg""mpeg1.avi"

mciSendString "open mName type MPEGVideo Aliasmovie parent %u Style %u notify",0&, 0, 0

 其中:

open            操作命令

mName           全路径文件名

type MPEGVideo  是指打开MPEG,AVI等类型,如果不加这一句,就是打开WAV,MP3等

Alias movie     定义了该操作的别名为movie,后续操作只要指明别名即可

parent %u       源

Style %u        风格、样式

notify          通知

 

  2.播放:

mciSendString "play movie", 0&, 0,0 

mciSendString "play movie fullscreen",0&, 0, 0 '全屏播放

  3.暂停:

mciSendString "pause movie", 0&, 0, 0

  4.继续:

mciSendString "resumemovie", 0&, 0, 0

  5.停止:

mciSendString "stopmovie", 0&, 0, 0

  6.关闭:

mciSendString "close movie", 0&, 0, 0

  7.前进到下一个位置:

mciSendString "step movie", 0&, 0,0     

  8.后退到上一个位置:

mciSendString "step movie reverse", 0&,0, 0

  9.前进或后退 N 個位置(其中 N<0 即表示后退)

mciSendString "step movie by " & str(N),0&, 0, 0  

  10.获取当前播放位置:

Dim ST As String*64

mciSendString "status movie position", st,len(st), 0 

  11. 获取媒体的总长度:

mciSendString "status movie length", st,len(st), 0

l=val(st) 'L就是所播放文件的长度

  12.获取播放信息:

Dim ST As String*64

mciSendString "status movie mode", ST,Len(ST), 0

If Left(ST, 7) = "stopped" Then (处理代码) '播放完毕

  13.循环播放:

mciSendString "play movie repeat", 0&,0, 0

 

 

二、控制声音大小:

Dim V As Long

mciSendString "status movie volume", V, 0, 0'V是获取的音量大小值。

V = 50

mciSendString "setaudio movie volume to "& V, &0, 0, 0 'V是设置的音量值

 

三、控制亮度(如果是播放视频)

Dim B As Long

mciSendString "status movie brightness", B,0, 0 'B是获取的亮度值。

B = 50

mciSendString "setvideo movie brightness to" & B, &0, 0, 0 'B是设置的亮度值

 

四、到指定的位置播放。

Dim P1 as Long, P2 as Long

P1 = 100: P2 = 3000

mciSendString "seek movie to ", P1, 0, 0 'P1是当前起始位置,单位:毫秒

mciSendString "seek movie to start", 0&,0, 0 '定位到开头位置

mciSendString "play movie", 0&, 0, 0 '定位后再播放

或者:

mciSendString "play movie FROM P1 toP2",0&, 0, 0 'P1是起始位置,P2是停止位置。单位:毫秒

 

mciSendString "seek movie to end", 0&,0, 0 '定位到最后位置

 

五、在指定控件上播放视频:

mciSendString "open AVI 文件名 parent hWnd style child", 0&, 0, 0

其中,hWnd 是控件的句柄

执行上述命令之后,影片会被放置在控件的左上角,且影片的大小不受控件大小的影响,如果想要改变

影片播放的位置及大小,可以在執行 play  指令前先执行 put 指令,格式如下:

 

mciendString "put AVI 文件名 window at X Y [Width Height]", 0&, 0, 0

 其中 X  及 Y 参数须填入位置,而 Width 及 Height 参数则填入影片显示出來的宽度及高度

 

六、录音设置:

  录音前,用以下语句初始化

  1.设为8位:

mciSendString "set wave bitpersample 8","", 0, 0

  2.设为11025Hz

mciSendString "set wave samplespersec11025", "", 0, 0

  3.设为立体声:

mciSendString "set wave channels 2","", 0, 0

  4.实现PCM格式(不一定正确):

MCISENDSTRING "set wave format tagpcm","", 0, 0

  5.开始录音:

mciSendString "close movie",0&,0,0

mciSendString "open new type WAVEAudio aliasmovie",0&,0,0

mciSendString "record movie",0&,0,0

  6.保存录音到c:"123.wav

mciSendString "stop movie",0&,0,0

mciSendString "save movieC:""123.wav",0&,0,0

mciSendString "close movie",0&,0,0

 

七、开关光驱:

mciSendString "set cdaudio door open","", 0, 0 '打开

mciSendString "set cdaudio door close","", 0, 0 '关闭

 

 

 

 

 

 

 

==============================================================================

 

 

 

 

 

 

 

==============

空指针补充内容

1.void 类型

1.概述

       许多初学者对C/C++语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说,并详述void及void指针类型的使用方法与技巧。

 

2.void的含义

       void的字面意思是“无类型”,void *则为“无类型指针”,void*可以指向任何类型的数据。void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义:

       void a;

       这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。

void真正发挥的作用在于:(1)对函数返回的限定;(2) 对函数参数的限定。

       众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。例如:

float *p1;

int *p2;

p1 = p2;

其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to'float *'”,必须改为:

p1 = (float *)p2;

而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:

void *p1;

int *p2;

p1 = p2;

但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错:

void *p1;

int *p2;

p2 = p1;

提示“'=' : cannot convertfrom 'void *' to 'int *'”。

3.void的使用

下面给出void关键字的使用规则:

规则一 如果函数没有返回值,那么应声明为void类型

 

在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如:

add ( int a, intb ){

       return a + b;

}

int main(intargc, char* argv[]){

       printf ( "2 + 3 = %d", add ( 2,3) );

}

程序运行的结果为输出:

2 + 3 = 5

这说明不加返回值说明的函数的确为int函数。

林锐博士《高质量C/C++编程》中提到:“C++语言有很严格的类型安全检查,不允许上述情况(指函数不加类型声明)发生”。可是编译器并不一定这么认定,譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确,所以不能寄希望于编译器会做严格的类型检查。

因此,为了避免混乱,我们在编写C/C++程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。

规则二 如果函数无参数,那么应声明其参数为void

在C++语言中声明一个这样的函数:

intfunction(void){

       return 1;

}

则进行下面的调用是不合法的:

function(2);

因为在C++中,函数参数为void的意思是这个函数不接受任何参数。

我们在Turbo C 2.0中编译:

#include"stdio.h"

fun(){

return 1;

}

main(){

       printf("%d",fun(2));

}

编译正确且输出1,这说明,在C语言中,可以给无参数的函数传送任意类型的参数,但是在C++编译器中编译同样的代码则会出错。在C++中,不能向无参数的函数传送任何参数,出错提示“'fun' : function does not take 1parameters”。

所以,无论在C还是C++中,若函数不接受任何参数,一定要指明参数为void。

规则三 小心使用void指针类型

按照ANSI(AmericanNational Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的:

void * pvoid;

void++; //ANSI:错误

void += 1; //ANSI:错误

//ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。

//例如:

int *pint;

int++; //ANSI:正确

int++的结果是使其增大sizeof(int)。

但是大名鼎鼎的GNU(GNU's NotUnix的缩写)则不这么认定,它指定void *的算法操作与char *一致。

因此下列语句在GNU编译器中皆正确:

void++; //GNU:正确

void += 1; //GNU:正确

void++的执行结果是其增大了1。

在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码:

void * pvoid;

(char *)pvoid++;//ANSI:正确;GNU:正确

(char *)pvoid +=1; //ANSI:错误;GNU:正确

GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合ANSI标准。

规则四 如果函数的参数可以是任意类型指针,那么应声明其参数为void *

典型的如内存操作函数memcpy和memset的函数原型分别为:

void *memcpy(void *dest, const void *src, size_t len);

void * memset (void * buffer, int c, size_t num );

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是char *,那才叫真的奇怪了!这样的memcpy和memset明显不是一个“纯粹的,脱离低级趣味的”函数!

下面的代码执行正确:

//示例:memset接受任意类型指针

intintarray[100];

memset (intarray, 0, 100*sizeof(int) ); //将intarray清0

//示例:memcpy接受任意类型指针

int intarray1[100],intarray2[100];

memcpy (intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1

有趣的是,memcpy和memset函数返回的也是void *类型,标准库函数的编写者是多么地富有学问啊!

规则五 void不能代表一个真实的变量

下面代码都企图让void代表一个真实的变量,因此都是错误的代码:

void a; //错误

function(void a);//错误

void体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(还有人妖?)。

void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。正如不能给抽象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。

4.总结

小小的void蕴藏着很丰富的设计哲学,作为一名程序设计人员,对问题进行深一个层次的思考必然使我们受益匪浅。

 

 

 

 

5.该函数有四个参数:

第一个参数:要发送的命令字符串。字符串结构是:[命令][设备别名][命令参数].

第二个参数:返回信息的缓冲区,为一指定了大小的字符串变量.

第三个参数:缓冲区的大小,就是字变量的长度.

第四个参数:回调方式,一般设为零

返回值:函数执行成功返回零,否则返回错误代码.

6.

 

 

 

 

 

 

函数指针是怎么调用函数的??

 

BOOLMain_OninitDialog(HWND hwnd,HWND hwndFocus,LPARAM)

HWND  combox = GetDialgItm(hwnd IDC_COMBO1);

ComboBOX_Isertstring(combox,-1.TEXT(“北京“);

ComboBOX_Isertstring(combox,-1.TEXT(“北京“);

ComboBOX_Isertstring(combox,-1.TEXT(“北京“);

SendMassage()

 

 
 
 

如鹏网 c干大事

intAPIENTRY  WinMain{HINSTANCE   hInstance.

                      HINSTANCE   hPreInrestance.
                      LPSTR       lpCmdLine.
                      int         cmdShow
                         }
{
   MessageBox(NULL//句柄,TEXT(“世界你好”),TEXT(“问好”),MB_OK//显示的按钮)->显示使用宏常量表示的  对话框的显示按钮图标MB_ICONQUSTION等量进行定义  .icon  图标);
  return 0;
}
vc是c语言的开发工具  只是一种开发c 和c++的工具
{
            int ret   =   MessageBox(NULL,T,T,MB_OK);
            if(ret==IDYES) {  MessageBox();}
            else           {  Message(NULL,T,T,MB_OK);}
}
 
 int  APIENTRY  WinMain(
                        HINSTANCE hInstance,
                        HINSTANCE hPreInstance,
                        LPSTR     lpCmdLine
                        int       cmdShow
                        )
 
干大事视频笔记
 
 
wsprintf(nstr,“格式”,formerstr);
sprintf(nstr,“格式”,formeerstr);
他们的作用是在win32接口中时用字符串表示所有的东西;
输出文本必须是字符串的形式;这样做才合法;
wsprintf(NUM3,”%i”,i3) 记牢;   格式化 文本格式化 格式化比较两个文本输入是否合法 
计算器核心代码
#include "stdafx.h"
#include <windows.h>
#include <windowsx.h>
#include "MainDlg.h"
BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)//iwillcallyou!    //dontcallme,iwillcallyou!
{
    switch(uMsg)
    {
      HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog);
      HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand);
                HANDLE_MSG(hWnd,WM_CLOSE, Main_OnClose);
    }
 
    return FALSE;
}
 
BOOL Main_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
    return TRUE;
}
 
void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
    switch(id)
    {
            case IDC_OK:
               {
                   TCHAR NUM1[256];
                       TCHAR NUM2[256];
                       TCHAR NUM3[256];
        
                       GetDlgItemText(hwnd,IDC_EDIT1,NUM1,sizeof(NUM1));
                       GetDlgItemText(hwnd,IDC_EDIT2,NUM2,sizeof(NUM2));
                       int i1  =  atoi(NUM1);
                       int i2  =  atoi(NUM2);
                       int  i3;
                       TCHAR NUM1temp[256];
                       TCHAR NUM2temp[256];
                       wsprintf(NUM1temp,"%i",i1);
                       wsprintf(NUM2temp,"%i",i2);
                               if(strcmp(NUM1,NUM1temp)!=0)
                               {
                                    MessageBox(hwnd,TEXT("第一个数格式错误!"),TEXT("警告"),MB_OK);
                                    return;
                               }
                               if(strcmp(NUM2,NUM2temp)!=0)
                               {
                                     MessageBox(hwnd,TEXT("第二个数格式错误 !"),TEXT("警告"),MB_OK);
                                     return;
                               }
                               i3 = i1+i2;
                          wsprintf(NUM3,"%i",i3);
                          SetDlgItemText(hwnd,IDC_EDIT3,NUM3);
               }
        break;
        default:
               break;
    }
}
 
 
void Main_OnClose(HWND hwnd)
{
    EndDialog(hwnd, 0);
}
 
 

比较是否为整数

BOOLIsInt(TCHAR * str)

{  

     int i=atoi(str);  //先转换为整数

     TCHAR strtemp[255];

     wsprintf(strtemp,"%i",i);//整数写入再写入字符串

     if(strcmp(str,strtemp)!=0)//比较是否是整数   在计算机中所有的显示都是以字符串实现的  是数字字符串的话就不会有任何的改变,反之就会有改变!字符串是否相等!

     {

         return FALSE;

     }

     else

     {

         return TRUE;

     }

}

 

#include "stdafx.h"

#include <windows.h>

#include <windowsx.h>

#include "resource.h"

#include "MainDlg.h"

 

BOOL WINAPI Main_Proc(HWND hWnd, UINT uMsg,WPARAM wParam, LPARAM lParam)

{

   switch(uMsg)

    {

       HANDLE_MSG(hWnd, WM_INITDIALOG, Main_OnInitDialog);

       HANDLE_MSG(hWnd, WM_COMMAND, Main_OnCommand);

         HANDLE_MSG(hWnd,WM_CLOSE,Main_OnClose);

    }

 

   return FALSE;

}

 

BOOL Main_OnInitDialog(HWND hwnd, HWNDhwndFocus, LPARAM lParam)

{

   HWND hwndComBox1 = GetDlgItem(hwnd,IDC_COMBO1);

   ComboBox_InsertItemData(hwndComBox1,-1,TEXT("+"));

     ComboBox_InsertItemData(hwndComBox1,-1,TEXT("-"));

     ComboBox_InsertItemData(hwndComBox1,-1,TEXT("*"));

     ComboBox_InsertItemData(hwndComBox1,-1,TEXT("/"));

 

     returnTRUE;

}

BOOLIsInt(TCHAR *str)

{

       int i=atoi(str);

       TCHARstrtemp[256];

       wsprintf(strtemp,"%i",i);

       if (strcmp(str,strtemp)==1)

       {

      

              returnFALSE;

       }

       else

       {

              returnTRUE;

       }

}

void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl,UINT codeNotify)

{

    switch(id)

    {

        caseIDC_OK:

              {

                     TCHARstr1[256];

                     TCHARstr2[256];

                     GetDlgItemText(hwnd,IDC_EDIT1,str1,sizeof(str1));

                     GetDlgItemText(hwnd,IDC_EDIT2,str2,sizeof(str2));

                     inti1 = atoi(str1);

                     inti2 = atoi(str2);

                     inti3=0;

                     HWNDhwndComBox1=GetDlgItem(hwnd,IDC_COMBO1);

                     intcurIndex = ComboBox_GetCurSel(hwndComBox1);

                     switch(curIndex)

                     {

                            case0:

                                   {     

                                          i3=i1+i2;

 

                                   }

                            case1:

                                   {     

                                          i3=i1-i2;

                                         

                                   }

                                   break;

                            case2:

                                   {     

                                           i3=i1*i2;

                                         

                                   }

                                   break;

                            case3:

                                   {     

                                           i3=i1/i2;

                                         

                                   }

                                   break;

 

                           

                            }

                     TCHAR str3[256];

                     if(IsInt(str1)==FALSE)

                     {

                                   MessageBox(hwnd,TEXT("第一个数输入数据不合法"),TEXT("警告"),MB_OK);

              }

              if(IsInt(str2)==FALSE)

              {

                       MessageBox(hwnd,TEXT("第二个数输入数据不合法"),TEXT("警告"),MB_OK);

              }

              itoa(i3,str3,10);

              SetDlgItemText(hwnd,IDC_EDIT3,str3);

 

 

         }

       break;

       default:

         break;

    }

}

 

void Main_OnClose(HWND hwnd)

{

   EndDialog(hwnd, 0);

}

计算器 增删该查

#include"stdafx.h"

#include<windows.h>

#include<windowsx.h>

#include"resource.h"

#include"MainDlg.h"

 

BOOL WINAPIMain_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

    switch(uMsg)

    {

        HANDLE_MSG(hWnd, WM_INITDIALOG,Main_OnInitDialog);

        HANDLE_MSG(hWnd, WM_COMMAND,Main_OnCommand);

              HANDLE_MSG(hWnd,WM_CLOSE,Main_OnClose);

    }

 

    return FALSE;

}

 

BOOLMain_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)

{

    HWND hwndComBox1 =GetDlgItem(hwnd,IDC_COMBO1);

   ComboBox_InsertItemData(hwndComBox1,-1,TEXT("+"));

       ComboBox_InsertItemData(hwndComBox1,-1,TEXT("-"));

       ComboBox_InsertItemData(hwndComBox1,-1,TEXT("*"));

       ComboBox_InsertItemData(hwndComBox1,-1,TEXT("/"));

 

       return TRUE;

}

 

BOOL IsInt(TCHAR*str)

{

       int i =atoi(str);

       TCHAR strtemp[256];

       wsprintf(strtemp,"%i",i);

       if (strcmp(str,strtemp)==1)

       {

      

              return FALSE;

       }

       else

       {

              return TRUE;

       }

}

voidMain_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)

{

    switch(id)

    {

       case IDC_OK:

              {

                     TCHAR str1[256];

                     TCHAR str2[256];

                     GetDlgItemText(hwnd,IDC_EDIT1,str1,sizeof(str1));

                     GetDlgItemText(hwnd,IDC_EDIT2,str2,sizeof(str2));

                     int i1 = atoi(str1);

                     int i2 = atoi(str2);

                     int i3=0;

                     HWNDhwndComBox1=GetDlgItem(hwnd,IDC_COMBO1);

                     int curIndex =ComboBox_GetCurSel(hwndComBox1);

                     switch (curIndex)

                     {

                     case 0:

                            {     

                                   i3=i1+i2;

                                  

                            }

                     case 1:

                            {     

                                   i3=i1-i2;

                                  

                            }

                            break;

                     case 2:

                            {     

                                   i3=i1*i2;

                                  

                            }

                            break;

                     case 3:

                            {     

                                   i3=i1/i2;

                                  

                            }

                            break;

                           

                           

                     }

                     TCHAR str3[256];

                     if (IsInt(str1)==FALSE)

                     {

                            MessageBox(hwnd,TEXT("第一个数输入数据不合法"),TEXT("警告"),MB_OK);

                   return;

              }

              if (IsInt(str2)==FALSE)

              {

                   MessageBox(hwnd,TEXT("第二个数输入数据不合法"),TEXT("警告"),MB_OK);

                   return;

              }

              itoa(i3,str3,10);

              SetDlgItemText(hwnd,IDC_EDIT3,str3);

             

             

         }

         case IDC_BUTTONADD:

              {

                   TCHAR str[256];

                   GetDlgItemText(hwnd,IDC_EDITADD,str,sizeof(str));

                   HWNDhwndedit=GetDlgItem(hwnd,IDC_COMBO1);

                   ComboBox_InsertItemData(hwndedit,-1,str);

                   SetDlgItemText(hwnd,IDC_EDITADD,TEXT(""));

              }

         case IDC_BUTTONDEL:

              {

                   HWNDhwndedit=GetDlgItem(hwnd,IDC_COMBO1);

                   int CurSel=ComboBox_GetCurSel(hwndedit);

                   ComboBox_DeleteString(hwndedit,CurSel);

              }

         case IDC_BUTTONSER:

              {

                   TCHAR strToSerch[256];

                   GetDlgItemText(hwnd,IDC_EDITSER,strToSerch,sizeof(strToSerch));

                   HWNDhwndser=GetDlgItem(hwnd,IDC_COMBO1);

                   int iCount =ComboBox_GetCount(hwndser);

                   int i;

                   BOOL bFound = FALSE;

                   {

                       TCHAR str[256];

                       ComboBox_GetLBText(hwndser,i,str);

                       if(lstrcmp(str,strToSerch)==0)

                       {

                            bFound =TRUE;

                            ComboBox_GetCurSel(hwndser,i);

 

                       }

 

                   }

                   if(bFound)

                   {

                       MessageBox(hwnd,TEXT("找到了"),TEXT("哈哈"),MB_OK);

                   }

 

}

break;

default:

break;

    }

}

 

voidMain_OnClose(HWND hwnd)

{

    EndDialog(hwnd, 0);

}

c语言精彩编程百例

一些c语言需要巩固的知识

2012-1-18 19:36:59

转义字符\   常量字符串“”  常量字符‘’

 

break 和continue的区别

一个是跳出本次循环一个是结束整个for或者while的很多次循环

比如while(1){int a++;if(a>100),break;}全部结束.

比如for(int i=0;i++;i<100){if(i<0)continue;}本次结束

MP3代码加注释

 

// mp3.cpp : Defines the entry point forthe application.

mp3.c

#include "stdafx.h"      //包涵的头文件

int APIENTRY WinMain(HINSTANCEhInstance,     //这里的句柄

HINSTANCE hPrevInstance,   //父句柄

 LPSTR    lpCmdLine,    //pointer to command   line命令行字符串主要指参数

 int      nCmdShow)   //   show  state  of window窗口显示状态,隐藏、最大化等。

{

       InitCommonControls();   //注册并初始化通用控件窗口类

       DialogBox(hInstance,MAKEINTRESOURCE(IDD_MAIN),NULL,MainProc);

       //生命一个图书

       return0;

}

Stdafx.c          

 

#include "stdafx.h"

#include <stdio.h>

#include <mmsystem.h>   

#include <stdlib.h>

#pragma comment(lib,"winmm.lib") 

BOOL CALLBACK MainProc (HWND hDlg, UINTmessage, WPARAM wParam, LPARAM lParam)

{

       staticTCHAR buf[255]="";

       staticint sound_value = 500;

       switch(message)

       {

       caseWM_INITDIALOG:

       {

       }

              return(TRUE);

       caseWM_CLOSE:

        {

                     mciSendString("closemp3",NULL,0,NULL);

           EndDialog(hDlg,0);

       }

              return(TRUE);

       caseWM_COMMAND:

              switch(LOWORD(wParam))

              {

              caseID_OPEN:

           {

                OPENFILENAME ofn;

                            ZeroMemory(&ofn,sizeof(ofn));

                            ofn.lStructSize = sizeof(ofn);

                            ofn.lpstrFilter= "mp3\0 *.mp3\0";

                            ofn.lpstrFile= buf;

                            ofn.lpstrFile[0]='\0';

                            ofn.nMaxFile= sizeof(buf);

                            if(GetOpenFileName(&ofn))

                            {

                                   SetDlgItemText(hDlg,IDC_MP3_LIST,buf);

                                   TCHARcmd[1000];

                                   sprintf(cmd,"open%s alias mp3",buf);

                                   mciSendString(cmd,NULL,0,NULL);

                                   charstr[100]="";

                                   mciSendString("statusmp3 volume",str,100,NULL);

                                   sound_value= atoi(str);

                            }

           }

                     return(TRUE);

              caseID_PLAY:                    

                     mciSendString("playmp3",NULL,0,NULL);

                     return(TRUE);

              caseID_PAUSE:

                     mciSendString("pausemp3",NULL,0,NULL);

                     return(TRUE);

              caseID_STOP:

                     mciSendString("stopmp3",NULL,0,NULL);

                     return1;

              caseID_SOUND_ADD:

                     if(sound_value<1000)

                     {

                            sound_value+=100;

                            sprintf(buf,"setaudiomp3 volume to %d",sound_value);

                            mciSendString(buf,0,0,0);

                            sprintf(buf,"音量大小:%d",sound_value);

                            SetDlgItemText(hDlg,SOUND_SHOW,buf);

                     }

                     return1;

              caseID_SOUND_SUB:

                     if(sound_value>0)

                     {

                                   sound_value-=100;

                                   sprintf(buf,"setaudiomp3 volume to %d",sound_value);

                                   mciSendString(buf,0,0,0);

                                   sprintf(buf,"音量大小:%d",sound_value);

                                   SetDlgItemText(hDlg,SOUND_SHOW,buf);

                     }

                     return1;

              }

              return(FALSE);

       }

       return(FALSE);

}

 

 

Stdafx.h

// stdafx.h : include file for standardsystem include files,

// or project specific include files that are used frequently, but

//     are changed infrequently

//

 

#if!defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)

#defineAFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_

 

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

 

#define WIN32_LEAN_AND_MEAN  

 

#include <windows.h>  

#include <windowsx.h>

#include "resource.h"

#include <COMMCTRL.H>

#include <commdlg.h>

 

#pragmacomment(lib,"comctl32.lib")

 

BOOL CALLBACK MainProc (HWND hDlg, UINTmessage, WPARAM wParam, LPARAM lParam);

 

#endif

 

 

======================================================

 

数据结构2012年2月6日9:19:29

数据结构2012年2月6日9:21:44

线性结构

阿夫曼二叉树

图型结构

gdb调试

bt

I locals

s

bt 1 查看1

display tmp

printf tmp

 

 

 

结构体第一节  链表

next = 0 elm =  0 head=008地址 值是9  next =null(0)   assert 声明开辟空间  指向另外一个地址tmp是指针  e=9 while(null)循环停止  next tmp 018  第一个tmp008  栈消失  堆存在  指针指向下一个地址  链表逻辑连续  物理上存储是不连续的 访问指针中的数据用à

 

 

while (cur->next)//把当前节点指针移动到最后

{

cur=cur->next;

}

 

动态库 使用链表

将链表导入动态库  使用makeinstall 安装动态库

 

 

 

数据结构郝斌

结构体中引用 使用的是book.bid

 

传递进去的一定要是地址才可以改变值

gowithme justlikabird

 

if (动态数组为空)

提示用户

else

输出有效数组

如何判断数组为空bool isempty

   if(0=count)   return ture

typedef 起别名 

typedef struct book BOOK.

 

p指向节点   新的节点

q指针指向节点

p的指针域

p指向的结构体才有指针域

p->pnext 存放的是地址

p->fornt  存放的是地址

r=p->pNext;p->pNext=q;q->pNext=r;

q->pNext=p->pNext;p->pNext=q;

 

 

linklist

#include<stdio.h>

#include<malloc.h>

 

typedef struct NODE

{

    int data;

    struct Node*pNext;

}NODE,*PNODE;

 

int main(void)

{

  PHONE  pHead=NULL;

 pHead=create_list();

 traverse_list(pHead);

  return 0;

}

 

PHONE create_list()

{

int len;

int i;

int val;

 

PNODE pHead=(PNODE)malloc(sizeof(NODE));

if(NULL==pHead)

{

 printf()

}

 

printf(“输入需要生成链表节点的个数:len=”)

scanf(“%d”,&len);

 

for(i=0;i<len;i++)

 {

  printf(“输入第%d个节点的值:”,i+1);

  scanf(“%d”,&val);

 }

}

 

PHONE tranverse_list(PNODE pHead)

{

  

}

 

 

动态分配的值可以被保留

静态的分配值是随机 出站进站被返回给了内存