⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 c primer plus

📁 系统地学习、撑握C语言的经典书。 这是我整理、加工过的《C Primer Plus》精简版
💻
📖 第 1 页 / 共 5 页
字号:

在上面例子中
datas == &data[0]		// 相同的地址
datas + 2 == &data[2]		// 相同的地址
*(dates + 2) == dates[2]	// 相同的值


/* day_mon3.c -- uses pointer notation */
#include <stdio.h>
#define MONTHS 12

int main(void)
{
    int days[MONTHS] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int index;

    for (index = 0; index < MONTHS; index++)
        printf("Month %2d has %d days.\n", index +1,
               *(days + index));   // 与 days[index] 相同
  
    return 0;
}


7、函数、数组与指针

// sum_arr1.c -- sums the elements of an array
// use %u or %lu if %zd doesn't work
#include <stdio.h>
#define SIZE 10
// int sum(int *ar, int n);
int sum(int ar[], int n);	

int main(void)
{
	int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
	long answer;

	answer = sum(marbles, SIZE);
	printf("The total number of marbles is %ld.\n", answer);
	printf("The size of marbles is %zd bytes.\n",
		sizeof marbles);

	return 0;
}

//int sum(int *ar, int n)	// ar[] 比 *ar 更好
int sum(int ar[], int n)     
{
	int i;
	int total = 0;

	for( i = 0; i < n; i++)
		total += ar[i];
	printf("The size of ar is %zd bytes.\n", sizeof ar);

	return total;
}


/* sum_arr2.c -- sums the elements of an array */
#include <stdio.h>
#define SIZE 10
int sump(int * start, int * end);
int main(void)
{
    int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
    long answer;

    answer = sump(marbles, marbles + SIZE);	// C保证指针marbles + SIZE是合法的,但marbles[SIZE]越界了
    printf("The total number of marbles is %ld.\n", answer);
  
    return 0;
}

/* use pointer arithmetic   */
int sump(int * start, int * end)	// start数组首元素地址;end数组末元素的下一个单元的地址
{
    int total = 0;

    while (start < end)
    {
        total += *start; /* add value to total              */
        start++;         /* advance pointer to next element */
    }
  
    return total;
}


8、对未初始化的指针存取值

int *pt;
*pt = 5;	// 可怕的错误!!! 因为指针pt未初始化,它的是随机的,不知5会被存储到哪时

int a;
int *pt=&a;
*pt = 5;	// 正确,5被存到a变量中


int urn[3];
int *ptr1, *ptr2;

ptr1 = urn;		// 正确,ptr1 指向 urn[0]的地址
ptr1++;			// 正确,ptr1 指向 urn[1]的地址
ptr2 = ptr1 + 2;	// 正确,ptr2 指向 urn数组之后的存储单元
ptr2 = urn + 1;		// 正确,ptr2 指向 urn[1]

urn++;			// 错误
ptr2 = ptr2 + ptr1;	// 错误
ptr2 = urn * ptr1;	// 错误


9、保护数组内容

通常我们直接传递数值,只有需要在函数中修改值时,才传递指针。
但对于数组,C不支持把整个数组作为函数参数进行传递,只能传递指针。
传址效率更高,如果是传值,那得分配足够内存块,然后复制一份过去。

如果不希望变量或数组的内容被函数修改,可以给形参加const。

例1:const用于形式参数

/* arf.c -- array functions */
#include <stdio.h>
#define SIZE 5
void show_array(const double ar[], int n);
void mult_array(double ar[], int n, double mult);
int main(void)
{
    double dip[SIZE] = {20.0, 17.66, 8.2, 15.3, 22.22};

    printf("The original dip array:\n");
    show_array(dip, SIZE); 
    mult_array(dip, SIZE, 2.5);
    printf("The dip array after calling mult_array():\n");
    show_array(dip, SIZE);
  
    return 0;
}

/* displays array contents */
void show_array(const double ar[], int n)	// ar数组被限定为只读,不能被修改
{
    int i;

    for (i = 0; i < n; i++)
        printf("%8.3f ", ar[i]);
    putchar('\n');
}

