一文读懂指针与数组

本文详细介绍了C语言中的指针和数组,包括如何将数值存储到内存地址、数组与指针的区别、数组指针与指针数组的概念,以及多维数组、函数指针和函数指针数组的使用。文中通过实例展示了指针操作、数组作为函数参数以及函数指针调用的方法,帮助读者深入理解C语言中的这些核心概念。
摘要由CSDN通过智能技术生成

1 指针

 如何将数值存储到指定的内存地址
 int *p = (int *)0x12ff7c; // 将地址 0x12ff7c 赋值给指针变量 p 的时候必须强制转换
 *p = 0x100;               // 通过钥匙“*”向这块内存写入一个数据 0x100
 *(int *)0x12ff7c = 0x100; // 这种一步搞定

2 数组 int a[5];

省政府和市政的区别----&a[0]和&a 的区别

 sizeof(&a[0])的值在 32 位系下为 4,这很好理解。取元素 a[0]的首地址。
 sizeof(&a)的值在 32 位系统下也为 4, 这也很好理解。 取数组 a 的首地址。
 但是在 VisualC++6.0 上,这个值为 20,我认为是错误的。
     
 sizeof(a)的值为 4*5 = 20


 a[0]是一个元素, a 是整个数组,虽然 a、 &a[0] 和 &a 的值一样,但其意义不一样。
 
 前者&a[0] / a 是 数组首元素 的 首地址,
 而后者 &a 是 数组 的首地址。
 
 举个例子:湖南的省政府在长沙,而长沙的市政府也在长沙。
 两个政府都在长沙,但其代表的意义完全不同。这里也是同一个意思。
 
 int a[5]={1,2,3,4,5};
 int *ptr=(int *)(&a+1); // 即 &a + 5*sizeof(int)
 printf("%d,%d",*(a+1),*(ptr-1));// 2,5

数组名 a 作为左值和右值的区别:

 出现在赋值符“=”右边的就是右值,出现在赋值符“=”左边的就是左值。 比如,x=y
 
 左值:在这个上下文环境中,编译器认为 x 的含义是 x 所代表的地址。
       这个地址只有编译器知道,在编译的时候确定,编译器在一个特定的区域保存这个地址,
       我们完全不必考虑这个地址保存在哪里.
       
 右值:在这个上下文环境中,编译器认为 y 的含义是 y 所代表的地址里面的内容。
       这个内容是什么,只有到运行时才知道。
 
  a 作为右值时其意义与&a[0]是一样,代表的是 数组首元素的首地址,
  而不是数组的首地址。这是两码事。
  但是注意,这仅仅是代表,并没有一个地方(这只是简单的这么认为,其具体实现细节不作过多讨论)来存储这个地址,
  也就是说编译器并没有为数组 a分配一块内存来存其地址,这一点就与指针有很大的差别。
  
  a 不能作为左值,数组地址不能被改变。
  我们可以把 a[i]当左值,而无法把 a当左值。

3 数组与指针的关系

 数组就是数组,指针就是指针,它们是完全不同的两码事!他们之间没有任何关系,只是经常穿着相似的衣服来迷惑你罢了。

4 数组指针 与 指针数组

指向数组的指针: 数组指针
存储指针的数组: 指针数组
    int *p1[10];  // “[]”的优先级比“*”要高。 p1 先与“[]”结合,构成一个数组的定义.
                  // 数组的内容 由 int* 给出,即包含10个指向整形数据的指针的数组
    int (*p2)[10];// “()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,
                  // int[10] 这里表示的是一个包含10个整形数据的 数组
                  // 数组在这里并没有名字,是个匿名数组。

5 多维数组 多维空间 超过二维的数组和超过二级的指针其实并不多用

char a[3][4];
// |a[0] - - - - | a[1] - - - - | a[2] - - - - |
// a[0],a[1],a[2]。 每个元素的大小为 sizeof(a[0]),即 sizeof(char)*4。 
// 由此可以计算出 a[0],a[1],a[2]三个元素的首地址分别为&a[0], &a[0]+ 1*sizeof(char)*4, &a[0]+ 2*sizeof(char)*4。
//  a[i][j]的首地址为      &a[i] + j*sizeof(char)
//  a[i][j]元素的首地址为: a + i*sizof(char)*4 + j*sizof(char)。

// 多维数组定义 都是花括号
int a [3][2]={(0,1),(2,3),(4,5)};
//  花括号里面嵌套的是小括号,而不是花括号!
// 这里是花括号里面嵌套了逗号表达式!其实这个赋值就相当于
int a [3][2]={ 1, 3, 5};

