例11-1. 单词索引
一个单词表存放了5个表示颜色的英文单词,输入一个字母,在单词表中查找并输出所有以此字母开头的单词,若没有找到,输出Not Found
#include<stdio.h>
int main()
{
int i, flag=0;
char ch;
const char* color[5]={"red", "blue", "yellow", "green", "black"}; /* 指针数组 */
printf("INput a letter: ");
ch=getchar();
for(i=0; i<5; i++)
{
if(*color[i]==ch){ /* 获得当前的字符 */
flag=1;
puts(color[i]); /* 输出数组的第i项,即单词 */
}
}
if(flag==0)
printf("Not Found\n");
return 0;
}
C语言中的数组可以是任何类型,如果数组的各个元素都是指针类型,用于存放内存地址,这个数组就是
一维指针数组的定义一般为
int a[10]; /* a是一个数组,有10个元素,每个元素的类型都是整型 */
char* color[5]; /* color是一个数组,有5个元素,为每个元素的类型都是字符指针 */
const char* color[5]={"red", "blue", "yellow", "green", "black"};
color是一个数组,有5个元素
每个元素的类型都是指针
数组元素可以处理字符串(
const的作用是限定变量的值不能改变,即常量
继续执行以下语句,功能是什么?
tmp=color[0];
color[0]=color[4];
color[4]=tmp;
指向指针的指针(二级指针)的一般定义为:
例11-2. 二级指针操作
对下列变量定义和初始化后,依次执行操作①-③后,各变量的值分别为什么?
int a=10, b=20; t;
int *pa=&a, *pb=&b, *pt;
int **ppa=&pa, **ppb=&pb, **ppt;
| 操作 | **ppa | **ppb | *pa | *pb | a | b |
|---|---|---|---|---|---|---|
| 0 | 10 | 20 | 10 | 20 | 10 | 20 |
| ① | 20 | 10 | 10 | 20 | 10 | 20 |
| ② | 10 | 20 | 20 | 10 | 10 | 20 |
| ③ | 20 | 10 | 10 | 20 | 20 | 10 |
int a=10, b=20; t;
int *pa=&a, *pb=&b, *pt;
int **ppa=&pa, **ppb=&pb, **ppt;
int a[3][4];
可以看成是由a[0],a[1],a[2]组成的一维数组
a[0],a[1],a[2]各自又是一个一维数组
二维数组是数组元素为一维数组的一维数组
例11-3. 使用二维指针改写例11-1.
#include<stdio.h>
int main()
{
int i, flag=0;
char ch;
const char* color[5]={"red", "blue", "yellow", "green", "black"};
const char** pc; /* 定义二级指针 */
pc=color; /* 二级指针赋值 */
printf("Input a letter: ");
ch=getchar();
for(i=0; i<5; i++){
if(**(pc+i)==ch){ /* 使用二级指针操作数组 */
flag=1;
puts(*(pc+i)); /* 输出字符串 */
}
}
if(flag==0)
printf("Not Found\n");
return 0;
}
指针数组与二维数组 -->
char ccolor[][7]={"red", "blue", "yellow", "green", "black"};
char *pcolor[]={"red", "blue", "yellow", "green", "black"};
例11-4. 将5个字符串从小到大排序后输出
#include<stdio.h>
#include<string.h>
void fsort(const char* color[], int n)
{
int k, j;
const char *temp;
for(k=1; k<=n; k++){
for(j=0; j<=n-k; j++){
if(strcmp(color[j], color[j+1])<0){
temp=color[j];
color[j]=color[j+1];
color[j+1]=temp;
}
}
}
}
int main()
{
int i;
const char* pcolor[5]={"red", "blue", "yellow", "green", "black"};
fsort(pcolor, 5);
for(i=0; i<5; i++)
printf("%s ", pcolor[i]);
return 0;
}
指针数组排序
void fsort(const char* color[], int n)
{
int k, j;
const char *temp;
for(k=1; k<=n; k++){
for(j=0; j<n-k; j++){
if(strcmp(color[j], color[j+1])<0){
temp=color[j];
color[j]=color[j+1];
color[j+1]=temp;
}
}
}
}
整数数组排序
void fsort(int a[], int n)
{
int k, j;
int temp;
for(k=1; k<=n; k++){
for(j=0; j<n-k; j++){
if(a[j]>a[j+1]){
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
排序前
排序后
例11-5. 解密英文藏头诗
将一首诗的每一句的第一个字连起来,所组成的内容就是该诗的真正含义
输入的藏头读小于20行,每行不超过80个字符,以#作为输入的结束标志,使用动态内存分配方式处理字符串的输入
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int i, n=0;
char *poem[20], str[80], mean[20]; /* poem用于存放每一行,str用于存放一行输入内容,mean用于存放每一行的第一个字 */
gets(str);
wihle(str[0]!='#'){ /* 一行不以'#'结束,为有效输入 */
poem[n]=(char*)malloc(sizeof(char)*(strlen(str)+1)); /* 动态分配一行字符串大小的空间 */
strcpy(poem[n], str); /* 将输入的字符串赋值给动态内存单元 */
n++;
gets(str);
}
for(i=0; i<n; i++){
mean[i]=*poem[i]; /* 每行取第一个字符 */
free(poem[i]); /* 释放动态分配的内存单元 */
}
mean[i]='\0';
puts(mean);
return 0;
}
例11-6. 随机发牌
一副纸牌有52张,4种花色,每种花色13张。用程序模拟随机发牌过程,将52张牌按轮转的方式发放给4人,并输出发牌结果
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
struct card{
int suit; /* 花色 */
int face; /* 牌点 */
};
void deal(struct card *wdeck)
{
int i, m, t;
static int temp[52]={0}; /* 发牌标记,0表示未发,1表示已发 */
srand(time(NULL)); /* 设置随机数的产生与系统时钟关联 */
for(i=0; i<52; i++){
while(1)
{
m=rand()%52; /* 生成一个随机数,介于0~51之间 */
if(temp[m]==0)
break;
}
temp[m]=1;
/* 4人轮转发牌 */
t=(i%4)*13+(i/4);
wdeck[t].suit=m/13;
wdeck[t].face=m%13;
}
}
}
int main()
{
int i;
struct card deck[52];
const char* suit[]={"Heart", "Diamond", "Club", "Spade"};
const char* face[]={"A","K","Q","J","10","9","8","7","6","5","4","3","2"};
deal(deck);
for(i=0; i<52; i++){
if(i%13==0)
printf("Player %d:\n", i/13+1);
printf("%s of %s\n", face[deck[i].face], suit[deck[i].suit]);
}
return 0;
}
命令行参数的一般形式为
用命令行的程序不能在编译器中执行,需要将源程序经编译、链接成相应的命令文件,回到命令行状态,再在该状态下直接输入命令文件名
第1个参数
第2个参数
如
例11-7. echo
将所有命令行参数在同一行输出
#include<stdio.h>
int main(int argc, char *argv[])
{
int k;
for(k=1; k<argc; k++)
printf("%s ", argv[k]);
printf("\n");
return 0;
}
例11-8. 字符定位
输入一个字符串和一个字符,如果该字符在字符串中,就从该字符首次出现的位置开始输出字符串中的字符。要求定义函数match(char* s, char chp),在字符串s中查找字符ch,如果找到则返回第一次找到的该字符在字符串中的位置(地址),否则,返回空指针NULL。如输入字符串program和字符r,输出rogram
#include<stdio.h>
char* match(char* s, char ch) /* match返回值为一个指针 */
{
while(*s!='\0'){
if(*s==ch)
return s;
s++;
}
return NULL;
}
int main()
{
char ch, str[80], *p=NULL;
printf("Input the string:");
scanf("%s", str);
getchar();
printf("Input a character:");
ch=getchar();
if((p=match(str, ch))!=NULL) /* 指针p接收match的返回值 */
printf("%s\n", p); /* 输出指针所指处的内容,直到'\0' */
else
printf("Not Found\n");
return 0;
}
函数返回值的类型可以有:
在C语言中,函数返回值也可以是指针,定义和调用这类函数的方法与其他函数是一样的
char* match()
{
char ch, str[80], *s=str;
scanf("%s", str);
getchar();
ch=getchar();
while(*s!='\0'){
if(*s==ch)
return s;
s++;
}
return NULL;
}
不能返回在函数内部定义的局部数据对象的地址,所有的局部数据对象在函数返回时都会消亡,其值不再有效
返回指针的函数一般都返回全局数据对象或主调函数中数据对象的地址
每个函数都占用一段内存单元,都有一个
C语言中,函数名代表函数的入口地址,因此可定义一个指针变量,接收函数的入口地址,使其指向函数,这就是指向函数的指针,也称为
通过函数指针可以调用函数,也可以作为函数的参数
类型名指定函数返回值的类型,变量名指向函数的指针变量的名称,参数类型表指的函数的参数类型列表,例如
定义的是一个函数指针funptr,它可以指向有两个整数类型参数,并且返回类型为int的函数
通过函数指针调用函数的一般格式为
例如
C语言的函数调用中,函数名或已赋值的函数指针也能作为实参,此时,形参就是函数指针,它指向实参所代表函数的入口地址,例如
{...}
f(int (*funptr)(int,int))
{...}
void main()
{...
int (*funptr)(int, int);
funptr=fun;
f(funptr);
......
}
例11-9. 函数指针示例
编写一个函数calc(f, a, b),用梯形公式求函数$f(x)$在[a,b]上的数值积分
$$
\int_{b}^{a}f(x)dx=(b-a)/2\times(f(a)+f(b))
$$
然后调用该函数计算下列数值积分(用函数指针作为函数参数示例)
① $\int_{0}^{1}x^2dx$ ②$\int_{1}^{2}\sin{x}/xdx$
#include<stdio.h>
#include<math.h>
double calc(double (*funp)(double), double a, double b)
{
double z;
z=(b-a)/(2*((*funp)(a)+(*funp)(b))); /* 调用funp指向的函数 */
return z;
}
double f1(double x)
{
return x*x;
}
double f2(double x)
{
return sin(x)/x;
}
int main()
{
double result;
double (*funp)(double);
result=calc(f1, 0.0, 1.0); /* 把函数名f1作为函数calc的实参 */
printf("1: result=%.4f\n", result);
funp=f2; /* 对函数指针funp赋值 */
result=calc(funp, 1.0, 2.0); /* 函数指针funp作为函数calc的实参 */
printf("2: result=%.4f\n", result);
return 0;
}
例11-10. 学生成绩信息库
建立一个学生成绩信息的单向链表,包括学号、姓名、成绩等,学生记录按学号由小到大顺序排列,要求实现对成绩信息的插入、修改、删除和遍历操作
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stud_node{
int num;
char name[20];
int score;
struct stud_node *next;
};
/* 新建链表 */
struct stud_node* create_stu_Doc()
{
struct stud_node *head, *p;
int num, score;
char name[20];
int size=sizeof(struct stud_node);
head=NULL;
printf("Input num, name and score:");
scanf("%d%s%d", &num, name, &score);
while(num!=0){
p=(struct stud_node*)malloc(size); /* 动态分配内存 */
p->num=num;
strcpy(p->name, name);
p->score=score;
head=insertDoc(head, p); /* 调用插入函数 */
printf("Input num, name and score:");
scanf("%d%s%d", &num, name, &score);
}
return 0;
}
struct stud_node* insertDoc(struct stud_node *head, struct stud_node *stud)
{
struct stud_node *ptr, *ptr1, *ptr2; /* 插入链表,需要三个指针,分别指向当前,前一个以及后一个 */
ptr2=head;
ptr=stud;
/* 原链表为空时插入 */
if(head==NULL){
head=ptr;
head->next=NULL;
}else{
while((ptr->num>ptr2->num) && (ptr2->next!=NULL)){
ptr1=ptr2; /* ptr1, ptr2分别往后移一个结点 */
ptr2=ptr2->next;
}
if(ptr->num<=ptr2->num){ /* 在ptr1和ptr2之间插入新结点 */
if(head==ptr2)
head=ptr;
else
ptr1->next=ptr;
ptr->next=ptr2;
}
else{
ptr2->next=ptr;
ptr->next=NULL;
}
}
return head;
}
struct stud_node* deleteDoc(struct stud_node* head, int num)
{
struct stud_node *ptr1, *ptr2;
/* 要被删除的结点为表头结点 */
while(head!=NULL && head->num==num){
ptr2=head;
head=head->next;
free(ptr2);
}
if(head==NULL) /* 链表空 */
return NULL;
/* 要被删除的结点为非表头结点 */
ptr1=head;
ptr2=head->next; /* 从表头的下一个结点搜索所有符合条件的结点 */
while(ptr2!=NULL){
if(ptr2->num==num){ /* ptr2指向要删除的结点 */
ptr1->next=ptr2->next;
free(ptr2);
}
else{
ptr1=ptr2;
ptr2=ptr2->next;
}
}
return head;
}
void print_stu_doc(struct stud_node *head)
{
struct stud_node *ptr;
if(head==NULL){
printf("\nNo Records \n");
return;
}
printf("\nThe students' Records are:\n");
printf("Num\t Name\t Score \n");
for(ptr=head; ptr!=NULL; ptr=ptr->next)
printf("%d\t %s\t %d\n", ptr->num, ptr->name, ptr->score);
}
int main()
{
struct stud_node *head, *p;
int choice, name, score;
char name[20];
int size=sizeof(struct stud_node);
do{
printf("1. Create\n2. Insert\n3. Delete\n4. Print\n0. Exit\n");
scanf("%d", &choice);
switch(choice){
case 1:
head=create_stu_Doc();
break;
case 2:
printf("Input num, name and score:\n");
scanf("%d%s%d", &num, name, &score);
p=(struct stud_node*)malloc(size);
p->num=num;
strcpy(p->name, name);
p->score=score;
head=insertDoc(head, p);
break;
case 3:
printf("Input num:\n");
scanf("%d", &num);
head=deleteDoc(head, num);
break;
case 4:
print_stu_doc(head);
break;
default:
break;
}
}while(choice!=0);
return 0;
}
一般使用结构定义单向链表结点的数据类型,采用结构的递归定义
int num;
char name[20];
int score;
}
建立链表
head=tail=NULL;
scanf("%d%s%d", &num, name, &score);
while(num!=NULL){
p=(struct stud_node*)malloc(size);
p->num=num;
strcpy(p->name, name);
p->score=score;
scanf("%d%s%d", &num, name, &score);
}
尾部插入
p->next=NULL;
if(head==NULL)
head=p;
else
tail->next=p;
tail=p;
头部插入
p->next=head;
head=p;
for(ptr=head; ptr!=NULL; ptr=ptr->next)
printf("%d\t%s\t%d\n", ptr->num, ptr->name, ptr->score);
ptr2=ptr1->next;