8.1 一维数组
8.1.1 数组名
int a;
int b[4];
那么,b的类型是什么?实际上不能说b表示的是整个数组。
数组名的值是一个指针常量,也就是数组第一个元素的地址。它的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的常量指针。”
但数组和指针是不同的。例如:数组具有确定数量的元素,而指针指示一个标量值。编译器用数组名来记住这些属性。只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。且指针常量是不可改变的:
#include <stdio.h>
int main(void)
{
int a[2] = {1,2};
a = (int*)100;
return 0;
}
备注:这段代码是错误的。
因为指针常量所指向的是内存中数组的起始位置,如果修改这个指针常量,唯一可行的操作就是把整个数组移动到内存的其他位置。但是,在程序完成链接之后,内存中数组的位置是固定的,所以当程序运行时,再想移动数组就为时已晚。因此,数组名的值是一个指针常量。
只有在两种场合下,数组名并不用指针常量表示---就是当数组名作为sizeof操作符或单目操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。
sizeof(arr) / sizeof(*arr);//计算数组的长度
取一个数组名的地址所产生的是一个指向数组的指针。考虑下面这个例子:
int a[10];
int b[10];
int *c;
c = &a[0];
表达式&a[0]是一个指向数组第一个元素的指针。所以它等价于:
c = a;
而下面的运算是非法的:
b = a;//a,b为指针常量
a = c;//指针常量不可修改
8.1.2 下标引用
int array[10];
int *ap = array + 2;
ap = array + 2 或 &array[2]
*ap = array[2]
ap[0] = *(ap) = array[2]
ap+6 = array + 8 或 &array[8]
*ap+6 = array[2] + 6
*(ap+6) = array[8]
ap[6] = array[8]
&ap = ap的地址,未知
ap[-1] = array[1]
ap[9] 非法,越界
8.1.3 指针与下标
规则:下标绝不会比指针更有效率,但指针有时会比下标更有效率。
例子:下标初始化
int array[10];
int a;
for ( a = 0; a < 10; a++ ){
array[a] = 0;
}
指针初始化:
int array[10];
int *ap;
for ( ap = array; ap < array + 10; ap++ ){
*ap = 0;
}
两者的区别在于:
在下标初始化中,a是相对于&array[0]进行偏移计算的,所以在运行阶段要进行10次的偏移计算。而ap是相对于上一个指针(ap-1)进行偏移计算的,而偏移量固定下来,所以在运行阶段只要进行1次偏移的计算。
而下面的代码只进行一个的偏移运算,故效率一样。
int array[10];
int *ap;
for ( ap = array; ap < array + 10; ap++ ){
*ap = 0;
}
a = get_value();
*( array + a ) = 0;
8.1.5 数组和指针
指针和数组并不相等的。
int a[5];
int *b;
声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间。而且,指针变量并未被初始化为指向任何现有的内存空间。
所以*a是合法的,而*b则是未定义的。
b++可以通过编译,但是由于a是指针常量,a++却无法通过编译。
8.1.6 作为函数参数的数组名
如何理解C语言中传递数组名的时候依旧是传值而不是传址呢?请看下面的例子:
#include <stdio.h>
void strcpy( char *buffer, char const *string )
{
while ( ( *buffer++ = *string++) != '\0' ){
;
}
}
int main(void)
{
char buffer[] = "hello";
char string[] = "world";
printf("%x\n", buffer);
strcpy( buffer, string );
printf("%s\n", buffer);
printf("%x\n", buffer);
return 0;
}
程序输出:
我们会发现,buffer的地址根本就没有改变过。所以,实际上的传值,副本是buffer(指针)而不是*buffer(指针所指向的值),所以buffer并为被改变,而*buffer则可以被改变。
我们这里对const进行一次复习:
char *const string = "hello";
string[0] = 'a';
这是合法的,const修饰的是char*,表明指针所指向的内容不可被修改,所以下面代码有误:
char *const string = "hello";
string = "hello";
同样的道理,下面代码是正确的:
char const *string = "hello";
string = "hello";
而下面这段代码则有误:
char const *string = "hello";
string[0] = 'a';
8.1.7 声明数组参数
int strlen( char *string );
int strlen( char string[] );
两种方式都正确,但是指针方式更准确一些。
8.2 多维数组
如何解释多维数组呢?
int a;
int b[10];
int c[6][10];
int d[3][6][10];
a是个简单的整数,b增加了1维,故为一个向量,包含10个整型元素。c只是在b的基础上增加一维,所以c为包含6个元素的向量,而每个元素本身是一个包含10个整型元素的向量。而d包含3个元素的数组,每个元素包含6个元素的数组,而这6个元素中的每一个又都是包含10个整型元素的数组。
8.2.2 数组名
一维数组名的值是一个指针常量,它的类型是“指向元素类型的指针”。而多维数组,如:
int matrix[3][10];
中的matrix则是一个指向一个包含10个整型元素的数组的指针。
8.2.4 指向数组的指针
int matrix[3][10];
int *mp = matrix;
声明是否正确?答案是否定的。mp被声明为一个指向整型的指针。但是matrix并不是一个指向整型的贺子珍,而是一个指向整型数组的指针。那我们如何声明指向整型数组的指针呢?我们必须先声明指针,然后在指针的基础上声明数组,所以下面的括号是必要的:
int (*p)[10] = matrix;
10为什么是必要的呢?因为我们进行指针偏移的时候,要确定偏移量是多少。下面这段代码说明了二维数组的指针声明方式:
#include <stdio.h>
int main(void)
{
int matrix[3][10];
int i = 0;
int j = 0;
int k = 0;
int (*p)[10] = matrix;
for ( i = 0; i < 3; i++ ){
for ( j = 0; j < 10; j++ ){
matrix[i][j] = k++;
}
}
printf("%d\n", **p);
printf("%d\n", **( p + 1 ));
return 0;
}
程序输出:
当然,如果想读取matrix[1][5],我们可以这样编写代码:
printf("%d\n", *( *( p + 1 ) + 5 ) );
多编写几次就习惯了。
由于多维数组的存储方式依旧是顺序存储的,所以如果我们希望逐个访问数据中的元素,那应该如何声明?请看下面的代码:
#include <stdio.h>
int main(void)
{
int matrix[3][10];
int i = 0;
int j = 0;
int k = 0;
int *p = matrix[0];
//或者为&matrix[0][0]
for ( i = 0; i < 3; i++ ){
for ( j = 0; j < 10; j++ ){
matrix[i][j] = k++;
}
}
printf("%d\n", *( p + 15 ) );
return 0;
}
程序输出:
8.2.5 作为函数参数的多维数组
int matrix[3][10];
func2( matrix );
那么,函数原型是什么?
void func2( int (*mat)[10] );
void func2( int mat[][10] );
两种都可以,但是第一种更好,因为代表的含义是:指向整型数组的指针。
当然,下面这个函数原型是错误的:(这个我之前还以为是正确的)
void func2( int **mat );
8.3 指针数组
我们用一个例子说明指针数组的作用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int lookup_keyword( char const * const desired_word, char const * keyword_table[])
{
char const **kwp;
for ( kwp = keyword_table; *kwp != NULL; kwp++ ){
if ( 0 == strcmp( desired_word, *kwp ) ){
return kwp - keyword_table;
}
}
return -1;
}
int main(void)
{
char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
NULL
};
printf("%d\n", lookup_keyword( "if", keyword ) );
printf("%d\n", lookup_keyword( "return", keyword ) );
printf("%d\n", lookup_keyword( "hello", keyword ) );
return 0;
}
程序输出:
习题:
0. 判断字符串是否为回文字符:
#include <stdio.h>
int isRight( char *buffer )
{
char *temp;
for ( temp = buffer; *temp != '\0'; temp++ ){
;
}
temp--;
while ( buffer < temp ){
if ( *buffer != *temp ){
return 0;
}
buffer++;
temp--;
}
return 1;
}
int main(void)
{
printf("%d\n", isRight( "helloollehh" ) );
return 0;
}
3,4.
#include <stdio.h>
int isIdentity( int (*arr)[10], int len )
{
int i = 0;
int j = 0;
for ( i = 0; i < len; i++ ){
for ( j = 0; j < len; j++ ){
if ( i == j ){
if ( 0 == arr[i][j] ){
return 0;
}
}
else{
if ( 1 == arr[i][j] ){
return 0;
}
}
}
}
return 1;
}
int main(void)
{
int a[10][10];
int i = 0;
int j = 0;
for ( i = 0; i < 10; i++ ){
for ( j = 0; j < 10; j++ ){
if ( i == j ){
a[i][j] = 1;
}
else{
a[i][j] = 0;
}
}
}
printf("%d\n", isIdentity( a, 10 ) );
a[3][4] = 1;
printf("%d\n", isIdentity( a, 10 ) );
return 0;
}
程序输出:
5.
#include <stdio.h>
void matrix_multiply( int (*m1)[2], int (*m2)[4], int (*r)[4], int x, int y, int z )
{
int i = 0;
int j = 0;
int k = 0;
int m = 0;
int n = 0;
for ( i = 0; i < x; i++ ){
for ( j = 0; j < z; j++ ){
for ( k = 0; k < y; k++ ){
r[m][n] += m1[i][k] * m2[k][j];
}
n++;
}
m++;
n = 0; //记得清零
}
}
int main(void)
{
int m1[3][2] = { 2, -6, 3, 5, 1, -1 };
int m2[2][4] = { 4, -2, -4, -5, -7, -3, 6, 7 };
int r[3][4];
int i = 0;
int j = 0;
for ( i = 0; i < 3; i++ ){
for ( j = 0; j < 4; j++ ){
r[i][j] = 0;
}
}
matrix_multiply( m1, m2, r, 3, 2, 4 );
for ( i = 0; i < 3; i++ ){
for ( j = 0; j < 4; j++ ){
printf("%d ", r[i][j]);
}
printf("\n");
}
return 0;
}
程序输出:
8. 智商不够,写不出八皇后问题,以下答案摘自网站上:
#include <stdio.h>
int
is_safe(int rows[8], int x, int y)
{
int i;
for (i=1; i <= y; ++i) {
if (rows[y-i] == x || rows[y-i] == x-i || rows[y-i] == x+i)
return 0;
}
return 1;
}
void
putboard(int rows[8])
{
static int s = 0;
int x, y;
printf("\nSolution #%d:\n---------------------------------\n", ++s);
for (y=0; y < 8; ++y) {
for (x=0; x < 8; ++x)
printf(x == rows[y] ? "| Q " : "| ");
printf("|\n---------------------------------\n");
}
}
void
eight_queens_helper(int rows[8], int y)
{
int x;
for (x=0; x < 8; ++x) {
if (is_safe(rows, x, y)) {
rows[y] = x;
if (y == 7)
putboard(rows);
else
eight_queens_helper(rows, y+1);
}
}
}
int main()
{
int rows[8];
eight_queens_helper(rows, 0);
return 0;
}