[关闭]
@songpfei 2016-02-24T11:38:45.000000Z 字数 5933 阅读 2125

《C专家编程》数组和指针并不同

程序设计论著笔记


1. 背景理解

1.1 区分定义与声明 p83

1.2 数组和指针的访问方式

2. 数组与指针的区别

2.1 不同点:

序号 指针 数组
1 保存数据的地址 保存数据
2 间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据。如果指针有一个下标[i],就把指针的内容加i作为地址,从中提取数据 直接访问数据,a[i]只是简单的以a+i为地址取得数据
3 通常用于动态数据结构 通常用于存储固定数目且数据类型相同的元素
4 先关函数为malloc(),free() 隐士分配和删除
5 通常指向匿名数据 自身即为数据明

注!注!注!
数组和指针都可以在他们的定义中使用字符串常量进行初始化,尽管看上去一样,但底层机制不一样。
定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义的同时赋给指针一个字符串常量进行初始化。
char *p="breadfruit";
注意只有对字符串常量才是如此。不能指望浮点数之类的常量分配空间,如:
float *pip=3.14;/*错误,无法编译通过*/
在ANSI C中,初始化指针所创建的字符串常量被定义为只读(存储在静态区)。如果试图通过指针修改这个字符串的值,程序出现未定义(出错)
数组也可以用字符串进常量进行初始化:
char a[]="gooseberry";
与指针相反,由字符串常量初始化的数组是可以修改的(存储在栈中)。其中单个字符在以后可以改变:
strncpy(a,"black",5);

2.2 相同点:

3. C语言的多维数组

C语言中,定义和引用多维数组唯一的方法是使用数组的数组:

------------------------------------------------------------------
char carrot[10][20];//声明一个10*20的多维数组
或者声明可以看上去更像“数组的数组”形式
typedef char vegetable[20];
vagetable carrot[10];

不论哪种情况,访问单个字节都可以通过carrot[i][j]的形式,
编译器在编译时会把它解析为*(*(carrot+i)+j)的形式
------------------------------------------------------------------
tips:C语言的数组就是一维数组:
  当提到C语言中的数组时,就把它看作是一种向量(vector),也就是某种对象的以为数组,数组的元素可以是另一个数组。

### 3.1 内存中数组的布局
在C语言多为数组中,最右边的下标是最先变化的,这个约定被称为“行主序”.其实线性存储, a[i][j] 与 *(*(a+i)+j)等价
### 3.2 多维数组初始化:
int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };

3.2 多维数组的声明

