Thierryxc 的博客

每天进步一点点


  • 首页

  • 归档

  • 标签

Java基础

发表于 2017-08-02

Java的程序结构

Java程序结构

源文件

源文件(.java)带有类的定义。类用来表示程序的一个组件,小程序或许只有一个类。类的内容必须包在花括号里。

类

类中带有一个或多个方法。在Dog这个类中,bark方法带有如何“汪汪”的指令。方法必须在类的内部声明。

方法

在方法的花括号中编写方法应该执行的指令。方法代码是由一组语句所组成,可以把方法想象成一个函数或过程。

程序启动

当Java虚拟机启动执行时,它会寻找你在命令列所指定的类。然后它会锁定一个特定的方法:

public static void main(String[] args) {
    //程序。。。
}  

接着Java虚拟机就会执行main方法在花括号间的函数所有指令。每个Java程序最少都会有一个类以及一个main()。每个应用程序只有一个main()函数。

历程

发表于 2017-07-26

记录我的学习历程

  1. 2016.12.24 —— 2017.2.13
    MIT 计算机科学和Python编程导论、计算思维和数据科学导论
    看完《计算机科学概论》

  2. 2017.02.25 —— 2017.04.09
    尝试HTML、CSS、PHP
    粗读了三本相关教材
    制作了一个简单网站

  3. 2017.04.10 —— 2017.05.30
    学习 coursea 斯坦福机器学习课
    学习《机器学习基石》前两周课程

  4. 2017.05.31 —— 2017.07.31
    学习《C Pirmer Plus》

  5. 2017.08.01 —— 2017.08.20
    学习《Head Frist for Java》

  6. 2017.08.21 —— 2017.09.15
    学习《第一行代码》

  7. 2017.09.15 ——
    html css Javascript Jquery React 完成FreeCodeCamp前端部分

二叉树ADT

发表于 2017-07-26

二叉查找树

       二叉查找树是一种结合了二分查找策略的链接结构。二叉树的每个节点都包含一个项和两个指向其他节点(称为子节点:左节点/右节点)的指针。其顺序按照按照如下规定:左节点的项在父节点的项的前面;右节点的项在父节点的项的后面。

二叉树

二叉树ADT

树定义

二叉查找树接口(tree.h)

接口中的函数是使用二叉树ADT的程序员可以操作的:

#ifndef _TREE_H_
#define _TREE_H_
#include<stdbool.h>

#define SLEN 20
typedef struct item
{
    char petname[SLEN];
    char petkind[SLEN];
} Item;

#define MAXITEMS 10

typedef struct trnode
{
    Item item;
    struct trnode * left;  //指向左分支的指针 
    struct trnode * right; //指向右分支的指针 
} Trnode;

typedef struct tree
{
    Trnode * root;  //指向根结点的指针 
    int size;  //树的项数 
} Tree;

//函数原型

//初始化树

void InitializeTree(Tree * ptree);

//确定树是否为空

bool TreeIsEmpty(const Tree * ptree);

//确定树是否为满

bool TreeIsFull(const Tree * ptree);

//确定树的项数

int TreeItemCount(const Tree * ptree);

//在树中添加一个项

bool AddItem(const Item * pi, Tree * ptree);

//在树中查找一个项

bool InTree(const Item * pi, Tree * ptree);

//在树中删除一个项

bool DeleteItem(const Item * pi, Tree * ptree);

//把函数应用于树中每一项

void Traverse(const Tree * ptree, void (*pfun) (Item item));

//删除树中所有内容

void DeteleAll(Tree * ptree);

#endif

二叉树的实现(tree.c)

//树的支持函数
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"tree.h"

//局部数据类型
typedef struct pair {
    Trnode * parent;
    Trnode * child;
} Pair;

//局部函数原型,这些函数是底层进行的操作
static Trnode * MakeNode(const Item *pi);
static bool ToLeft(const Item *i1, const Item *i2);
static bool ToRight(const Item *i1, const Item *i2);
static void AddNode(Trnode * new_node, Trnode * root);
static void InOrder(const Trnode * root, void (*pfun) (Item item));
static Pair SeekItem(const Item * pi, const Tree * ptree);
static void DeleteNode(Trnode **ptr);
static void DeleteAllNodes(Trnode * root);

//函数定义
void InitializeTree(Tree * ptree)
{
    ptree->root = NULL;
    ptree->size = 0;
}

bool TreeIsEmpty(const Tree * ptree)
{
    if (ptree->root == NULL)
        return true;
    else
        return false;
}