6 多级指针-二级指针-指针的指针

7 数组与指针作为函数参数

C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。    
无法把指针变量本身传递给一个函数, 但可以改变指针指向的值。    
可以利用函数返回值,也可以传递二级指针,即 想改变的指针 的 地址。   

当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维再也不可改写。  
比如: a[3][4][5]作为参数时可以被改写为(*p) [4][5]  

8 函数指针 函数指针数组

char * fun3(char * p1,char * p2);    // fun3是函数,返回值为 char*类型    
char * *fun2(char * p1,char * p2);   // fun2也是函数, 返回值为 char** 类型     
char * (*fun1)(char * p1,char * p2); // fun1是函数指针,指向一个类型为 char*(char*,char*)的函数    

函数指针调用示例:

#include <stdio.h>
#include <string.h>
char * fun(char * p1,char * p2)
{
     int i = 0;
     i = strcmp(p1,p2);
     if (0 == i)
     {
          return p1;
     }
     else
     {
          return p2;
     }
}
int main()
{
     char * (*pf)(char * p1,char * p2);// 定义函数指针
     pf = &fun;        // 赋值,初始化
     (*pf) ("aa","bb");// 调用函数指针指向的函数
     return 0;
}

(int)&p 强转 指针地址类型,再解引用

void Function()
{
printf("Call Function!\n");
}
int main()
{
     void (*p)(); // void(void)类型函数的指针 p, 
     // &p 是求指针变量 p 本身的地址,这是一个 32 位的二进制常数(32 位系统)。
     // (int*)&p 表示将地址强制转换成指向 int 类型数据的指针。
     // (int)Function 表示将函数的 入口 地址 强制转换成 int 类型 的 数据。
     *(int*)&p=(int)Function;// 表示将函数的入口地址赋值给指针变量 p。
     (*p) ();// 表示对函数的调用。
     return 0;
}

((void() ())0)()

void(*)() , 指向类型为 void(void)的函数的 函数指针
(void(*) ())0 ,将 0 强制转换为函数指针类型, 0 是一个地址,也就是说一个函数存在首地址为 0 的一段区域内。
(*(void(*) ())0),取 0 地址开始的一段内存里面的内容,其内容就是保存在首地址为 0 的一段区域内的函数。
(*(void(*) ())0)(), 这是函数调用
同理 :
(*(char**(*) (char **,char **))0) ( char **,char **);

函数指针数组

char * (*pf)(char * p);   // 定义的是一个函数指针 pf, 指向一个类型为 char*(char*)的函数
char * (*pf[3])(char * p);// 定义一个函数指针数组, 数组名为 pf,数组内存储了 3 个指向函数的指针。

示例:

#include <stdio.h>
#include <string.h>
char * fun1(char * p)
{
     printf("%s\n",p);
     return p;
}
char * fun2(char * p)
{
     printf("%s\n",p);
     return p;
}
char * fun3(char * p)
{
     printf("%s\n",p);
     return p;
}
int main()
{
     char * (*pf[3])(char * p);// pf 为 函数指针数组名
     // 为数组赋值
     pf[0] = fun1;  // 可以直接用函数名
     pf[1] = &fun2; // 可以用函数名加上取地址符
     pf[2] = &fun3;
     pf[0]("fun1");// 调用函数
     pf[1]("fun2");
     pf[2]("fun3");
     return 0;
}

函数指针数组的指针

char * (*pf)(char * p);   // 定义的是一个函数指针 pf, 指向一个类型为 char*(char*)的函数
char * (*pf[3])(char * p);// 定义一个 函数指针 数组, 数组名为 pf,数组内存储了 3 个指向函数的指针。
char * (*(*pf)[3])(char * p);// 定义一个指针,指向一个函数指针数组,该数组内存放3个 指向类型为char*(char*)的函数的函数指针。

用法:

#include <stdio.h>
#include <string.h>
char * fun1(char * p)
{
     printf("%s\n",p);
     return p;
}
char * fun2(char * p)
{
     printf("%s\n",p);
     return p;
}
char * fun3(char * p)
{
     printf("%s\n",p);
     return p;
}
int main()
{
     char * (*pf[3])(char * p);// pf 为 函数指针 数组名
     char * (*(*pf)[3])(char * p);// 函数指针数组 的 指针
     pf = &a;
     // 为数组赋值
     a[0] = fun1;
     a[1] = &fun2;
     a[2] = &fun3;

     pf[0][0]("fun1");// 调用函数
     pf[0][1]("fun2");
     pf[0][2]("fun3");
     return 0;
}

参考
https://github.com/Ewenwan/ShiYanLou

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值