IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 《C程序设计语言》学习笔记——指针与数组 -> 正文阅读

[C++知识库]《C程序设计语言》学习笔记——指针与数组

指针与数组

指针是一种保存变量地址的变量。
指针常常是表达某个计算机的唯一途径,
使用指针通常可以生成更高效、更紧凑的代码。

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"; /* an array */
char *pmessage = "now is the time"; /* a pointer */

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]) { ... }

一般来说,除数字的第一维(下标)可以不指定大小外,其余各维都必须明确指定大小。

指针数组的初始化

/* month_name 函数:返回第n个月份的名字 */
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);
/* find: print lines that match pattern from 1st arg */
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 %c\n", c);
					argc = 0;
					found = -1;
					break;
			}
		}
	}
		
	// argc表示处理了首个参数,选项参数后剩余的参数个数
	if (argc != 1)
	{
		printf("Usage: find -x -n pattern\n");
	}
	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 <stdio.h>
#include <string.h>
#define MAXLINES 5000 /* max #lines to be sorted */
char *lineptr[MAXLINES]; /* pointers to text lines */
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 *);
/* sort input lines */
main(int argc, char *argv[])
{
	int nlines; /* number of input lines read */
	int numeric = 0; /* 1 if numeric sort */
	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 sort\n");
		return 1;
	}
}

/* qsort函数: 以递增顺序对 v[left]...v[right] 进行排序 */
void qsort(
	void *v[], 
	int left, 
	int right,
	int (*comp)(void *, void *))
{
	int i, last;
	void swap(void *v[], int, int);
	if (left >= right) /* do nothing if array contains */
		return; /* fewer than two elements */
	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 (*)(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
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-26 12:02:17  更:2021-10-26 12:04:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 13:23:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码