bool TreeIsFull(const Tree * ptree)
{
    if (ptree->size == MAXITEMS)
        return true;
    else
        return false;
}

int TreeItemCount(const Tree * ptree)
{
    return ptree->size;
}

bool AddItem(const Item * pi, Tree * ptree)
{
    Trnode * new_node;

    if (TreeIsFull(ptree))
    {
        fprintf(stderr,"Tree is full.\n");
        return false;
    }
    if (SeekItem(pi, ptree).child != NULL)
    {
        fprintf(stderr,"Attempted to add duplicate item.\n");
        return false;
    }
    new_node = MakeNode(pi);
    if (new_node == NULL)
    {
        fprintf(stderr,"Couldn't create node.\n");
        return false;
    }

    //成功创建!
    ptree->size++;

    if (ptree->root == NULL)   //树为空 
        ptree->root = new_node;
    else                         //树不为空 
        AddNode(new_node, ptree->root);

    return true; 
}

bool InTree(const Item * pi, Tree * ptree)
{
    return (SeekItem(pi, ptree).child == NULL) ? false : true;    
} 

bool DeleteItem(const Item * pi, Tree * ptree)
{
    Pair look;

    look = SeekItem(pi, ptree);
    if (look.child == NULL)
        return false;
    else if (look.parent->left == look.child)
        DeleteNode(&look.parent->left);
    else
        DeleteNode(&look.parent->right);

    ptree->size--;

    return true;
}

void Traverse(const Tree * ptree, void (*pfun) (Item item))
{
    if (ptree != NULL)
        InOrder(ptree->root, pfun);
}

void DeleteAll(Tree * ptree)
{
    if (ptree != NULL)
        DeleteAllNodes(ptree->root);
    ptree->root = NULL;
    ptree->size = 0;
}

//局部函数
static void InOrder(const Trnode * root, void (*pfun) (Item item))
{
    if(root != NULL)
    {
        InOrder(root->left, pfun);
        (*pfun)(root->item);
        InOrder(root->right, pfun);     
    }
}

static void DeleteAllNodes(Trnode * root)//使用递归从左至右删除
{
    Trnode * pright;

    if (root != NULL)
    {
        pright = root->right;
        DeleteAllNodes(root->left);
        free(root);
        DeleteAllNodes(pright);
    }

}

static void AddNode(Trnode * new_node, Trnode * root)//使用递归找符合条件的叶节点
{
    if (ToLeft(&new_node->item,&root->item))
    {
        if (root->left == NULL)
            root->left = new_node;
        else
            AddNode(new_node, root->left);
    }
    else if (ToRight(&new_node->item,&root->item))
    {
         if (root->right == NULL)
             root->right = new_node;
         else
             AddNode(new_node, root->right);
    }
    else
    {
        fprintf(stderr,"Location error in AddNode()\n");
        exit(1);
    }
}

static bool ToLeft(const Item *i1, const Item *i2)
{
    int comp1;

    if ((comp1 = strcmp(i1->petname, i2->petname)) < 0)
        return true;
    else if (comp1 == 0 && strcmp(i1->petkind, i2->petkind) < 0)
        return true;
    else
        return false;    
} 

static bool ToRight(const Item *i1, const Item *i2)
{
    int comp1;

    if ((comp1 = strcmp(i1->petname, i2->petname)) > 0)
        return true;
    else if (comp1 == 0 && strcmp(i1->petkind, i2->petkind) > 0)
        return true;
    else
        return false;    
}

static Trnode * MakeNode(const Item *pi)
{
    Trnode * new_node;

    new_node = (Trnode *) malloc(sizeof(Trnode));
    if (new_node != NULL)
    {
        new_node->item = *pi;
        new_node->left = NULL;
        new_node->right = NULL;
    }

    return new_node;
}

static Pair SeekItem(const Item * pi, const Tree * ptree)
{
    Pair look;
    look.parent = NULL;
    look.child = ptree->root;

    if (look.child == NULL)
        return look;

    while (look.child != NULL)
    {
        if (ToLeft(pi,&(look.child->item)))
        {
            look.parent = look.child;
            look.child = look.child->left;
        }
        else if (ToRight(pi,&(look.child->item)))
        {
            look.parent = look.child;
            look.child = look.child->right;
        }
        else
            break;  //如果前两项都不满足,则必定是相等的情况 
    }

    return look;
}

