为什么80%的码农都做不了架构师?>>>   

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);elseeight_queens_helper(rows, y+1);}}
}int main()
{int rows[8];eight_queens_helper(rows, 0);return 0;
}

转载于:https://my.oschina.net/voler/blog/161537

C和指针---第八章:数组相关推荐

  1. C指针4:数组指针(指向数组的指针)

    先明确两个概念:(1和2是两个完全不一样的东西) 1.数组指针:如果一个指针指向了数组(该数组就是普通定义的数组),我们就称它为数组指针(Array Pointer).(1强调的是指针) 2.指针数组 ...

  2. 用指针实现删除数组中小于10的数据

    <程序设计基础实训指导教程-c语言> ISBN 978-7-03-032846-5 p92 5.1.2 上级实训内容 [实训内容5]用指针实现删除数组中小于10的数据 正确程序已更新 正确 ...

  3. 《C和指针》对于数组这一节的总结

    <C和指针>对于数组这一节的总结,感觉总结的很精炼,多读有益! 在绝大多数表达式中,数组名的值是指向数组第一个元素的指针.这个规则只有两个例外: sizeof返回整个数组所占用的字节而不是 ...

  4. 【C 语言】数组 ( 数组指针 | 数组指针定义 | 使用 数组类型* 定义数组指针 )

    文章目录 总结 一.使用 数组类型* 定义数组指针 二.完整代码示例 总结 typedef int(ArrayType)[3];ArrayType *p = NULL; 一.使用 数组类型* 定义数组 ...

  5. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  6. 指针增量和数组的关系,指针偏移的补充,(重要面试),gdp调试,将数组中的n个元素逆序存放

    1.指针增量和数组的关系 //加1  代表了地址偏移了一个类型的字节数(整形数偏移四个字节,char形数偏移了一个字节) 再来下标法: 2.指针偏移的补充 也可以换一种写法(第12行) 但是呢同样的代 ...

  7. c++17(26)-数组、二维数组的指针、指向数组的指针、指向数组的指针的指针

    #include <iostream> #include <string.h> using namespace std;void setC1(int a[],int size) ...

  8. C语言数组类型、数组指针类型、数组指针类型变量

    C语言数组类型.数组指针类型.数组指针类型变量 数组类型 数组指针类型 数组指针用于指向一个数组 数组指针:用数组类型加*定义一个数组指针 数组指针:定义一个数组指针类型,然后用类型定义变量 数组指针 ...

  9. c语言 数组指针,C语言数组名及指向数组指针的小结

    相信不少的C语言初学者都知道,数组名相当于指针,指向数组的首地址,而函数名相当于函数指针,指向函数的入口地址. 现在有这样一个问题,如果对数组名取地址,那得到的会是什么呢?很多人立刻会想到:给指针取地 ...

最新文章

  1. koa+mongoose基础入门
  2. 5.1软件升级的小阳春
  3. Dubbo监控中心Windows安装
  4. 电子白板技术_电子白板种类介绍及产品功能概述
  5. 翻译 github上How to be a good programmer
  6. python 某个数是不是在某个范围内_教写一个简单的python小程序(04)
  7. Java快速开发平台——JEECG 3.7.8 版本发布!我们的目标是有鱼丸也有粗面
  8. mybatis源码分析、底层原理
  9. STM32工作笔记0078---UCOSIII任务挂起和恢复
  10. git修改本地和远程仓库名称的解决方法
  11. 大型网站建设方案(学院网站建设方案)
  12. ElasticSearch 7.10.1 集群搭建
  13. java 数组的扩容,缩容,插入元素,查找元素 详解(通俗易懂)
  14. KlipC数据显示2022年日元兑美元汇率有进一步下跌的风险和可能性
  15. 谈 Linux,Windows 和 Mac -王银纠正对linux的看法
  16. 项目管理案例分析-小型旅游网站开发项目
  17. SwiftUI @State @Published @ObservedObject 深入理解和使用
  18. 快速将多个excel表合并成一个excel表
  19. JSP四大作用域的生命周期
  20. 微服务治理 - 初探Istio

热门文章

  1. 7-10 先序序列创建二叉树,输出先序序列、中序序列、后序序列并输出叶子结点数 (10 分)
  2. 7-277 单身狗 (25 分)
  3. python中函数作用域_Python中的函数作用域
  4. excel表中怎么插入visio_快速制作组织架构图,还在用Visio就out了,Excel简单三步搞定...
  5. oracle批量替换保留字,oracle保留字大全
  6. ES6的generator
  7. DFS-20190206
  8. OC 观察者模式(通知中心,KVO)
  9. Hadoop组件启停命令和服务链接汇总
  10. Elasticsearch安装X-Pack插件