/* multiplies each array member by the same multiplier */
void mult_array(double ar[], int n, double mult) // 没加const,ar数组可以修改
{
    int i;

    for (i = 0; i < n; i++)
        ar[i] *= mult;
}


例2:const用于指针与数组

#include <stdio.h>
int main(void)
{
	double arr_var[4] = {88.00, 100.12, 59.45, 183.11};	// 普通数组
	arr_var[2] = 99.99;

	const double arr_con[4] = {0.08, 0.075, 0.0725, 0.07};	// 常量数组
	//arr_con[2] = 99.99;	// 不允许

	const double * pd = arr_var;			// 指向常量的普通指针:指针可变,内容不可变
	const double * pd2 = arr_con;
	pd = &arr_var[2];
	pd = &arr_con[2];
	//*pd = 99.99;		// 不允许
	//pd[2] = 222.22;	// 不允许

	double * const pdc = arr_var;			// 常量指针:指针不可变,内容可变
	//double * const pdc2 = arr_con;//不允许
	//pdc = &arr_var[2];	// 不允许
	//pdc = &arr_con[2];	// 不允许
	*pdc = 99.99;
	pdc[2] = 222.22;

	const double * const pcdc = arr_var;	// 指向常量的常量指针:指针、内容都不可变
	const double * const pcdc2 = arr_con;
	//pcdc = &arr_var[2];	// 不允许
	//pcdc = &arr_con[2];	// 不允许
	//*pcdc = 99.99;	// 不允许
	//pcdc[2] = 222.22;	// 不允

	double *p = arr_var;			// 普通指针:指针、内空都可变
	//double *p2 = arr_con;	// 不允许
	p = &arr_var[2];
	//p = &arr_con[2];	// 不允许
	*p = 99.99;
	p[2] = 222.22;
	
	return 0;
}


10、指针和多维数组

/* zippo1.c --  zippo info */
#include <stdio.h>
int main(void)
{
	int zippo[4][2] = { {11,12}, {21,22}, {31,32}, {44, 42} };

	printf("   zippo = %p,     zippo + 1 = %p  &zippo[1][0] = %p\n",
		zippo, zippo + 1, &zippo[1][0]);
	printf("zippo[0] = %p,  zippo[0] + 1 = %p  &zippo[0][1] = %p\n", 
		zippo[0], zippo[0] + 1, &zippo[0][1]);
	printf("  *zippo = %p,    *zippo + 1 = %p\n\n", 
		*zippo, *zippo + 1);

	printf("zippo[0][0] = %d\n", zippo[0][0]);
	printf("  *zippo[0] = %d\n", *zippo[0]);
	printf("    **zippo = %d\n\n", **zippo);

	printf("      zippo[2][1] = %d\n", zippo[2][1]);
	printf("*(*(zippo+2) + 1) = %d\n\n", *(*(zippo+2) + 1));

	int i,j;
	for (i=0; i<4; i++)
	{
		for (j=0; j<2; j++)
			printf("&zippo[%d][%d] = %p  ", i, j, &zippo[i][j]);
		printf("\n");
	}

	return 0;
}
输出结果:
   zippo = 0012FF44,     zippo + 1 = 0012FF4C  &zippo[1][0] = 0012FF4C
zippo[0] = 0012FF44,  zippo[0] + 1 = 0012FF48  &zippo[0][1] = 0012FF48
  *zippo = 0012FF44,    *zippo + 1 = 0012FF48

zippo[0][0] = 11
  *zippo[0] = 11
    **zippo = 11

      zippo[2][1] = 32
*(*(zippo+2) + 1) = 32

&zippo[0][0] = 0012FF44  &zippo[0][1] = 0012FF48
&zippo[1][0] = 0012FF4C  &zippo[1][1] = 0012FF50
&zippo[2][0] = 0012FF54  &zippo[2][1] = 0012FF58
&zippo[3][0] = 0012FF5C  &zippo[3][1] = 0012FF60


