c语言指针和数组探究

指针的本质

  • 指针就是地址,而指针用指针变量来存储

  • 指针变量和数组没有关系

指针变量也是一种变量类型,比如char *p 就是指向char类型的指针,所以指针类型的定义方法是

  • type + *

若是 char **p 就可以拆开理解为指向char* 的指针p.

由于数组可以使用下标访问和指针访问,所以通常会混淆以为数组就是指针,其实数组就是一种分配连续空间的变量类型,和指针变量是不同的。同时数组传参会降级形成指向数组内部元素类型的指针。

指针数组

是一个数组,里面存放的都是指针

char* arr[10];

数组指针

是一个指针,指向的是数组

char (*arr)[10];

利用括号优先级确定了arr是指针类型,指向一个大小为10的数组

这个在指向二维数组的时候可以用到

int arr[3][4];
int (*p)[4];           

多维数组

如上定义的数组指针为什么就可以指向二维数组?

因为二维数组若看作线性结构,其实就是一个长度为3的一维数组,只不过每个元素都是长度为4的数组,而不是平常的一个整数。

所以二维数组第一个元素就是一个长度为4的数组,定义一个指向数组元素的类型的指针就可以指向它了。

int arr[3][4];
a = sizeof(a[0]);
b = sizeof(a[0]+1);
c = sizeof(a +1);

a 也可以写作 *(a+0),就是二维数组第一个元素,就是第一个4个元素数组的数组名,所以是16.

b 相当于 *(a+0) +1,也就是 &a[0][1]

c 指向二维数组的第二个元素首地址

整型指针和字符指针的差别

int a = 1
int *pa = &a;
char *pb = "abc";
printf("%d",pa);
printf("%d",pb);

上述代码打印结果都是8,这个取决于电脑的位数(字长),32位机指针都是4字节,64位机8字节。也就是说所有指针的存储空间是一样的,而差异在于存取权限不一样,比如:

char a[]="abce";
char *pa = a;
int *pb = (int *) a;
*pa = '1';   //数组会变成1bce
*pb = '1';   //数组会变成1

说明了char * 的存储权限是1个字节,而int *的权限是4个字节.

深度理解数组名和数组元素地址

char* pa="abc";
char* pb="abc";
//以上 pa = pb ,相同的字符串存同一份

数组名作为整个数组的两种情况

  1. sizeof(arr)
  2. &arr

其他情况均为数组首元素地址,以此作为判断条件,区分以下式子的值

注意sizeof和strlen的区别在于sizeof计算空间字节数,strlen是从当前地址向后数字符个数,直至\0停止。

而sizeof的值的判断只有两种,值和地址。