指针数组
char *pea[4]; //一维指针数组,每个指针指向一个字符串,数组的每个元素内为一个char*指针
(注意区别:char(*pea)[4];数组指针,一个指向具有4个字符类型元素的数组
可以进行如下初始化:
for (j=0;j<=4;j++)
pea[j]=malloc(6);

也可以一次性的用malloc分配整个数组:
malloc(row_size*column_size*sizeof(char));
软件信条
对于s[i][j]这样形式的原型声明:

  1. int s[2][3];/* int 型 二维 数组*/
  2. int *s[2]; /* int 指针数组,每个指针指向一个含有3个元素的以为一维数组*/
  3. int **s; /* 指向指针的指针*/
  4. int (*s)[3];/* 数组指针,一个指向为int数组(长度为3)的指针

都是由于作为左值的数组名本编译器当做指针。

锯齿状数组:
如果声明一个字符串指针数组,并根据需要为这些字符串分配内存,将会大大节省系统资源:

  1. char* turnip[UMPTEEN]
  2. char my_string[]="your message here";
  3. /*贡献字符串*/
  4. turnip[i]=&my_string[0];
  5. /*拷贝字符串*/
  6. turnip[j]=malloc(strlen(my_string)+1);
  7. strcpy(turnip[j],my_string);

tips: p225
数组和指针参数被编译器的修改规则:

实参 所匹配的形参
数组的数组 char c[2][3] char(*)[3] 数组指针
指针数组 char *c[2] char** 指针的指针
数组指针(行指针) char (*a)[3] char (*a)[3] 不改变
指针的指针 char **a char**a 不改变

3.3 使用指针向函数传递一个多维数组

  1. func(int array[10][20]);
    这种办法最简单,但作用最小,其迫使你只处理10行20列的int型数组
  2. 省略第一维长度:
    func(int array[][20]);
    但其依然限定每行必须是20个整数的长度
    类似的也可以声明为:
    func(int (*array)[20]);传递一个数组指针,数组的长度为20.
  3. func(int ** array);
    动态数组形式:二维数组在堆上分配,各行地址空间不一定连续,函数参数使用指针形式:
    为了进一步提高泛用性,把二维数组空间的分配也动态化,使用malloc()在堆上分配空间,重复一下前言中的方式如下:
  1. //1. 数组声明
  2. int **array;
  3. array = (int **)malloc(m *sizeof(int *));
  4. for(i=0;i<M;i++)
  5. array[i] = (int *)malloc(n *sizeof(int));
  6. 这时,在分配空间的作用域里,对0<=i<M,0<=j<Narray[i][j]的访问完全没有问题。那么,对应地,函数写作
  7. //2. 函数声明
  8. int func(int **array,int m,int n) {
  9. ...
  10. printf("%d ", *(*(array+i)+j));
  11. ...
  12. }
  13. 值得注意的是,虽然malloc()每次分配的空间在地址上是连续的,但是多次malloc()分配的空间之间并不一定是连续的,这与在栈上分配的二维矩阵有着根本的不同,对于二维数组array[3][3],不能再用array[1][4]来访问array[2][1]了,前者地址越界。
  1. 折中形式:用堆上分配的一维数组表示二维数组,函数参数使用指针形式
    用一维数组来实现二维数组,是一种折中方案,但是很好理解,也不易出错。这样分配的数组空间是连续的。使用时需要把两维下标转化为一维下标。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. int func(int *array, int m, int n) {
  5. int i,j;
  6. for(i=0;i<m;i++) {
  7. for(j=0;j<n;j++)
  8. printf("\t%d",*(array+i*n+j));
  9. printf("\n");
  10. }
  11. return 0;
  12. }
  13. int main(int argc,char** argv) {
  14. int m,n,i;
  15. int *array;
  16. assert(argc == 3);
  17. m = atoi(argv[1]);
  18. n = atoi(argv[2]);
  19. array = (int*)malloc(m*n*sizeof(int));
  20. for(i=0;i<m*n;i++)
  21. array[i] = i;
  22. func(array,m,n);
  23. return 0;
  24. }
  1. 较新的编译器:用栈上分配的直到执行时才确定大小的二维数组(即C99的变长数组,它允许使用变量定义数组各维)
  1. int quarters = 4;
  2. int regions = 5;
  3. double sales[quarters][regions]; //一个变长数组VAL

变长数组有一些限制:变长数组必须是自动存储类的,意味着它们必须在函数内部或作为函数参数声明,而且声明时不可以进行初始化。
C90不支持这种形式,C99支持,因此一些较新的编译器可以对下面的代码进行执行。注意print()的参数顺序不能改变。

  1. void print(int x, int y, int a[x][y]){
  2. printf("\n");
  3. int i, j;
  4. for(i = 0; i < x; i++){
  5. for(j = 0; j < y; j++)
  6. printf("%d ", a[i][j]);
  7. printf("\n");
  8. }
  9. }
  10. // Function to initialize the two-dimensional array
  11. void init_2d(int *a, int x, int y){
  12. int i, j;
  13. for(i = 0; i < x; i++){
  14. for(j = 0; j < y; j++){
  15. a[i*y + j] = i + j;
  16. }
  17. printf("\n");
  18. }
  19. }
  20. int main(){
  21. int m , n ;
  22. scanf("%d %d",&m,&n);
  23. int a[m][n]; // a two dimensional whose size has been defined using variables
  24. init_2d(a, m, n);
  25. print(m, n, a);
  26. }

 这段代码出自http://stackoverflow.com/questions/17181577/two-dimensional-arrays-in-c

  (2013.7.28更新)

  另外,这种分配方式仍然是在栈上,相关讨论可见于http://bbs.csdn.net/topics/90350681

spf 20160121 二维数组使用测试

  1. 动态二维数组,在堆上分配,但存在内存不连续情况
  1. void PrintArray(int **array, int m, int n)
  2. {
  3. if (array == NULL || (*array) == NULL)
  4. return;
  5. printf("********打印二维数组**************\n");
  6. for (int i = 0; i < m; i++)
  7. {
  8. for (int j = 0; j < n; j++)
  9. printf("%d ", array[i][j]);
  10. printf("\n");
  11. }
  12. }
  13. int main()
  14. {
  15. int M, N;
  16. printf("请输入二维数组的行和列,以空格隔开:\n");
  17. scanf("%d %d", &M, &N);
  18. int **array=NULL;//声明array[m][n] 二维数组
  19. array = (int**)malloc(M*sizeof(int*));
  20. if (array == NULL)
  21. return -1;
  22. for (int i = 0; i < M; i++)
  23. {
  24. array[i] = (int*)malloc(N*sizeof(int));
  25. if (array[i] == NULL)
  26. return -1;
  27. }
  28. printf("请输入%d行%d列的二维数组\n",M,N);
  29. for (int i = 0; i < M; i++)
  30. for (int j = 0; j < N; j++)
  31. scanf("%d", &array[i][j]);
  32. PrintArray(array, M, N);
  33. for (int i = 0; i < M; i++)
  34. {
  35. free(array[i]);
  36. array[i] = NULL;
  37. }
  38. free(array);
  39. array = NULL;
  40. free(NULL);
  41. system("pause");
  42. return 0;
  43. }
  1. 动态一维数组,当做二维数组使用(推荐)
  1. void Print(int* array, int m, int n)
  2. {
  3. if (array == NULL)
  4. return;
  5. printf("********打印二维数组**************\n");
  6. for (int i = 0; i < m; i++)
  7. {
  8. for (int j = 0; j < n; j++)
  9. printf("%d ", *(array+i*n+j));
  10. printf("\n");
  11. }
  12. }
  13. int main()
  14. {
  15. int M, N;
  16. printf("请输入二维数组的行和列,以空格隔开:\n");
  17. scanf("%d %d", &M, &N);
  18. int* array = (int*)malloc(M*N*sizeof(int));
  19. if (array == NULL)
  20. return -1;
  21. printf("请输入%d行%d列的二维数组\n", M, N);
  22. for (int i = 0; i < M; i++)
  23. for (int j = 0; j < N; j++)
  24. scanf("%d",array+i*N+j);
  25. Print(array, M, N);
  26. free(array);
  27. array = NULL;
  28. system("pause");
  29. return 0;
  30. }

4. 参考文献:

  1. http://www.cnblogs.com/wuyuegb2312/archive/2013/06/14/3135277.html
  2. http://www.cnblogs.com/cpoint/p/3368380.html
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注