从上分析得出:
zippo[0]、*zippo 都是指向第1行的指针;
**zippo、*zippo[0]、zippo[0][0] 都是第1个元素的值;
zippo + 1 表示指向下一行。

数组与指针关系图

	     zippo	     zippo+1	     zippo+2	     zippo+3
	|    zippo[0]	|    zippo[1]	|    zippo[2]	|    zippo[3]	|
	|~~~~~~~|~~~~~~~|~~~~~~~|~~~~~~~|~~~~~~~|~~~~~~~|~~~~~~~|~~~~~~~|
	|zippo	|zippo	|zippo	|ippo	|zippo	|zippo	|ippo	|zippo	|
	|[0][0]	|[0][1]	|[1][0]	|[1][1]	|[2][0]	|[2][1]	|[3][0]	|[3][1]	|
	|_______|_______|_______|_______|_______|_______|_______|_______|
   地址	|FF44	 FF48	|FF4C	 FF50	|FF54	 FF5C	|FF60	FF64	|



/* zippo2.c --  zippo info via a pointer variable */
#include <stdio.h>
int main(void)
{
	int zippo[4][2] = { {2,4}, {6,8}, {1,3}, {5, 7} };
	int (*pz)[2];	// pz是指针,指向一个包含2个元素的int数组,pz[i]被解释为一个由2个整数构成的元素,pz[i][j]是一个int
	// int * pax[2];// pax是数组,是由两个指向int的指针构成的数组,pax[i]是一个int的地址
	pz = zippo;

	printf("   pz = %p,    pz + 1 = %p\n",
		pz,         pz + 1);
	printf("pz[0] = %p, pz[0] + 1 = %p\n", 
		pz[0],      pz[0] + 1);
	printf("  *pz = %p,   *pz + 1 = %p\n", 
		*pz,        *pz + 1);
	printf("pz[0][0] = %d\n", pz[0][0]);
	printf("  *pz[0] = %d\n", *pz[0]);
	printf("    **pz = %d\n", **pz);
	printf("      pz[2][1] = %d\n", pz[2][1]);
	printf("*(*(pz+2) + 1) = %d\n", *(*(pz+2) + 1));

	return 0;
}

pz是一个指针,也可以像数组名一样使用:
zippo[m][n] == * ( *(zippo+m) + n )
pz[m][n] == * ( *(pz+m) + n )


11、函数和多维数组

int sum2(int ar[][], int rows);		// 错误的声明

int sum2(int ar[3][4], int rows);	// 3被忽略
int sum2(int ar[][4], int rows);	// 标准形式
int sum2(int [][4], int );		// 可省略参数名

typedef int arr4[4];			// aar4是4个int的数组
typedef arr4 arr3x4[3];			// arr3x4 是3个aar4的数组
int sum2(arr3x4 ar, int rows);

一般声明N维数组的指针时,除了最左边的方括号可以留空之外,其他都需要填写数值。

int sum4d(int ar[][12][20][30], int rows);	// ar是四维数组指针
int sum4d(int (*ar)[12][20][30], int rows);	// 与上面的方式等效,其中ar指向一个12x20x30的int数组


12、指针兼容性

int n = 5;
double x;
int * pi = &n;
double * pd = &x;
x = n;			// 隐式的类型转换
pd = pi;		// 编译时错误


int * pt;
int (* pa)[3];		// 指向3个元素的int数组,pa[i]被解释为一个由3个整数构成的元素,pa[i][j]是一个int
int ar1[2][3];
int ar2[3][2];
int **p2;		// 指向指针的指针

pt = &ar1[0][0];	// 都指向int
pt = ar1[0];		// 都指向int
pt = ar1;		// 非法

pa = ar1;		// 都指向int[3]
pa = ar2;		// 非法

p2 = &pt;		// 都指向int *
*p2 = ar2[0];		// 都指向int
p2 = ar2;		// 非法