#include <stdio.h>
#include <string.h>
int main(){

    char arr[] = {'a','b','c','d','e'}; //后面不会补 \0 的 
    printf("%d\n",strlen(arr));         //随机 整个数组字符数 
    printf("%d\n",strlen(arr+0));       //随机 首元素开始往后数 
//  printf("%d\n",strlen(*arr));        //err 
//  printf("%d\n",strlen(arr[1]));      //err 
    printf("%d\n",strlen(&arr));        //随机 整个数组字符数 
    printf("%d\n",strlen(&arr+1));      //随机 跳过整个数组
    printf("%d\n",strlen(&arr[0]+1));   //随机 从第二个元素向后数 
    printf("\n"); 
    printf("%d\n",sizeof(arr));         //5 数组名单独作为整个数组(不是地址) 
    printf("%d\n",sizeof(arr+0));       //8 +0后降级作为首元素地址
    printf("%d\n",sizeof(*arr));        //1 首元素解引用 
    printf("%d\n",sizeof(arr[1]));      //1 首元素 
    printf("%d\n",sizeof(&arr));        //8 整个数组的地址 
    printf("%d\n",sizeof(&arr+1));      //8 指向整个数组后的地址 
    printf("%d\n",sizeof(&arr[0]+1));   //8 第二个元素地址 


    char arr[] = "abcdef";
    printf("%d\n",strlen(arr));         //6 整个数组字符数 
    printf("%d\n",strlen(arr+0));       //6 首元素开始往后数 
//  printf("%d\n",strlen(*arr));        //err 
//  printf("%d\n",strlen(arr[1]));      //err 
    printf("%d\n",strlen(&arr));        //6 整个数组字符数 
    printf("%d\n",strlen(&arr+1));      //随机 跳过整个数组
    printf("%d\n",strlen(&arr[0]+1));   //5 从第二个元素向后数 

    printf("%d\n",sizeof(arr));         //7 数组名单独作为整个数组(不是地址) 
    printf("%d\n",sizeof(arr+0));       //8 +0后降级作为首元素地址
    printf("%d\n",sizeof(*arr));        //1 首元素解引用 
    printf("%d\n",sizeof(arr[1]));      //1 首元素 
    printf("%d\n",sizeof(&arr));        //8 整个数组的地址 
    printf("%d\n",sizeof(&arr+1));      //8 指向整个数组后的地址 
    printf("%d\n",sizeof(&arr[0]+1));   //8 第二个元素地址 


    char *p = "abcdef";                 //p存放的是地址,指向a 
    printf("%d\n",sizeof(p));           //8 首元素地址 
    printf("%d\n",sizeof(p+1));         //8 次元素地址 
    printf("%d\n",sizeof(*p));          //1 首元素解引用 
    printf("%d\n",sizeof(p[0]));        //1 相当于*(p+0)
    printf("%d\n",sizeof(&p));          //8 取出指针p的地址 
    printf("%d\n",sizeof(&p+1));        //8 指针p地址的下一块地址 
    printf("%d\n",sizeof(&p[0]+1));     //8 第二个元素地址 
    printf("\n"); 
    printf("%d\n",strlen(p));           //6 从首地址往后数 
    printf("%d\n",strlen(p+1));         //5 从第二个元素往后数 
//  printf("%d\n",strlen(*p));          //错 传入的是第一个字符 
//  printf("%d\n",strlen(p[0]));        //错
    printf("%d\n",strlen(&p));          //随机值 是指针的地址 
    printf("%d\n",strlen(&p+1));        //随机值  指针的地址的下一块地址 
    printf("%d\n",strlen(&p[0]+1));     //5 从次元素地址往后数 

    //二维数组首元素是第一行地址 
    int a[3][4] = {0};
    printf("%d\n",sizeof(a));       //48 整个数组大小 
    printf("%d\n",sizeof(a[0][0])); //4 第一行第一个元素大小 
    printf("%d\n",sizeof(a[0]));    //16 是第一行的数组名,单独放在sizeof里,所以是第一行大小 
    printf("%d\n",sizeof(a[0]+1));  //8 第一行第二个元素地址 
    printf("%d\n",sizeof(a+1));     //8 数组地址+1降为第二行地址 
    printf("%d\n",sizeof(&a[0]+1)); //8 跳过整个第一行,成为第二行地址 
    printf("%d\n",sizeof(*a));      //16 第一行数组名单独放 
    printf("%d\n",sizeof(a[3]));    //16 越界,但由于计算的是类型属性不会报错

} 

函数指针

函数也是代码,代码就会有地址,那么函数的地址用函数指针变量来存储。

函数指针的声明

void (*p)(int);   //返回值是void 参数是int

函数指针的调用

p(1)    //或者 *p(1)
void (*signal(int, void(*)(int)))(int);

关于上面这个函数指针详解可以看http://blog.sina.com.cn/s/blog_4850a7880100hnam.html

函数指针可以当作参数传入函数,指向回调函数,函数指针封装在结构体中还可以使c语言面向对象编程。

函数指针数组

int (*)() arr[10];

上面的意思是定义一个数组,指向函数指针,对吗?

其实可以这样理解但是语法规则规定了,名字必须在中间

int (*arr[10])();
//正如指针数组的定义
int *p[10];

函数指针数组可以作为转移表

//把几个函数放进函数指针数组,形成转移表
int (*p[5])(int , int ) = {0, sum , sub, mul, div};
//使用转移表可以方便的调用其中的函数
int input = 1;
int ret = (*p[input])(a, b);    //调用sum

指向 函数指针数组 的指针

void test(const char* str) {    
    printf("%s\n", str);
} 
int main() { 
    // 函数指针 pfun   
    void (*pfun)(const char*) = test;
    // 函数指针的数组 pfunArr
    void (*pfunArr[5])(const char* str);
    pfunArr[0] = test; 
    // 指向函数指针数组 pfunArr 的指针 ppfunArr
    void (*(*ppfunArr)[10])(const char*) = &pfunArr; 
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值