static void DeleteNode(Trnode **ptr)
{
    Trnode * temp;

    if ((*ptr)->left == NULL)
    {
        temp = *ptr;
        *ptr = (*ptr)->right;
        free(temp);
    }
    else if ((*ptr)->right == NULL)
    {
        temp = *ptr;
        *ptr = (*ptr)->left;
        free(temp);
    }
    else//被删除的节点有两个子节点 
    {
        //找到重新连接右子树的位置
        for (temp = (*ptr)->left; temp->right != NULL; temp = temp->right)
            continue;
        temp->right = (*ptr)->right;
        temp = *ptr;
        *ptr = (*ptr)->left;
        free(temp); 
    }
}

主程序(petclub.c)

//使用二叉树查找数 
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include"tree.h"

char menu(void);
void addpet(Tree * pt);
void droppet(Tree * pt);
void showpets(const Tree * pt);
void findpet(const Tree * pt);
void printitem(Item item);
void uppercase(char * str);
char * s_gets(char *st, int n);

int main(void)
{
    Tree pets;
    char choice;


    InitializeTree(&pets);
    while ((choice = menu()) != 'q')
    {
        switch (choice)
        {
            case 'a': addpet(&pets);
                break;
            case 'l': showpets(&pets);
                break;
            case 'f': findpet(&pets);
                break;
            case 'n': printf("%d pets in club.\n", TreeItemCount(&pets));
                break;
            case 'd': droppet(&pets);
                break;
            default: puts("Switching error");
        }
    }
    DeleteAll(&pets);
    puts("Bye.");

    return 0;
}

char menu(void)
{
    int ch;

    puts("Nerfville Pet Club Membership Program");
    puts("Enter the letter corresponding to your choice:");
    puts("a) add a pet          l) show list of pets");
    puts("n) number of pets     f) find pets");
    puts("d) delete a pet       q) quit");
    while ((ch = getchar()) != EOF)
    {
        while (getchar() != '\n')  /* discard rest of line */
            continue;
        ch = tolower(ch);
        if (strchr("alrfndq",ch) == NULL)
            puts("Please enter an a, l, f, n, d, or q:");
        else
            break;
    }
    if (ch == EOF)       /* make EOF cause program to quit */
        ch = 'q';

    return ch;
}

void addpet(Tree * pt)
{
    Item temp;

    if (TreeIsFull(pt))
        puts("No room in the club!");
    else
    {
        puts("Please enter name of pet:");
        s_gets(temp.petname,SLEN);
        puts("Please enter pet kind:");
        s_gets(temp.petkind,SLEN);
        uppercase(temp.petname);
        uppercase(temp.petkind);
        AddItem(&temp, pt);
    }
}

void showpets(const Tree * pt)
{
    if (TreeIsEmpty(pt))
    puts("No entries!");
    else
        Traverse(pt, printitem);
}

void printitem(Item item)
{
    printf("Pet: %-19s  Kind: %-19s\n", item.petname,
           item.petkind);
}

void findpet(const Tree * pt)
{
    Item temp;

    if (TreeIsEmpty(pt))
    {
        puts("No entries!");
        return;     /* quit function if tree is empty */
    }

    puts("Please enter name of pet you wish to find:");
    s_gets(temp.petname, SLEN);
    puts("Please enter pet kind:");
    s_gets(temp.petkind, SLEN);
    uppercase(temp.petname);
    uppercase(temp.petkind);
    printf("%s the %s ", temp.petname, temp.petkind);
    if (InTree(&temp, pt))
        printf("is a member.\n");
    else
        printf("is not a member.\n");
}

void droppet(Tree * pt)
{
    Item temp;

    if (TreeIsEmpty(pt))
    {
        puts("No entries!");
        return;     /* quit function if tree is empty */
    }

    puts("Please enter name of pet you wish to delete:");
    s_gets(temp.petname, SLEN);
    puts("Please enter pet kind:");
    s_gets(temp.petkind, SLEN);
    uppercase(temp.petname);
    uppercase(temp.petkind);
    printf("%s the %s ", temp.petname, temp.petkind);
    if (DeleteItem(&temp, pt))
        printf("is dropped from the club.\n");
    else
        printf("is not a member.\n");
}

void uppercase(char * str)
{
    while (*str)
    {
        *str = toupper(*str);
        str++;
    }
}
char * s_gets(char * st, int n)
{
    char * ret_val;
    char * find;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st, '\n');   // look for newline
        if (find)                  // if the address is not NULL,
            *find = '\0';          // place a null character there
        else
            while (getchar() != '\n')
                continue;          // dispose of rest of line
    }
    return ret_val;
}

队列ADT

发表于 2017-07-22

C语言中使用抽象数据类型方法编程的三个步骤

  1. 以抽象、通用的方式描述一个类型,包括该类型的操作。
  2. 设计一个函数接口表示这个新类型。
  3. 编写具体代码实现这个接口。