int *p1;
const int * p2;
const int **pp2;
p1 = p2;		// 非法,把const指针赋给非const指针
p2 = p1;		// 合法,把非const指针赋给const指针
pp2 = &p1;		// 非法,把非const指针赋给const指针


const int **pp2;
int *p1;
const int n = 13;
pp2 = &p1;		// 不允许,但我们假设允许
*pp2 = &n;		// 合法,二者都是const,但这同时会使p1指向n
*p1 = 10;		// 合法,改变const n的值


13、变长数组(VLA)

Fortran语言允许在函数调用中指定二维的大小,Fortran是很古老的编程语言
但多年来,数值计算库丰富,C正在逐步代替Fortran,如果能转找到C是很有意义的。
因此,C99标准引入了变长数组,它允许使用变量定义数组各维大小。

int  quarters = 4;
int regions = 5;
int sales[regions][quarters];	// 一个变长数组(VLA)

变长数组必须是自动存储类的,必须在函数内部或作为参数声明,而且声明时不可以初始化。
变长数组允许动态分配存储单元,即在程序运行时指定数组大小;
常规的C数组是静态存储分配的,也就是说数组大小在编译时已经确定。

int sum2d(int rows, int cols, int ar[rows][cols]);	// ar是一个变长数组(VLA)
int sum2d(int ar[rows][cols], int rows, int cols);	// 错误
int sum2d(int, int, int ar[*][*]);			// 可省去参数名

int sum2d(int rows, int cols, int ar[rows][cols])
{
	int r;
	int c;
	int tot = 0;

	for (r=0; c<rows; r++)
		for (c=0; c<cols; c++)
			tot += ar[r][c];
	return tot;
}


14、复合文字

C99标准新增复合文字,不过许多编译器尚未支持。

普通数组声明方法:
int diva[2] = {10, 20};

下面是一个复合文字,创建一个包含两个int值的无名称数组复合文字:
(int [2]) {10, 20}

使用复合文字方法:
int * pt1;
pt1 = (int [2]){10, 20};


例:

// flc.c -- funny-looking constants
#include <stdio.h>
#define COLS 4
int sum2d(int ar[][COLS], int rows); 
int sum(int ar[], int n);

int main(void)
{
	int total1, total2, total3;
	int * pt1;
	int (*pt2)[COLS];

	pt1 = (int [2]) {10, 20};	// 复合文字
	pt2 = (int [2][COLS]) { {1,2,3,-9}, {4,5,6,-8} };

	total1 = sum(pt1, 2);
	total2 = sum2d(pt2, 2);
	total3 = sum((int []){4,4,4,5,5,5}, 6);	// 参数为复合文字
	printf("total1 = %d\n", total1); 
	printf("total2 = %d\n", total2); 
	printf("total3 = %d\n", total3); 

	return 0;
}

int sum(int ar[], int n)
{
	int i;
	int total = 0;

	for( i = 0; i < n; i++)
		total += ar[i];

	return total;
}

int sum2d(int ar[][COLS], int rows)
{
	int r;
	int c;
	int tot = 0;

	for (r = 0; r < rows; r++)
		for (c = 0; c < COLS; c++)
			tot += ar[r][c];

	return tot;
}




第十一章 字符串和字符串函数

stdio.h
	gets()		输入字符串
	puts()		输出字符串

string.h (ANSI C 之前的可能是 strings.h)
	strcat()	连接字符串
	strncat()	连接字符串,限制长度
	strcmp()	比较字符串
	strncmp()	比较字符串,限制长度
	strcpy()	复制字符串
	strncpy()	复制字符串,限制长度
	sprintf()	格式化字符串
	strchr()	从左向右查找字符
	strrchr()	从右向左查找字符
	strstr()	从左向右查找字符串


1、字符串表示和字符串I/O

// strings.c -- stringing the user along
#include <stdio.h>
#define MSG "You must have many talents. Tell me some."
                          // a symbolic string constant
#define LIM  5
#define LINELEN 81        // maximum string length + 1
int main(void)
{
    char name[LINELEN];
    char talents[LINELEN];
  

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -