- 指针与数组
- 指针与地址
- 指针与函数参数
- 指针与数组
- 地址算术运算
- 字符指针与函数
- 指针数组以及指向指针的指针
- 多维数组
- 指针数组的初始化
- 指针与多维数组
- 命令行参数
- 指向函数的指针
- 复杂声明
指针是一种保存变量地址的变量。
指针常常是表达某个计算机的唯一途径,
使用指针通常可以生成更高效、更紧凑的代码。
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地址算术运算
字符指针与函数定义了ptrdiff_t,保证足够大的有符号数,可存储两个指针的差值 指针+/- 整数 :意思是指针执行当前位置向前/向后移动指定数量同类型对象后的地址 指针1 - 指针2:意思是指针1和指针2之间相差的同类型对象的个数 指针比较,仅限定为指向同一个数组内各个指针间的比较 指向不同数组元素的指针间的比较或算术运算没有定义 指针可以可0比较,0可赋给指针,其他整形常量不 行。 程序中常用NULL代替0,定义在 有效指针运算: 相同类型指针间赋值运算[两者之一为void*,另一方 无指向类型要求] 指针同整数间的加法或减法运算 指向相同数组中元素的两个指针间的减法或比较运算 将指针赋值为0或指针与0间的比较
char amessage[] = "now is the time"; char *pmessage = "now is the time";
amessage是一个仅仅足以存放初始化字符以及空字符’ ’ 的一维数组。
数组中的单个字符可以进行修改,但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指向函数的指针#include #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; // 遍历参数,处理选项 // 以数组形参使用char* argv[]时, // 需要将其转换为char** argv // argv是一个变量,指向数组首元素,数组的每个元素是char*类型 // ++argv,会改变argv,下次再访问时,argv是改变后的值 // 要求选项必须放在 匹配内容参数之前 while (--argc > 0 && (*++argv)[0] == '-') { // argv[i]返回的是数组索引i元素的引用, // ++argv[i] 会通过引用改变argv[i]元素的值,下次再访问argv[i]访问的是改变后的值 while (c = *++argv[0]) { switch (c) { case 'x': except = 1; break; case 'n': number = 1; break; default: printf("find: illegal option %cn", c); argc = 0; found = -1; break; } } } // argc表示处理了首个参数,选项参数后剩余的参数个数 if (argc != 1) { printf("Usage: find -x -n patternn"); } else while (getline(line, MAXLINE) > 0) { lineno++; // 对argv的预期 // 1. argv不是首个参数 // 2. argv指向的不是已经处理的选项参数 // argv此时指向匹配内容参数 if ((strstr(line, *argv) != NULL) != except) { if (number) printf("%ld:", lineno); printf("%s", line); found++; } } return found; }
函数本身不是变量, 但可能定义指针指向函数,指针是变量。
#include#include #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, // numcmp, strcmp 函数地址 // 对函数名或数组名赋值给指针时,不需要在前面加& (int (*)(void*,void*))(numeric ? numcmp : strcmp)); writelines(lineptr, nlines); return 0; } else { printf("input too big to sortn"); 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 // 注意,此函数原型可以与int (*)(void*,void*)指向函数指针类型匹配 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] // x是一个函数 // 函数的参数是空 // 函数的返回值是一个指针,指向一个数组 // 数组的每个元素是一个函数指针,函数返回值是char,参数是空。 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