定义队列抽象数据类型

队列(queue)是具有两个特殊属性的链表:

  • 新项只能添加到链表末尾;
  • 只能从链表开头移除项;

队列是一种先进先出(FIFO)的数据形式。

抽象定义:
队列抽象定义

定义接口(queue.h接口头文件)

//Queue的接口
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include<stdbool.h>

typedef int Item;

#define MAXQUEUE 10

typedef struct node{
    Item item;
    struct node * next;
} Node;

typedef struct queue{
    Node * front;   //指向队列首项的指针 
    Node * rear;   //指向队列尾项的指针 
    int items;    //队列中的项数 
} Queue;

//初始化队列
void InitializeQueue(Queue * pq);

//检查队列是否已满
bool QueueIsFull(const Queue * pq);

//检查队列是否为空
bool QueueIsEmpty(const Queue * pq);

//确定队列中的项数
int QueueItemCount(const Queue * pq);

//在队列末尾添加项
bool EnQueue(Item item, Queue * pq);

//在队列开头删除项
bool DeQueue(Item *pitem, Queue * pq);

//清空队列
void EmptyTheQueue(Queue * pq);

#endif   

实现接口函数(queue.c)

//Queue类型的实现
#include<stdio.h>
#include<stdlib.h>
#include"queue.h"

//局部函数
static void CopyToNode(Item item, Node * pn);
static void CopyToItem(Node * pn, Item * pi);

void InitializeQueue(Queue * pq)
{
    pq->front = pq->rear = NULL;
    pq->items = 0;
}

bool QueueIsFull(const Queue * pq)
{
    return pq->items == MAXQUEUE; 
}

bool QueueIsEmpty(const Queue * pq)
{
    return pq->items == 0;    
}

int QueueItemCount(const Queue * pq)
{
    return pq->items;
}

bool EnQueue(Item item, Queue * pq)
{
    Node * pnew;

    if (QueueIsFull(pq))
        return false;
    pnew = (Node *)malloc(sizeof(Node));
    if (pnew == NULL)
    {
        fprintf(stderr, "Unable to allocate memory!\n");
        exit(1);
    }
    CopyToNode(item,pnew);
    pnew->next = NULL;
    if (QueueIsEmpty(pq))
        pq->front = pnew;      //项位于首端 
    else
        pq->rear->next = pnew; //项位于末端 
    pq->rear = pnew;
    pq->items++; //项数+1 

    return true;
}

bool DeQueue(Item * pitem, Queue * pq)
{
    Node * pt;

    if(QueueIsEmpty(pq))
        return false;
    CopyToItem(pq->front, pitem);
    pt = pq->front;
    pq->front = pq->front->next;
    free(pt);
    pq->items--;
    if(pq->items == 0)
        pq->rear == NULL;

    return true;

} 

void EmptyTheQueue(Queue * pq)
{
    Item dummy;
    while(!QueueIsEmpty(pq))
        DeQueue(&dummy, pq);
}

static void CopyToNode(Item item, Node * pn)
{
    pn->item = item;
 }

static void CopyToItem(Node * pn, Item * pi)
{
    *pi = pn->item;
 } 

测试队列(驱动程序 use_q.c)

//驱动程序测试Queue接口

#include<stdio.h>
#include"queue.h"

int main(void)
{
    Queue line;
    Item temp;
    char ch;

    InitializeQueue(&line);
    puts("Testing the Queue interface. Type a to add a value,");
    puts("type d to delete a value, and type q to quit.");
    while ((ch = getchar()) != 'q')
    {
        if (ch != 'a' && ch != 'd')
            continue;//忽略其他输出
        if (ch == 'a')
        {
            printf("Integer to add: ");
            scanf("%d", &temp);
            if (!QueueIsFull(&line))
            {
                printf("Putting %d into queue.\n", temp);
                EnQueue(temp,&line);
            }
            else
                puts("Queue is full!"); 
         }
        else
        {
            if(QueueIsEmpty(&line))
                puts("Nothing to delete.");
            else
            {
                DeQueue(&temp, &line);
                printf("Delete %d from queue.\n", temp);
            }    
        }
        printf("%d items in queue.\n", QueueItemCount(&line));
        puts("Type a to add. d to delete, q to quit:");
    }

    EmptyTheQueue(&line);
    puts("88");

    return 0;
}     

抽象数据类型(ADT)

发表于 2017-07-21

抽象数据类型(ADT)

理论基础

定义新的类型

