欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数组与指针详细知识点(全)

程序员文章站 2022-07-12 15:30:59
...
一、数组
 一维数组的定义和初始化
int arr[5] ;  // 初始值为随机值
int arr[5] = {99, 88, 77,66,55};  // 完整
int arr[5] = {99, 88, 77,66} ;// 只要对数组进行过初始化,其它元素的初始值默认为0
int arr[ ] = {99, 88, 77};  // int arr[3] 系统自动统计
由n个同类型(int)的数据组成的数组
一维数组的元素就是int类型的数据

一维数组名通常情况下表示首元素地址
arr === &arr[0]
sizeof(arr)表示整个数组占用内存的大小
&arr  表示整个数组地址

二维数组
二维数组由N个一维数组组成,每个一维数组由M个同类型的数据组成
二维数组的元素是一维数组
int arr [3][2] ;   //  该二维数组由3个一维数组组成,每个一维数组由2个int 组成
// 初始值为随机值
int arr [3][2]  =  { 3,4,5,6,7,8 };
等价于 { { 3,4}  , {5,6} ,  {7,8} } 

int arr [3][2]  =  { 3,4,5,6 };
等价于 { { 3,4}  , {5,6} ,  {0,0} } 
int arr [3][2]  =  { {3} , {4}, {5,6}};
等价于 { { 3,0}  , {4,0} ,  {5,6} } 

int arr [  ][2]  =  { 3,4,5,6,7,8 };
等价于int arr [3][2]= { { 3,4}  , {5,6} ,  {7,8} } 
int arr [  ][2]  =  { 3,4,5};
等价于int arr [2][2]= { { 3,4}  , {5,0} }

二维数组名通常情况下表示首元素地址
    arr ===== &arr[0]
sizeof(arr)表示整个数组占用内存的大小
&arr  表示整个数组地址

通常情况下使用数组的下标访问对应的元素
int  arr[n][m]    ;
调用的时候使用arr[x][y]
x的有效范围是 0 ~~ n-1
y 的有效范围是 0~~  m -1

arr[1][0]   == (*( arr+1 ))  [0] ====*( (*(arr+1) ) + 0) ==== **(arr+1)
*( *arr +2)  ===  (*arr + 0)[2]  ==  arr[0][2]  这种方法是在你确定空间可以通过使用数组越界的下标进行访问时候才能使用 



分别使用数组的下标和指针,来得到一个二维数组中的最大值和各个一维数组之和
int arr[n][m]
 双重for 
for(i=0 ; i<n ; i++)i表示当前是第几个一维数组
{
for(j=0 ; j<n ; j++)j表示当前是该一维数组的第几个元素
{
arr[ i ] [ j ]
}
}

//练习:
如果用一指针表示一个一维数组,然后再进行以上计算,怎么实现	


指针和数组的关系
int arr[5];
arr[ 1 ]==>  *( arr + 1) ==> *( &arr[0] + 1) ==> *(&arr[1])   

p[-1] ==> *( p -1 ) ==> -1 [ p ] 

如果 int arr[5]= {12, 4,5, 7,8,};
 int * p = arr; //   *p ==== arr NG这个是错误的理解   p ==== arr 对
 max = *p; // arr[0]  *arr
 for(i=1 ; i<5 ; i++)
 {
if(max < *(p+i))  // arr[0+i]   *(arr+i)
{	
max = *(p+i);
}
 }
1: 如果定义了一个数组int arr[5], 并设置了指针等于数组名 int *p = arr
那么在操作的时候 p 和arr是可以混用的
arr[i]   p[i]
*(p+i)  *(arr+i)

2:  使用字符串直接给指针赋值  和 使用字符数组名给指针赋值 有差异
 	char str[10] = "hello";

char *p = str;// 用数组str的首元素地址给p赋值
	char *q = "hello";// 使用只读数据段中的"hello"的首元素地址给取q赋值

可以通过*p 修改字符数组的值,
但是不能通过 *q修改只读数据段的值,该值是在编译时候就已经确定的

3: 指针赋值
int *q = NULL;
q = 100  有风险, 访问地址100涉嫌访问非法空间
    指针解引用后赋值
*q  在*q操作之前要先判断q的值不为NULL
if(q !=NULL)
*q = 100;

4;   数组名在传参的时候,实际传的是首元素地址
函数的形参应该为同类型的指针变量
如果在函数中需要知道该数组的元素个数, 则需要再传一个参数告诉函数

int  * p   占用4B   表示指向的是int数据, 4B
char *q    占用4B  表示指向的是char数据, 1B

二、数组指针 
 指向数组的指针

int arr[3][2] = {11,22,33,44,55,66}
int (*p)[2] = arr === &arr[0]    指向数组的指针,该数组由2个int类型数据构成的
---- 数组指针
int (*p)[2]  --- 4B sizeof(p)
p ======= arr
arr[0][0] == **arr=== **p

p+1 === arr+1 === &arr[0] +1 == &arr[1]

数组指针在传参时候常被使用
把以上操作放在一个函数中实现



 三、指针数组
由指针构成的数组
int  a[10]   ; 数组由10个 int 类型的数据构成
int * a[10]   ;  数组由10个 int指针类型的数据构成
a 是数组名 表示首元素的地址 元素是int *
int * a[10]   ;    40B
char * b[10] ;     40B

int arr[3][2]

int *pt[3] =  { arr[0] , arr[1], arr[2]};
&arr[0][0]  

