有书共读22《C primer Plus》
1)数组:可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。
2)声明数组:声明一个数组,需要指定元素的类型和元素的数量,如下所示:
type arrayName [ arraySize ];
这叫做一维数组。arraySize必须是一个大于零的整数常量,type可以是任意有效的 C 数据类型。例如,要声明一个类型为 double 的包含 10 个元素的数组balance,声明语句如下:
double balance[10];
现在balance是一个可用的数组,可以容纳10个类型为 double的数字。
3)初始化数组:可以逐个初始化数组,也可以使用一个初始化语句,如下所示:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
如果您省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果:
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
您将创建一个数组,它与前一个实例中所创建的数组是完全相同的。下面是一个为数组中某个元素赋值的实例:
balance[4] = 50.0;
上述的语句把数组中第五个元素的值赋为 50.0。所有的数组都是以 0 作为它们第一个元素的索引,也被称为基索引,数组的最后一个索引是数组的总大小减去 1。以下是上面所讨论的数组的图形表示:
4)访问数组元素:
数组元素可以通过数组名称加索引进行访问。元素的索引是放在方括号内,跟在数组名称的后边。例如:
double salary = balance[9];
上面的语句将把数组中第 10个元素的值赋给 salary 变量。下面的实例使用了声明数组、数组赋值、访问数组的概念:
#include <stdio.h>
int main ()
{
int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */
int i,j;
/* 初始化数组元素 */
for ( i = 0; i < 10; i++ ) {
n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */
}
/* 输出数组中每个元素的值 */
for (j = 0; j < 10; j++ ) {
printf("Element[%d] = %d\n",
j, n[j] );
}
return 0;
} 程序运行结果如下:
5)***数组:***数组声明的一般形式如下:
type name[size1][size2]...[sizeN];
例如,下面的声明创建了一个三维 5 . 10 . 4 整型数组:
int threedim[5][10][4];
6)二维数组
***数组最简单的形式是二维数组。一个二维数组,在本质上,是一个一维数组的列表。声明一个 x 行 y 列的二维整型数组,形式如下:
type arrayName [ x ][ y ];
其中,type可以是任意有效的 C 数据类型,arrayName是一个有效的 C 标识符。一个二维数组可以被认为是一个带有 x 行和 y 列的表格。因此,数组中的每个元素是使用形式为 a[ i , j ] 的元素名称来标识的,其中 a 是数组名称,i 和 j 是唯一标识 a 中每个元素的下标。
7)初始化二维数组:
***数组可以通过在括号内为每行指定值来进行初始化。下面是一个带有 3 行 4 列的数组。
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
内部嵌套的括号是可选的,下面的初始化与上面是等同的:
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
8)访问二维数组元素:
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。例如:
int val = a[2][3];
下面是使用嵌套循环来处理二维数组:
#include <stdio.h>
int main ()
{
/* 一个带有 5 行 2 列的数组 */
int a[5][2] = { {0,0}, {1,2}, {2,4},
{3,6},{4,8}};
int i, j;
/* 输出数组中每个元素的值 */
for ( i = 0; i < 5; i++ )
{
for ( j = 0; j < 2; j++ )
{
printf("a[%d][%d] = %d\n",
i,j, a[i][j] );
}
}
return 0;
} 运行结果如下:
9)指针:
每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。如动态内存分配,没有指针是无法执行的。
指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址,就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var-name;
type是指针的基类型,它必须是一个有效的 C 数据类型,var-name是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch /* 一个字符型的指针 */
所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。
10)如何使用指针
使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符*来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:
#include <stdio.h>
int
main ()
{
int
var = 20; /* 实际变量的声明 */
int
*ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("Address of var variable: %x\n",
&var );
/* 在指针变量中存储的地址 */
printf("Address stored in ip variable: %x\n",
ip );
/* 使用指针访问值 */
printf("Value of *ip variable: %d\n", *ip );
return 0;
} 运行结果如下:
11)指向整数的指针数组的声明:int *ptr[MAX];
12)指向指针的指针:是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如 int **var;
是一个指向 int 类型指针的指针。
13)指针的算术运算
指针是一个用数值表示的地址。因此,可以对指针进行四种算术运算:++、--、+、-。
假设ptr是一个指向地址 1000 的整型指针,是一个 32 位的整数,我们对该指针执行下列的算术运算:ptr++,在执行完运算之后,ptr将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果ptr指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。
14)递增一个指针
在程序中可以使用指针代替数组,因为变量指针可以递增,而数组不能递增,因为数组是一个常量指针。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:
#include
<stdio.h>
const
int MAX = 3;
int
main ()
{
int
var[] = {10, 100, 200};
int
i, *ptr;
/* 指针中的数组地址 */
ptr = var;
for ( i = 0; i < MAX; i++)
{
printf("Address of var[%d] =
%x\n", i, ptr );
printf("Value of var[%d] = %d\n",
i, *ptr );
/* 移动到下一个位置 */
ptr++;
}
return 0;
} 运行结果如下:
15)递减一个指针
#include
<stdio.h>
const
int MAX = 3;
int
main ()
{
int
var[] = {10, 100, 200};
int
i, *ptr;
/* 指针中最后一个元素的地址 */ ptr = &var[MAX-1];
for ( i = MAX; i > 0; i--)
{
printf("Address of var[%d] =
%x\n", i, ptr );
printf("Value of var[%d] =
%d\n", i, *ptr );
/* 移动到下一个位置 */
ptr--;
}
return 0;
} 运行结果如下:
16)指针的比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。
17)对形式参数使用const:如果编写的函数需要修改数组,在声明数组参数时则不使用const,如果写的函数不用修改数组,那么在声明数组形参时最好使用const。
课后习题(编程练习):
#include<stdio.h>
#define SIZE 4
void printf_ar(int ar[],int n);
void add_ar(int ar1[], int ar2[], int ar3[], int n);
int main(void)
{
int ar1[SIZE]={2,4,5,8};
int ar2[SIZE]={1,0,4,6};
int ar3[SIZE];
printf("the first array:");
printf_ar(ar1,SIZE);
printf("the second array:");
printf_ar(ar2,SIZE);
add_ar(ar1,ar2, ar3, SIZE);
printf("the third array:");
printf_ar(ar3,SIZE);
return 0;
}
void printf_ar(int ar[],int n)
{
int i;
for (i = 0; i < n; i++)
printf("%d", ar[i]);
printf("\n");
}
void add_ar(int ar1[], int ar2[], int ar3[], int n)
{
int i;
for (i = 0; i < n; i++)
ar3[i]=ar1[i]+ar2[i];
}
#include<stdio.h>
#define ROWS 3
#define COLS 5
void change_ar(int ar[][COLS], int n);
void print_ar(int ar[][COLS], int n);
int main(void)
{
int array[ROWS][COLS] =
{
{ 1, 3, 3, 6, 5 },
{ 3, 6, 3, 2, 8 },
{ 5, 6, 5, 2, 3 }
};
printf("original array:\n");
print_ar(array, ROWS);
change_ar(array, ROWS);
printf("present array:\n");
print_ar(array, ROWS);
return 0;
}
void change_ar(int ar[][COLS], int n)
{
for(int i=0;i<ROWS;i++)
for(int j=0;j<COLS;j++)
ar[i][j] = ar[i][j] * 2;
}
void print_ar(int ar[][COLS], int n)
{
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
printf("%3d ", ar[i][j]);
printf("\n");
}
} 运行结果: 