什么是类型?类型特指两类信息:属性和操作。
假设要定义一个新的数据类型:

  • 首先,必须提供存储数据的方法,例如设计一个结构。
  • 其次,必须提供操控数据的方法。

计算机科学领域已开发了一种定义新类型的好方法,用3个步骤完成抽象到具体的过程。

  1. 提供类型属性和相关操作的抽象描述。这些描述既不能依赖特定的实现,也不能依赖特定的编程语言。这种正式的抽象描述被称为抽象数据类型(ADT)。
  2. 开发一个实现ADT的编程接口。也就是说,指明如何储存数据和执行所需操作的函数。例如在C中,可以提供结构定义和操控该结构的函数原型。这些作用于用户定义类型的函数相当于作用于C基本类型的内置运算符。需要使用该新类型的程序员可以使用这个接口进行编程。
  3. 编写代码实现接口。这一步至关重要,但是使用该新类型的程序员无需了解具体的实现细节。

实现过程

建立抽象

以简单电影项目为例,所需的是一个链表数据类型,每一项包含电影名和评级。所需的操作是把新项添加到链表的末尾和显示链表中的内容。

链表的属性:

  • 能储存一系列的项,并按照一定方式排列;
  • 链表类型应该提供一些有用的操作。

链表一些有用的操作:

  • 初始化一个空链表;
  • 在链表末尾添加一个新项;
  • 确定链表是否为空;
  • 确定链表是否已满;
  • 确定链表的项数;
  • 访问链表的每一项执行某些操作,如显示该项。

对于该电影项目而言,暂时不需要其他操作。但是一般的链表还应包含以下操作:

  • 在链表的任意位置插入一个项;
  • 移除链表中的一个项;
  • 在链表中检索一个项(不改变链表);
  • 用另一个项替换链表中的一个项。

最后该类型总结如下:
简单链表

建立接口

list.h接口头文件:

/* list.h -- header file for a simple list type */
#ifndef LIST_H_
#define LIST_H_
#include <stdbool.h>     

/* 特定程序的声明 */

#define TSIZE      45   //储存电影名的数组大小
struct film
{
    char title[TSIZE];
    int rating;
};

/* 一般类型定义 */

typedef struct film Item;

typedef struct node
{
    Item item;
    struct node * next;
} Node;

typedef Node * List;  //List是指向Node类型的指针

/* 函数原型 */

void InitializeList(List * plist); //plist是指向List类型的指针


bool ListIsEmpty(const List *plist);


bool ListIsFull(const List *plist);


unsigned int ListItemCount(const List *plist);


bool AddItem(Item item, List * plist);


void Traverse (const List *plist, void (* pfun)(Item item) );//pfun是指向无返回值、参数为Item类型的函数的指针


void EmptyTheList(List * plist);

#endif  

使用接口

使用这个接口编写程序,但是不必知道具体细节。
伪代码方案:

创建一个List类型的变量。
创建一个Item类型的变量。
初始化链表为空。
当链表未满且有输入时:
    把输入读取到Item类型的变量中。
    在链表末尾添加项。
访问链表中的每个项并显示他们。  

films.c使用ADT风格的链表

#include<stdio.h>
#include<stdlib.h>  //提供exit()的原型
#include"list.h"    //定义Item、List

void showmovies(Item item);
char * s_gets(char *st, int n);
int main(void)
{
    List movies;
    int count;
    Item temp;

    /* 初始化 */

    InitializeList(&movies);
    if (ListIsFull(&movies))
    {
        fprintf(stderr, "No memory available!\n");
        exit(1);
    }

    /* 获取用户输入并储存 */

    puts("Enter first moive title");
    while (s_gets(temp.title, TSIZE) != NULL && temp.title[0] != '\0')
    {
        puts("Enter your rating <1-10>:");
        scanf("%d", &temp.rating);
        while (getchar() != '\n')
            continue;
        if (AddItem(temp,&movies) == false)
        {
            fprintf(stderr,"Problem allocating memory.\n");
            break;
        }
        if (ListIsFull(&movies))
        {
            fprintf(stderr, "The list is now full.\n");
            break;
        }
        puts("Enter the next title:");    
    }

    /* 显示 */

    if (ListIsEmpty(&movies))
        printf("No data entered.\n");
    else
    {
        printf("Here is the moive list:\n");
        Traverse(&movies, showmovies);
    }



    printf("You entered %d moives.\n", ListItemCount(&movies));

    /* 清理 */

    EmptyTheList(&movies);
    puts("88");

    return 0;
}

void showmovies(Item item)
{
    printf("Moive: %s Rating: %d\n",item.title, item.rating);
}

