📄 c primer plus
字号:
在上面例子中
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 + -