c语言要点摘录(25~28 指针和数组 下)

时间:2022-05-07 16:45:41

二十五.指针数组和数组指针

          数组类型:c语言中的数组有自己特定的类型

                               数组类型由元素类型和数组大小共同决定,例如:int  array[5]的类型是int [5],而不是int

                  定义数组类型:通过typedef为数组类型重命名-------typedef   type(name)[size];

                       数组类型:typedef    int(AINT5)[5];

                                           typedef    float(AFLOAT10)[10];    

                       数组定义:AINT5  iArray;

                                           AFLOAT10  fArray;

         数组指针:1.数组指针用于指向一个数组

                              2.数组名是数组首元素的起始地址,但并不是数组的起始地址

                              3.通过将取地址符&作用于数组名可以得到数组的起始地址

                              4.可通过数组类型定义数组指针:ArrayType*  pointer;

                              5.也可以直接定义:type(*pointer)[n];//pointer为数组指针变量名,type为指向的数组的类型,n为指向的数组的大小  

#include <stdio.h>

typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
typedef char(ACHAR9)[9];

int main()
{
AINT5 a1;
float fArray[10];
AFLOAT10* pf = &fArray;
ACHAR9 cArray;
char(*pc)[9] = &cArray;
char(*pcw)[4] = cArray;//编译器会给出警告

int i = 0;

printf("%d, %d\n", sizeof(AINT5), sizeof(a1));

for(i=0; i<10; i++)
{
(*pf)[i] = i;//数组指针的用法
}

for(i=0; i<10; i++)
{
printf("%f\n", fArray[i]);
}

printf("%0X, %0X, %0X\n", &cArray, pc+1, pcw+1);//pc+1<====>(unsigned int)pc+1*sizeof(*pc)//+9
}
输出:

20, 20
0.000000
1.000000
2.000000
3.000000
4.000000
5.000000
6.000000
7.000000
8.000000
9.000000
28FECB, 28FED4, 28FECF

指针数组:1.指针数组就是一个普通的数组

                    2.指针数组中的每个元素为一个指针

                    3.数组指针的定义:type*  pArray[n];type*为数组中每个元素的类型,pArray为数组名,n为数组大小

使用举例:

#include <stdio.h>
#include <string.h>

int lookup_keyword(const char* key, const char* table[], const int size)
{
int ret = -1;

int i = 0;

for(i=0; i<size; i++)
{
if( strcmp(key, table[i]) == 0 )
{
ret = i;
break;
}
}

return ret;
}

#define DIM(a) (sizeof(a)/sizeof(*a))

int main()
{
const char* keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
"case",
"static"
};

printf("%d\n", lookup_keyword("return", keyword, DIM(keyword)));
printf("%d\n", lookup_keyword("main", keyword, DIM(keyword)));
}

  main函数的参数:main函数可以理解为操作系统调用的函数,执行程序的时候可以向mian函数传递参数

          有以下形式:int  main()

                                  int  main(int argc)

                                  int  main(int argc ,char* argv[])

                                  int  main(int argc ,char* argv[],char* env[])

二十六.多维数组和多维指针

           指向指针的指针:1.指针在本质上也是变量,也同样存在传值调用与传址调用

           使用举例:重制动态空间大小

 

#include <stdio.h>
#include <malloc.h>

int reset(char**p, int size, int new_size)
{
int ret = 1;
int i = 0;
int len = 0;
char* pt = NULL;
char* tmp = NULL;
char* pp = *p;

if( (p != NULL) && (new_size > 0) )
{
pt = (char*)malloc(new_size);

tmp = pt;

len = (size < new_size) ? size : new_size;

for(i=0; i<len; i++)
{
*tmp++ = *pp++;
}

free(*p);
*p = pt;
}
else
{
ret = 0;
}

return ret;
}

int main()
{
char* p = (char*)malloc(5);

printf("%0X\n", p);

if( reset(&p, 5, 3) )
{
printf("%0X\n", p);
}

return 0;
}

  二维数组与二级指针:

            1.二维数组在内存中以一维的方式排布

            2.二维数组中的第一维是一维数组

            3.二维数组中的第二维才是具体数组     

            4.二维数组的数组名可看做常量指针                                                                                                 c语言要点摘录(25~28 指针和数组 下)

 以一维数组方式遍历二维数组:

#include <stdio.h>
#include <malloc.h>

void printArray(int a[], int size)
{
int i = 0;

printf("printArray: %d\n", sizeof(a));//此处打印的是指针长度

for(i=0; i<size; i++)
{
printf("%d\n", a[i]);
}
}

int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
int* p = &a[0][0];

printArray(p, 9);

return 0;
}
打印结果:

printArray: 4
0
1
2
3
4
5
6
7
8

数组名:

     一维数组名代表数组首元素的地址int a[5]----->a的类型为int*

     二维数组名同样代表数组首元素地址int  m[2][5]------->m的类型为int(*)[5] 

结论:1.二维数组名可以看做是指向数组的常量指针

           2.二维数组可以看做是一维数组

           3.二维数组中的每个元素都是同类型的一维数组

#include <stdio.h>