char * s_gets(char *st, int n)
{
    char *ret_val;
    char *find;

    ret_val = fgets(st, n, stdin);
    if (ret_val)
    {
        find = strchr(st,'\n');
        if (find)
        *find = '\0';
        else
            while (getchar() != '\n')
                continue;

    }
    return ret_val;
}      

实现接口

list.c实现文件

//支持链表操作的函数
#include<stdio.h>
#include<stdlib.h>
#include"list.h"

//局部函数原型
static void CopyToNode(Item item, Node * pnode);//内部链接,只在文件内生效

//接口函数
//把链表设置为空
void InitializeList(List * plist)//plist是指向List的指针,*plist是指向Node的指针
{
    *plist = NULL; 
 }

bool ListIsEmpty(const List * plist)
{
    if (*plist == NULL)
        return true;
    else
        return false;
 }

bool ListIsFull(const List * plist)
{
    Node * pt;
    bool full;

    pt = (Node *)malloc(sizeof(Node));
    if (pt == NULL)
        full = true;
    else
        full = false;
    free(pt);

    return full;
 }

unsigned int ListItemCount(const List * plist)
{
    unsigned int count = 0;
    Node * pnode = *plist;

    while (pnode != NULL)
    {
        ++count;
        pnode = pnode->next;
    }

    return count;
 }

bool AddItem(Item item, List * plist)
{
    Node * pnew;
    Node * scan = *plist;

    pnew = (Node *)malloc(sizeof(Node));
    if(pnew == NULL)
        return false;

    CopyToNode(item, pnew);
    pnew->next = NULL;
    if (scan == NULL)
        *plist = pnew;
    else
    {
        while(scan->next != NULL)
            scan = scan->next;
        scan->next = pnew;
    }

    return true;
 }

//访问每个节点并执行pfun指向的函数
void Traverse(const List * plist, void (*pfun)(Item item))
{
    Node * pnode = *plist;

    while (pnode != NULL)
    {
        (*pfun)(pnode->item);
        pnode = pnode->next;
    }
 }

void EmptyTheList(List * plist)
{
    Node * psave;

    while (*plist != NULL)
    {
        psave = (*plist)->next;
        free(*plist);
        *plist = psave;
    }
 }

static void CopyToNode(Item item, Node * pnode)
{
    pnode->item = item;
 } 

字符串和字符串函数

发表于 2017-07-08

字符串

字符串是以空字符(\0)结尾的char类型数组。

在程序中定义字符串

字符串字面量

  • 用双引号括起来的内容称为字符串字面量,也叫做字符串常量。
char greeting[50] = "Hello!";  
  • 字符串常量属于静态存储类别,这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个生命周期存在。

数组和指针的区别

char ar1[] = "Something is point at me.";
char * pt1 = "Something is point at me.";
  • 在数组形式中,ar1是地址常量。不能更改ar1,如果改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操作。
  • 指针变量(pt1)最初指向该字符串的首字符,但是它的值可以改变。因此可以使用递增运算符,++pt1将指向第二个字符’o’。

总之,初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。

字符串函数

输入、输出函数

puts()

接受一个字符串,输出到屏幕上,末尾自动加换行符。

fgets()和fputs()

fgets(char类型数组,int,文件指针)

  • fgets()函数的第二个参数指明了读入字符的最大数量。如果是n,那么fgets()将读入n-1个字符,或者读到遇到的第一个换行符为止。
  • 如果fgets()读到一个换行符,会把它存储在字符串中。
  • fgets()函数的第三个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数。
  • fgets()返回指向char的指针,如果一切进行顺利,返回的地址应与第一个参数地址相同;若读到文件结尾,返回空指针(NULL)。

fputs(char类型数组,文件指针)

  • fputs()函数的第二个参数指明要写入数据的文件,如要打印在显示器上,用stdout(标准输出)。
  • fputs()不会在末尾加换行符。

字符串函数

strlen()

  • 接受一个字符串作为参数,返回它的长度。

strcat()和strncat()

  • strcat()用于拼接字符串,接受两个字符串作为参数,把第二个字符串的备份附加在第一个字符串末尾,返回第一个参数(即拼接后第一个字符串的地址)。
  • strncat()前两个参数与strcat()相同,第三个参数指定了最大添加字符数。例如,strncat(bugs,addon,13)将把addon字符串的内容附加给bugs,在加到第13个字符或遇到空字符时停止(无论哪种情况都要添加空字符)。

