指针与数组
指针是一种保存变量地址的变量。 指针常常是表达某个计算机的唯一途径, 使用指针通常可以生成更高效、更紧凑的代码。
ANSI C 使用类型 void* (指向void的指针)代替char* 作为通用指针的类型。
指针与地址
通常,机器的一个字节可以存放一个char类型的数据,两个相邻的字节存储单元可存储一个short(短整型)类型的数据,而4个相邻的字节存储单元可存储一个long(长整型)类型的数据。 指针是能够存放一个地址的一组存储单元(通常2或4个字节)。
指针与函数参数
C语言是以传值得方式将参数传递给被调用函数的,因此,被调用函数不能直接修改主调函数中变量的值。
指针函数使得被调用函数能够访问和修改主调函数中对象的值。
指针与数组
int a[10];
int *pa;
pa = &a[0];
pa = a;
a[i]
*(a+i)
&a[i]
a+i
指针是一个变量,因此,在C语言中,语句 pa = a和pa++都是合法的。 但数组名不是变量,因此,类似于a=pa和a++形式的语句是非法的。
函数定义中,形式参数
char s[]
等价于
char*s
地址算术运算
<stddef.h>定义了ptrdiff_t,保证足够大的有符号数,可存储两个指针的差值
指针+/- 整数 :意思是指针执行当前位置向前/向后移动指定数量同类型对象后的地址
指针1 - 指针2:意思是指针1和指针2之间相差的同类型对象的个数
指针比较,仅限定为指向同一个数组内各个指针间的比较
指向不同数组元素的指针间的比较或算术运算没有定义
指针可以可0比较,0可赋给指针,其他整形常量不 行。
程序中常用NULL代替0,定义在<stddef.h>
有效指针运算:
相同类型指针间赋值运算[两者之一为void*,另一方 无指向类型要求]
指针同整数间的加法或减法运算
指向相同数组中元素的两个指针间的减法或比较运算
将指针赋值为0或指针与0间的比较
字符指针与函数
char amessage[] = "now is the time";
char *pmessage = "now is the time";
amessage是一个仅仅足以存放初始化字符以及空字符’\0’ 的一维数组。 数组中的单个字符可以进行修改,但amessage始终指向同一个存储位置。 另一方面,pmessage是一个指针,其初值指向一个字符串常量,之后它可以被修改以指向其他地址,但如果试图修改字符串的内容,结果是没有意义的。
指针数组以及指向指针的指针
由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。
如果待排序的文本行首尾相连地存储在一个长字符数组中,那么每个文本行可通过指向它的第一个字符的指针来访问。 这些指针本身可以存储在一个数组中。这样,将指向两个文本行的指针传递给函数strcmp就可以实现对这两个文本行的比较。 当交换次序颠倒的两个文本行时,实际上交换的是指针数组中与这两个文本行向对应的指针,而不是这两个文本行本身。
排序过程的3个步骤:
读取所有输入行
对文本进行排序
按次序打印文本行
输入函数必须收集和保存每个文本行中的字符,并建立一个指向这些文本的指针的数组。 它同事还必须统计输入的行首,因为在排序和打印时要用到这一信息。 由于输入函数只能处理有限数目的输入行,所以在输入行数过多二超过限定的最大行数时,该函数返回某个用于表示非法行首的数字,例如 -1.
输出函数只需要按照指针数组中的次序依次打印这些文本行即可。
多维数组
二维数组:
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}
};
二维数组作为参数传递
f(int daytab[2][13]) { ... }
f(int daytab[][13]) { ... }
f(int (*daytab)[13]) { ... }
一般来说,除数字的第一维(下标)可以不指定大小外,其余各维都必须明确指定大小。
指针数组的初始化
char *month_name(int n)
{
static char *name[] =
{
"Illegal month",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
return (n < 1 || n > 12) ? name[0]:name[n];
}
name数组的初始化通过一个字符串列表实现,列表中的每个字符串赋值给数组相应位置的元素。
第i个字符串的所有字符存储在存储器中的某个位置
指向它的指针存储在name[i]
未指明数组name长度下,编译器编译时,对初值个数进行统计,将准确数字填入数组长度
指针与多维数组
指针数组的声明和图形化描述: 二维数组的声明和图形化描述:
命令行参数
ANSI要求argv[argc]值需为NULL
支持参数选项的命令,选项命令一般允许以任意次序或组合形式出现
#include <stdio.h>
#include <string.h>
#define MAXLINE 1000
int getline(char *line, int max);
main(int argc, char *argv[])
{
char line[MAXLINE];
long lineno = 0;
int c, except = 0, number = 0, found = 0;
while (--argc > 0 && (*++argv)[0] == '-')
{
while (c = *++argv[0])
{
switch (c)
{
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("find: illegal option %c\n", c);
argc = 0;
found = -1;
break;
}
}
}
if (argc != 1)
{
printf("Usage: find -x -n pattern\n");
}
else
while (getline(line, MAXLINE) > 0)
{
lineno++;
if ((strstr(line, *argv) != NULL) != except)
{
if (number)
printf("%ld:", lineno);
printf("%s", line);
found++;
}
}
return found;
}
指向函数的指针
函数本身不是变量,
但可能定义指针指向函数,指针是变量。
#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(void *lineptr[], int left, int right,
int (*comp)(void *, void *));
int numcmp(char *, char *);
main(int argc, char *argv[])
{
int nlines;
int numeric = 0;
if (argc > 1 && strcmp(argv[1], "-n") == 0)
numeric = 1;
if ((nlines = readlines(lineptr, MAXLINES)) >= 0)
{
qsort(
(void**) lineptr,
0,
nlines-1,
(int (*)(void*,void*))(numeric ? numcmp : strcmp));
writelines(lineptr, nlines);
return 0;
}
else
{
printf("input too big to sort\n");
return 1;
}
}
void qsort(
void *v[],
int left,
int right,
int (*comp)(void *, void *))
{
int i, last;
void swap(void *v[], int, int);
if (left >= right)
return;
swap(v, left, (left + right)/2);
last = left;
for (i = left+1; i <= right; i++)
{
if ((*comp)(v[i], v[left]) < 0)
{
swap(v, ++last, i);
}
}
swap(v, left, last);
qsort(v, left, last-1, comp);
qsort(v, last+1, right, comp);
}
#include <stdlib.h>
int numcmp(char *s1, char* s2)
{
...
}
int (*comp)(void *, void *)
它表明comp是一个指向函数的指针,该函数具有两个void*类型的参数,其返回值类型为int。
if ((*comp)(v[i], v[left]) < 0)
comp的使用和其声明是一致的,comp是一个指向函数的指针,
*comp代表一个函数。对该函数调用:
(*comp)(v[i], v[left])
其中圆括号是必需的,这样才能保证其中的各个部分正确结合。
int *comp(void *, void *) /* 错误的写法 */
则表明comp是一个函数,该函数返回一个指向int类型的指针。
复杂声明
创建复杂声明较好的方法是,使用typedef通过简单的步骤合成
char **argv
argv: pointer to char
int (*daytab)[13]
daytab: pointer to array[13] of int
int *daytab[13]
daytab: array[13] of pointer to int
void *comp()
comp: function returning pointer to void
void (*comp)()
comp: pointer to function returning void
char (*(*x())[])()
x: function returning pointer to array[] of
pointer to function returning char
char (*(*x[3])())[5]
x: array[3] of pointer to function returning
pointer to array[5] of char
dcl: optional *'s direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[optional size]
char (*(*x())[])()
声明符dcl是前面可能带多个*的direct-dcl
direct-dcl可是name,由一对圆括号括起来的dcl,后跟一对圆括号的direct-dcl,后跟用方括号括起来的表示可选长度的direct-dcl.
(*pfa[])()
pfa是一个name,是一个direct-dcl
pfa[]也是一个direct-dcl
*pfa[]被识别为一个dcl
故,(*pfa[])是一个direct-dcl
(*pfa[])()被识别为一个direct-dcl,也是一个dcl
学习参考资料:
《C程序设计语言》第2版 新版
https://blog.csdn.net/x13262608581/article/details/108592819
|