int main()
{
int a[5][5];
int(*p)[4];

p = a;

printf("%d\n", &p[4][2] - &a[4][2]);
}
输出:-4  ( 指向同一个数组的指针相减,得出下标差,而不是地址差

如何动态申请二维数组:

#include<stdio.h>
#include<malloc.h>

int** malloc2d(int row,int col)
{
int** ret = (int**)malloc(sizeof(int*) * row);
int* p = (int*)malloc(sizeof(int)*row*col);

int i = 0;

if( ret && p)
{
for(i=0;i<row;i++)
{
ret[i] = (p + i*col);
}

}
else
{
free(ret);
free(p);

ret = NULL;

}

return ret;
}
void free2d(int** a )
{
free(a[0]);
free(a);
}

小结:1.c语言中只有一维数组,而且数组大小必须在编译期就作为常数确定    

           2.c语言中的数组元素可以是任何类型的数据,即数组的元素可以是另一个数组

           3.c语言中只有数组的大小和数组首元素的地址是编译器直接确定的 


二十七.数组参数和指针参数

            为什么c语言中的数组参数会退化成为指针??

                  1.c语言中只会以值拷贝的方式传递参数

                  2.当向函数传递数组时,将整个数组拷贝一份传入函数是很浪费空间和时间的,所以将数组名看做常量指针传递数组元素首地址


           二维数组参数同样存在退化的问题:1.二维数组可以看做是一维数组,二维数组中的每个元素是一维数组

           二维数组参数中的第一维的参数可以省略

                void f(int a[5]);<---->void f(int a[])<------>void f(int *a)

              void g(int a[3][3])<---->void g(int a[][3])<---->void g(int (*a)[3])

退化的等价关系:

    c语言要点摘录(25~28 指针和数组 下)        


       注意:1.c语言中无法向一个函数传递任意的多维数组

                  2.为了提供正确的指针运算,必须提供除第一维以外的所有维长度

       限制:一维数组参数------》必须提供一个标识数组结束位置的长度信息

                 二维数组参数------》不能直接传递给函数

                 三维或更多维数组参数------》无法使用    

    例子:

#include <stdio.h>

void access(int a[][3], int row)
{
int col = sizeof(*a) / sizeof(int);*a是一个数组,求*a的元素个数:3
int i = 0;
int j = 0;

printf("sizeof(a) = %d\n", sizeof(a));//指针长度,a是一个指针,长度:4字节

for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d\n", a[i][j]);
}
}
}

int main()
{
int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};

access(a, 3);
}

打印结果:

sizeof(a) = 4
0
1
2
3
4
5
6
7
8

二十八.函数与指针分析

            函数的类型:由返回值,参数类型和参数个数共同决定,例如:int add(int i,int j)类型就是int (int,int)

                                 c语言中通过typedef为函数类型重命名

                                                 typedef  type  name(parameter  list)

                                 例如:typedef  int  f(int,int);typedef  void  p(int)

           函数指针:函数指针用于指向一个函数,函数名是执行函数体的入口地址

                            定义函数指针:FuncType*  pointer;

                            类似数组指针,可以这样定义:type  (*pointer)(parameter list)

  

#include <stdio.h>

typedef int(FUNC)(int);

int test(int i)
{
return i * i;
}

void f()
{
printf("Call f()...\n");
}

int main()
{
FUNC* pt = test;

void(*pf)() = &f;//&可有可无,旧版本编译器仅支持有&取地址符

pf();//新式调用
(*pf)();//老式调用<span style="font-family: Arial, Helvetica, sans-serif;">,一样的效果</span>

printf("Function pointer call: %d\n", pt(2));
}
 输出结果:

Call f()...
Call f()...
Function pointer call: 4

回调函数:是利用函数指针实现的一种调用机制

       原理:1.调用者不知道具体事件发生的时候需要调用的具体函数

                  2.被调函数不知道何时被调用,只知道被调用后需要完成的任务

                  3.当具体事件发生时,调用者通过函数指针调用具体函数

       回调机制将调用者和被调函数分开,两者互不依赖,写大型软件的时候可以模块化

#include <stdio.h>

typedef int(*FUNCTION)(int);

int g(int n, FUNCTION f)
{
int i = 0;
int ret = 0;

for(i=1; i<=n; i++)
{
ret += i*f(i);
}

return ret;
}

int f1(int x)
{
return x + 1;
}

int f2(int x)
{
return 2*x - 1;
}

int f3(int x)
{
return -x;
}

int main()
{
printf("x * f1(x): %d\n", g(3, f1));
printf("x * f2(x): %d\n", g(3, f2));
printf("x * f3(x): %d\n", g(3, f3));
}

指针阅读技巧解析:

        右左法则:1.从最里层的圆括号中未定义的标识符看起

                          2.首先往右看,再往左看

                          3.当遇到圆括号或者方括号时可以确定部分类型,并调转方向

                          4.重复2,3步骤,直到阅读结束

         例如:int  (*func)(int*);*func这是一个指针,指向类型为int  (int*)的函数

#include <stdio.h>

int main()
{
int (*p2)(int*, int (*f)(int*));//*p2指针,指向int (int*,int (*f)(int*))的函数

int (*p3[5])(int*);//[]级别高,所以p3先是一个5元素的数组,然后拿出p3[5],能看到元素是指针,指向的是int (int*)

int (*(*p4)[5])(int*);(*p4)指针-->int (*[5])(int*)-->指向数组[5]-->int (*)(int *)-->数组类型是函数指针指向int (int*)

int (*(*p5)(int*))[5];//(*p5)指针->int (*(int*))[5]->指向函数(int *)->函数返回值是->int(*)[5]
}

诀窍在于:每解析完一个,就将其拿出去