strcmp()和strncmp()

  • strcmp()接收两个字符串作为参数,若相同则返回0,strcmp()依次比较每个字符直到发现不同的字符为止,比较的依据是机器排序序列,即字符的数值,通常是ASCⅡ值(第一个参数的减去第二个参数的)。
  • strncmp()函数在比较两个字符串时,可以比较到字符不同的地方,也可以只比较第3个参数指定的字符数。

strcpy()和strncpy()

  • strcyp()是字符串赋值函数,接受两个字符串指针作为参数,可以把指向源字符串的第2个指针声明为指针、数组或字符串常量;而指向源字符串副本的第一个指针应指向一个数据对象(如,数组),且该对象有足够的空间储存源字符串的副本。声明数组将分配存储数据的空间,声明指针只分配存储一个地址的空间。
  • strcpy()返回类型是 char *,是第一个参数的值,即一个字符的地址。第一个参数不必指向数组的开始,这个属性可以用于拷贝数组的一部分。
  • strncpy()第3个参数指明可拷贝的最大字符数。

sprintf()

  • 第一个参数是一个char类型数组,把获取的输入格式化为表纯形式储存在其中。(类比printf)。

char *strchr(const char * s, int c);

  • 如果s字符串中包含c字符,返回指向s字符串中首次出现c字符的地址,没找到,返回NULL。

char *strrchr(const char * s, int c);

  • 如果s字符串中包含c字符,返回指向s字符串中最后出现c字符的地址,没找到,返回NULL。

char *strpbrk(const char s1, const char s2);

  • 如果s1字符串中包含s2字符串中的任意字符,返回s1中首次出现s2中字符的地址,否则返回NULL。

char *strstr(const char s1, const char s2);

  • 该函数返回s1字符串中s2字符串出现的收位置,否则返回NULL。

存储类别

发表于 2017-06-30

五种存储类型

分配内存:malloc( )和free( )

malloc( )函数

       该函数接受一个参数:所需内存的字节数。malloc( )函数会找到合适的空闲内存块,这样的内存是匿名的。也就是说,malloc( )函数分配内存但是不会为其赋名。malloc( )函数返回动态内存块的首字节地址。因此,可以把该地址赋给一个指针变量,并使用指针访问这块内存。malloc( )函数可用于返回指向数组的指针、指向结构的指针等,应该使用强制类型转换来提高代码可读性。如果malloc( )分配内存失败,返回空指针。

例如,用malloc( )函数创捷数组:

double *pt;  
pt = (double *) malloc(30 * sizeof(double));  

以上代码为30个double请求内存空间,并设置pt指向该位置。

free( )函数

       free( )函数用来释放malloc( )函数分配的内存,free( )函数只释放其参数指向的内存块。

C语言中的指针

发表于 2017-06-30

指针

什么是指针

指针是一个值为内存地址的变量(或数据对象),也就是说,指针变量的值是地址。
假设一个指针变量名是ptr,可以编写如下语句:

ptr = &pooh; //把pooh的地址赋给ptr

对于这条语句,我们说ptr “指向”pooh。ptr是变量(指针),&pooh是常量(地址),所以还可以把ptr指向别处:

ptr = &bah; //把ptr指向bah,而不是pooh  

现在ptr的值是bah的地址。
要创建指针变量(例如ptr),先要声明指针变量的类型。需要用到间接运算符:*。

间接运算符:*

假设已知ptr指向bah,即:

ptr = &bah;

使用间接运算符 * 找出储存在bah中的值,该运算符有时也称作解引用运算符。

val = *ptr; //找出ptr指向的值  

语句ptr = &bah;和val = *ptr;放在一起相当于:

val = bah;  

  • 小结:与指针相关的运算符
    地址运算符:&
    后跟一个变量名时,&给出该变量的地址。
    地址运算符:*
    后跟一个指针名或地址是,*给出储存在指针指向地址上的值。

声明指针

声明指针变量时必须指定指针所指向变量的类型:

int * pi; //pi是指向int类型的变量的指针  
int * pc; //pc是指向char类型的指针

类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int * pi;声明的意思是pi是一个指针,* pi是int类型。

声明并使用指针

指针的类型:
pc指向的值(*pc)是char类型,我们描述pc的类型是指向char类型的指针。pc的只是一个地址。

数组中的指针

数组名是数组首元素的地址。也就是说,如果flizny是一个数组,下面的语句成立:

flizny == &flizny[0]; //数组名是该数组首元素的地址  

注意:flizny 和 &flizny[0]都表示数组首元素的内存地址,它们都是常量 ,在程序的运行过程中不会改变。但是,可以把他们赋值给指针变量,然后可以修改指针变量的值。

