目录
第5章 指针与数组
指针:保存变量地址的变量
5.1 指针与地址
- 一个字节 - 存char
- 两个字节 - 存 short
- 四个字节 - 存 long,int
地址运算符 &:去一个内存对象的地址。
间接寻址(引用)运算符 *:作用于指针时,将访问指针所指向的对象。
// 声明:
double *p, atof(char *);
// *p 指向 double 类型的指针
// 指向 char 类型的指针
-
每个指针都必须指向某种特定的数据类型
-
指针改变对象的值
-
指针直接使用
int *p, x=1; p = &x; // 1.指针p 指向 x *p = 0; // 2.指针p 改变对象 x 的值 int *q; q = p; // 3.指针q也指向p 所指向的对象
5.2 指针与函数参数【将输入的下一个整型数赋值给 *pn】
指针参数使被调函数能够访问和修改主调函数中对象的值。(址传递)
swap(&a, &b)
void swap(int *px, int *py)
{
int temp;
temp = *px;
*px = *py;
*py = temp;
}
/* getint(int *) 将输入的下一个整型数赋值给 *pn */
#include <stdio.h>
#include <ctype.h>
int getch(void); //读下一个字符
void ungetch(int); //字符放回输入中
// 将输入的下一个整型数赋值给 *pn
int getint(int *pn)
{
int c, sign;
while(isspace(c = getch()))
;
if(!isdigit(c) && c != EOF && c != '+' && c != '-')
{
ungetch(c); // 输入不是数字时,返回0
return 0;
}
sign = (c == '-')?-1 : 1; // sign 存正负号
if(c == '+' || c == '-') //若有正负号,继续读下一个
c = getch();
for(*pn = 0; isdigit(c); c = getch()) // 是数字,则给到 *pn
*pn = 10 * *pn + (c - '0');
*pn *= sign; // 最后加上正负
if(c != EOF)
ungetch(); // 若最后读入不是 EOF,则写回
return c;
}
5.3 指针与数组
指针比数组下标编写的程序执行速度快。
数组名代表数组最开始的一个元素地址。不是变量,不可以进行数值运算。a=pa 和 a++ 非法
int *pa, a[10];
pa = a; //指针pa指向 a[0]
// 由于数组名代表数组第一个元素的地址,等价 pa=&a[0];
a[i] -> *(a+i) -> *(pa+i) -> pa[i] //等价
函数定义时,func(char s[])
和 func(char *s)
等价。
5.4 地址算数运算【字符串长度】
有效的指针运算包括:
-
相同类型指针之间赋值运算;
-
指针同整数之间的加减运算;
p+n
表示p指向的对象之后第n个对象的地址,如果int类型占4个字节,则n按4的倍数计算。 -
指向相同数组元素的指针间减法或比较运算;
-
将指针赋值为0或指针与0之间比较运算。
其他形式的指针运算都非法,例如
- 指针间加法、乘法、除法、移位或屏蔽运算;
- 指针同 float 或 double 类型之间的加法运算;
- 不经强制类型转换,不同类型指针之间赋值运算。(void * 类型除外)
/* 返回字符串长度:strlen */
int strlen(char *s)
{
char *p = s;
while(p != '\0')
p++;
return p-s;
}
5.5 字符指针与函数【字符串复制比较】
char amsg[] = "now is the time"; //定义一维数组
- amsg 是仅足以存放字符串以及’\0’的一维数组;
- 单个字符可修改,如:amsg[2]=‘b’;
- amsg 始终指向同一存储位置。
char pmsg[] ="now is the time"; //定义指向字符串常量的指针
- 不能改字符串内容;
- 可修改指向其他地址。
/* 字符串复制 */
// 版本1:数组下标
void strcpy(char *s, char *t)
{
int i;
i = 0;
while((s[i] = t[i]) != '\0')
i++;
}
// 版本2:指针实现
void strcpy(char *s, char *t)
{
while((*s++ = *t++) != '\0')
;
}
// 版本3:优化
void strcpy(char *s, char *t)
{
while(*s++ = *t++) // 表达式同'\0'的比较是多余的,只需判断表达式值是否为0即可
;
}
/* 字符串比较 */
// 版本1:数组下标
int strcmp(char *s, char *t)
{
int i;
for(i=0; s[i] == t[i]; ++i)
if(s[i] == '\0') //默认s,t 长度相等
return 0;
return s[i] - t[i];
}
// 版本2:指针方式
int strcmp(char *s, char *t)
{
for(; *s == *t; s++, t++)
if(*s == '\0')
return 0;
return *s - *t;
}
*p++ = val; //入栈
val = *–p; //出栈
5.6 指针数组以及指向指针的指针【文本排序、快速排序】
- 读取所有输入行;
- 对文本行进行排序;
- 按次序打印文本行。
指针数组的声明:char *lineptr[MAXLINES];
/* 对输入的文本进行排序:readlines, writelines, qsort, getline, *alloc, afree */
#include <stdio.h>
#include <string.h>
#define MAXLINES 5000 //最大文本行
char *lineptr[MAXLINES]; //指向文本行的指针数组
int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
void qsort(char *lineptr[], int left, int right);
int main()
{
int nlines; //读取输入行数目
if((nlines = readlines(lineptr, MAXLINES)) >= 0) // 读取输入行数,如果大于0
{
qsort(lineptr, 1, nlines-1); //排序
writelines(lineptr, nlines); //输出
return 0;
}
else
{
printf("error: input too big to sort\n");
return 1;
}
return 0;
}
#define MAXLEN 1000
int getLine(char *, int);
char *alloc(int);
/* 读取输入行 */
int readlines(char *lineptr[], int maxlines)
{
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while((len = getLine(line, MAXLEN)) > 0)
{
if(nlines >= maxlines || (p = alloc(len)) == NULL)
return -1;
else
{
line[len-1] = '\0';
strcpy(p, line);
lineptr[nlines++] = p;
}
}
return nlines;
}
/* 将行保存到 s 中,并返回该行的长度*/
int getLine(char s[], int lim)
{
int c, i;
i = 0;
while(--lim > 0 && (c=getchar()) != EOF && c != '\n')
s[i++] = c;
if(c == '\n')
s[i++] = '\n';
s[i] = '\0';
return i;
}
#define ALLOCSIZE 1000
static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;
char *alloc(int n)
{
if(allocbuf + ALLOCSIZE - allocp >= n)
{
allocp += n;
return allocp - n;
}
else
return 0;
}
void afree(char *p)
{
if(p >= allocbuf && p < allocbuf + ALLOCSIZE)
allocp = p;
}
/* 写输出行 */
void writelines(char *lineptr[], int nlines)
{
int i;
for(i=0; i<nlines; i++)
printf("%s\n", lineptr[i]);
}
// 版本2:文本行排序,数组元素是字符串,v[left]... v[right]
void qsort(char *v[], int left, int right)
{
int i, last;
void swap(char *v[], int i, int j);
if(left >= right) // 左右下标碰到,排序结束
return;
swap(v, left, (left+right)/2); // 把 mid 拿到最前面,作为比较的参照物
last = left; // last 移动到v[0]
for(i = left+1; i<=right; ++i) // 遍历从 left+1 到 right
if(strcmp(v[i], v[left]) < 0)
// 小于v[left]的元素交换到第二个元素位置,并往后推移
swap(v, ++last, i);
swap(v, left, last); // 交换left, last,如此v[last]固定位置
qsort(v, left, last-1);
qsort(v, last+1, right);
}
void swap(char *v[], int i, int j)
{
char *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
快速排序:qsort (版本2)文本行
// 版本2:文本行排序,数组元素是字符串,v[left]... v[right]
void qsort(char *v[], int left, int right)
{
int i, last;
void swap(int *v[], int i, int j);
if(left >= right) // 左右下标碰到,排序结束
return;
swap(v, left, (left+right)/2); // 把 mid 拿到最前面,作为比较的参照物
last = left; // last 移动到v[0]
for(i = left+1; i<=right; ++i) // 遍历从 left+1 到 right
if(strcmp(v[i], v[left]) < 0)
// 小于v[left]的元素交换到第二个元素位置,并往后推移
swap(v, ++last, i);
swap(v, left, last); // 交换left, last,如此v[last]固定位置
qsort(v, left, last-1);
qsort(v, last+1, right);
}
void swap(int *v[], int i, int j)
{
char *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
5.7 多维数组【日期转第几天】
日期转某年中第几天、某年第几天转某月某日:day_of_year, month_day
#include <stdio.h>
static char daytab[2][13] =
{
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
};
/* 日期转某年中第几天 */
int day_of_year(int year, int month, int day)
{
int i, leap;
leap = (year%4==0 && year%100!=0) || year%400==0; //判断闰年
for(i=1; i<month; i++)
day += daytab[leap][i];
return day;
}
/* 某年第几天转某月某日 */
void month_day(int year, int yearday, int *pmonth, int *pday)
{
int i, leap;
leap = (year%4==0 && year%100!=0) || year%400==0;
for(i=1; yearday>daytab[leap][i]; i++)
yearday -= daytab[leap][i];
*pmonth = i; // 传指针是为了直接改变
*pday = yearday;
}
int main()
{
int year, month, day;
printf("请输入年月日: ");
scanf("%d %d %d", &year, &month, &day);
printf("这天是该年的第%d天\n", day_of_year(year, month, day));
return 0;
}
5.8 指针数组的初始化
static char *name[] =
{
"Illegal month",
"January","February","March","April","May","June","July","August","September","October","November","December"
};
5.9 指针与多维数组
int a[10][20]; //二维数组,分配了200个 int 类型长度的存储空间
int *b[10]; //指针数组,每行长度可以不同
5.10 命令行参数
- argc:参数计数,表示命令行参数的个数。
- *argv[]:参数向量,指向字符串数组的指针。
argc 至少为1,argv[0] 值为启动该程序的程序名。
argv[argc] 的值必须为一个指针。
#include <stdio.h>
/* 回显程序命令行参数 版本1 */
int main(int argc, char *argv[])
{
int i;
for(i=1; i<argc; i++)
{
printf("%s%s", argv[i], (i<argc-1)? " " : "");
}
printf("\n");
return 0;
}
#include <stdio.h>
/* 回显程序命令行参数 版本2 */
int main(int argc, char *argv[])
{
while(--argc > 0)
printf("%s%s", *++argv, (argc > 1)? " " : "");
printf("\n");
return 0;
}
参数命令行程序的运行方案:
- 找到可执行文件 .exe 的位置,复制路径(建议放在桌面)
- 打开cmd,输入: cd C:\Users\Administrator\Desktop\000temp_test\bin\Debug
- 000temp_test.exe hello, world
- 输出: hello, world
打印与第一个参数指定的模式匹配的行
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getLine(char *line, int max);
int main(int argc, char *argv[])
{
char line[MAXLINE];
int found = 0;
if(argc != 2)
printf("Usage: find pattern\n");
else
while(getLine(line, MAXLINE) > 0)
{
if(strstr(line, argv[1] != NULL))
{
printf("%s", line);
found++;
}
}
return found;
}
/* 将行保存到 s 中,并返回该行的长度*/
int getLine(char *s, int lim)
{
int c, i;
i = 0;
while(--lim > 0 && (c=getchar()) != EOF && c != '\n')
s[i++] = c;
if(c == '\n')
s[i++] = '\n';
s[i] = '\0';
return i;
}
标准库函数 strstr(s, t)
返回一个指针,指向字符串 t 在字符串 s 中第一次出现的位置。声明在头文件<string.h>中
5.11 指向函数的指针
int (*cmp)(void *, void *)
comp 是一个指向函数的指针。
具有两个 void* 类型的参数,返回值类型 int 。
5.12 复杂声明
int *f() // f 是一个函数,返回一个指向 int 型指针
int (*pf)() // pf是指向函数的指针,返回 int 对象
char **argv // 指向字符指针的指针
int (*daytab)[13] // 指向整型数组的指针(数组指针)
int *daytab[13] // daytab是一维数组,元素是指向 int 的指针(指针数组)
void *comp() // comp是函数,返回空指针
void (*comp)() // comp是指向函数的指针,返回空。
char (*(*x())[])()
/*
(从内向外)
1.x是一个函数;
2.返回指针;
3.指针指向数组;
4.数组元素是指针;
5.指向函数;
6.返回 char。
*/
char (*(*x[3])())[5]
/*
(从内向外)
1.x是拥有3个元素的数组;
2.数组元素是指针;
3.指向函数;
4.返回指针;
5.指针指向5个元素的数组;
6.数组元素是 char
2-dim 数组元素,五种引用方式:a[3][4]
a[i][j]
*(a[i]+j)
*(*(a+i)+j)
(*(a+i))[j]
*(&a[0][0]+4*i+j)