int  (*pt[3]) [4]    数组由3个数组指针构成, 每个指针指向的是一个有4个int元素的数组 
int  (*pt[3]) [4]  = { &arr[0] , &arr[1] , &arr[2] };
等同于  {arr, arr+1, arr+2};


 四、关于数组名的使用
1: 如果是一维数组, 数组名通常表示首元素地址, 
int arr[10];
arr等同于 &arr[0] 
但是&arr 则表示整个数组的地址     sizeof(arr)表示整个一维数组的大小
2:如果是二维数组, 数组名通常表示首元素地址, 但是注意,二维数组是由一维数组组成的, 
int  a[3][4];
也就是说二维数组由一维数组a[0],a[1] ,a[2] 构成 , 这里面一共有1个二维数组名和3个一维数组名
所以:
a 等同于 &a[0]   二维数组中第一个一维数组的地址 是int (*)[4] 类型
  &a则表示整个二维数组的地址     sizeof(a)表示整个二维数组的大小

a[0] 是一维数组名, 
通常表示&arr[0][0] 也就是表示第一个一维数组中的第一个int类型的地址 是int *类型;
&a[0] 表示整个第一个一维数组的地址    sizeof(a[0])表示该一维数组的大小

a[1] 是一维数组名, 
通常表示&arr[1][0] 也就是表示第二个一维数组中的第一个int类型的地址 是int *类型;
&a[1] 表示整个第二个一维数组的地址   sizeof(a[1])表示该一维数组的大小

a[2] 是一维数组名, 
通常表示&arr[2][0] 也就是表示第三个一维数组中的第一个int类型的地址 是int *类型;
&a[2] 表示整个第三个一维数组的地址   sizeof(a[2])表示该一维数组的大小

***************************************************************************************************

五、数组名和指针的一些区别
int arr[5]= {11, 22,33,44,55};
int *p = arr;
  sizeof(arr)   4B*5  20   
  sizeof(p)    4B 
  p++;   p = p+1 === arr+1 === &arr[1]  22的地址      
 *p 得到22
      arr++  数组名不能被重新赋值, 因为其表示地址, 值由系统决定的



const  设置只读变量,为了保护该变量值不会发生改变
 assignment of read-only variable 
const int *a      不允许直接修改 *a的值, 但是可以通过其它途径修改
*a不能被重新赋值 不能出现在等号左边
int * const a    不允许直接修改 a的值
a不能被重新赋值 不能出现在等号左边


 六、函数指针
指向函数的指针 
写一个函数声明 , 返回值为void , 形参为两个int类型
void func( int, int); 
定义一个函数指针,  指向的函数是返回值为void , 形参为两个int类型
 void (*p)(int , int) 
定义一个函数指针,  指向的函数是返回值为char * , 形参为两个int类型
    char * (*p)( int , int )
定义一个函数指针,  指向的函数是返回值为char * , 形参为一个通用指针类型
char *(*p)(void * )

函数指针的赋值
void func( int, int); 
void (*p)( int, int) = func;   等价于 void (*p)( int, int) = &func;  
 等价于 void (*p)( int, int) ;  p= func;  

使用函数指针
函数指针解引用 *p   可以简写成p
p(100, 200); 相当于是调用 func(100, 200)

/*函数指针被使用在回调函数中 
A函数 ---- 直接调用C函数
A函数调用B函数, 并被C函数的地址告诉给B函数, 由B函数调用C函数
C 函数 是回调函数 (中断处理函数, 信号处理函数)
A 函数和B函数说好了, 发生了不同情况就调用不同函数
if( 情况1  )    p = 函数1的地址
if( 情况2  )    p = 函数2的地址
if( 情况3  )    p = 函数3的地址
B函数进行判断, 情况1 发生了, 找函数1对应的地址,执行之
*/


 七、指针函数
返回值为指针的函数
返回值是char *类型的函数, 形参为两个int
char * func (int ,int )    这个就是指针函数了 返回值是char *


 函数指针数组
由函数指针构成的数组
 	由3个函数指针构成的数组,函数指针指向的函数是返回值为void , 形参为两个int类型
 void  ( * a[3]) ( int , int)


练习:
定义3个函数, 分别实现对两个int类型数据的+  -   * 
要求,在主函数中,可以循环输入两个变量值, 并选择运算方式
根据运算方式给一个函数指针赋值, 并通过函数指针调用,得到正确的结果

如果数据类型不确定,可能是char 也可能是int 
int add1(char , char)
int add2(int , int)
如果你使用的是char   给一个函数指针变量赋值为?
如果你使用的是int     给一个函数指针变量赋值为?
使用void * 



作业:
定义一个二维数组管理10个学生的姓名(“”“”)
   姓名最长为16个字节
增 删 查 改
要求这个系统可以一直工作, 直到你键入的是0,才退出
 如果键入的是1, 则增加一个同学的姓名、
 如果键入的是2, 则选择删除某个同学的姓名
 如果键入的是3 ,显示全部名字
 如果键入的是4, 可以选择对某个同学的姓名进行修改 			

char name[10] [16] = {0} ; {  “zhangsan”, “lisi”     }
{ ‘z’,’h’.... ‘\0’ }
int num=0;

1: 		( char n[16]; scanf(“%s”, n);    &n[0] )
scanf(“%s”, name[num]);  name[0] === &name[0][0]
2:       赋为0
3:     for (    ; i < 10;  )
4:      7    name[7]