数组和指针加法

  • 指针的值是它所指向对象的地址。许多计算机一般都是按字节编址。这里,一个较大对象(如short类型)的地址一般是该对象第一个字节的地址。
  • 在指针前面使用*运算符可以得到该指针所指向对象的值。
  • 指针加1,指针的值递增它所指向类型的大小。

    dates + 2 == &dates[2]  //相同的地址
    *(dates + 2) == dates[2]  //相同的值  
    

       以上关系表明了数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素的值。从本质上看,同一个对象有两种表示法。实际上,C语言标准在描述数组表示法时确实借助了指针。也就是说,定义ar[n]的意思是*(ar + n)。可以认为*(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。

函数、数组和指针

       假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用函数?也许是下面这样:

total = sum(marbles); //可能的函数调用  

       那么,该函数的原型是什么?数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应该把它赋给一个指针形式参数,即该形参是一个指向int的指针:

int sum(int * ar); //对应的函数原型  

       sum()从该参数中获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数。

使用指针形参

指针形参是变量,这意味着可以用索引表明访问数组中的拿一个元素。

#include <stdio.h>
#define SIZE 10
int sump(int * start, int * end);
int main(void)
{
    int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
    long answer;

    answer = sump(marbles, marbles + SIZE);
    printf("The total number of marbles is %ld.\n", answer);

    return 0;
}  
int sump(int * start, int * end)  
{
    int total = 0;

    while (start < end)
    {
        total += *start; // add value to total
        start++;         // advance pointer to next element
    }

    return total;
}  

形参、实参:

        int sump(int start, int end);告诉编译器sump()使用两个参数start和end都是指向int类型的指针。这两个变量属于函数私有,是局部变量。每次调用函数,就会给这些变量赋值,也就是说当main()函数中的nswer = sump(marbles, marbles + SIZE);执行时,同时执行了start = marbles;和 end = (marbles + SIZE);实际参数提供了start和end的值。
       简而言之,形式参数是被调函数中的变量,实际参数是主调函数赋给被调函数的具体值,有一个赋值的过程而不是单纯的替换。

指针表示法和数组表示法

       对于C语言,ar[i]和*(ar + i)这两个表达式都是等价的。无论ar是数组名还是指针变量,这两个表达式都没问题。但是,只有当ar是指针变量时,才能使用ar++这样的表达式。

指针和多维数组

假设有下面的声明:

int zippo[4][2]; //内含int数组的数组  

数组名zippo是该数组首元素的地址。zippo的首元素是一个内含两个int值的数组,所以zippo是这个内含两个int值的数组的地址。

  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同。而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素(一个整数)的地址(即&zippo[0][0]的值)相同。简而言之,zippo[0]是一个占用一个int大小对象的地址,而zippo是一个占用两个int大小对象的地址。由于这个整数和内含内含两个整数的数组都开始于同一个地址,所以zippo和zippo[0]的值相同。
  • 给指针或地址加1,其值会增加对应类型大小的数值。在这方面,zippo和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象只占用了一个int大小。因此zippo + 1和zippo[0] + 1的值不同。
  • 解引用一个指针(在指针前使用*运算符)或在数组名后使用带下标的[]运算符,得到解引用对象代表的值。因为zippo[0]是该数组的首元素(zippo[0][0])的地址,所以*(zippo[0])表示储存在zippo[0][0]上的值(即一个int类型的值)。与此类似,*zippo代表首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。该值地址是&zippo[0][0],所以*zippo就是&zippo[0][0],对两个表达式应用解引用运算符表明,**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。简而言之zippo是地址的地址,必须解引用两次才能获得原始值。地址的地址或指针的指针就是双重间接的例子。

数组的数组

指向多维数组的指针

       如何声明一个指针变量pz指向一个二维数组(如zippo)?把指针声明为int类型还不够。因为指向int只能与zippo[0]的类型匹配,说明该指针指向一个int类型的值。但是zippo是它首元素的地址,该元素是一个内含两个int类型值的一维数组。因此,pz必须指向一个内含两个int类型值的数组,而不是指向一个int类型值,其声明如下:

int (* pz)[2];  //pz指向一个内含两个int类型值的数组  

以上代码把pz声明为指向一个数组的指针,该数组内含两个int类型值。
如前所述,虽然pz是一个指针,不是数组名,但是也可以使用pz[2][1]这样的写法。

Hello World

发表于 2017-06-28

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

123
Xu Chen

Xu Chen

programming self-learner

29 日志
7 标签
© 2017 Xu Chen
由 Hexo 强力驱动
主题 - NexT.Pisces