C语言深度剖析(第四章要点)

一、指针

  1. int *p = NULL; 与 *p = NULL;两者的区别:

Int *p = NULL;

这时候我们可以通过编译器查看 p 的值为 0x00000000。这句代码的意思是:定义一个指针变量 p,其指向的内存里面保存的是 int 类型的数据;在定义变量 p 的同时把 p 的值设置为0x00000000,而不是把*p 的值设置为 0x00000000。这个过程叫做初始化,是在编译的时候进行的

  1. 将数值储存到指定内存地址

例如:往内存0x12ff7c地址上存入一个整形数0x100

Int *p = (int *)0x12ff7c;

*p = 0x100;

二、数组

  1. 数组的内存布局:int a[5]; 当我们定义一个数组 a 时,编译器根据指定的元素个数和元素的类型分配确定大小(元素类型大小*元素个数)的一块内存,并把这块内存的名字命名为 a。名字 a 一旦与这块内存匹配就不能被改变。
  2. &a[0]是一个元素,a是整个数组,虽然&a[0]和&a的值一样,但是意义完全不一样。&a[0]是数组首元素的首地址,后者是数组的首地址。
  3. 数组名a作为左值和右值的区别:

左值:在这个上下文环境中,编译器认为 x 的含义是 x 所代表的地址。这个地址只有编译器知道,在编译的时候确定,编译器在一个特定的区域保存这个地址,我们完全不必考虑这个地址保存在哪里。

右值:在这个上下文环境中,编译器认为 y 的含义是 y 所代表的地址里面的内容。这个内容是什么,只有到运行时才知道。

“可修改的左值”。意思就是,出现在赋值符左边的符号所代表的地址上的内容一定是可以被修改的。说白了,就是我们只能给非只读变量赋值

当 a 作为右值的时候代表的是什么意思呢?很多书认为是数组的首地址,其实这是非常错误的。a 作为右值时其意义与&a[0]是一样,代表的是数组首元素的首地址,而不是数组的首地址

a不能作为左值

三、指针和数组

  1. 指针和数组之间没有任何的关系
  2. a和&a的区别:

例如:

main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}

 

对指针进行加 1 操作,得到的是下一个元素的地址,而不是原有地址值直接加 1。所以,一个类型为 T 的指针的移动,以 sizeof(T) 为移动单位。 因此,对上题来说,a 是一个一维数组,数组中有 5 个元素; ptr 是一个 int 型的指针。&a + 1: 取数组 a 的首地址,该地址的值加上 sizeof(a) 的值,即 &a + 5*sizeof(int),也就是下一个数组的首地址,显然当前指针已经越过了数组的界限。(int *)(&a+1): 则是把上一步计算出来的地址,强制转换为 int * 类型,赋值给 ptr。*(a+1): a,&a 的值是一样的,但意思不一样,a 是数组首元素的首地址,也就是 a[0]的首地址,&a 是数组的首地址,a+1 是数组下一元素的首地址,即 a[1]的首地址,&a+1 是下一个数组的首地址。所以输出 2*(ptr-1): 因为 ptr 是指向 a[5],并且 ptr 是 int * 类型,所以 *(ptr-1) 是指向 a[4] ,输出 5

四、指针数组和数组指针

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。

数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下永远是占 4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

五、多维数组与多级指针

这是一张假象的布局图

 2.二级指针

取值方法:

第一步:根据 p 这个变量,取出它里面存的地址。
第二步:找到这个地址所在的内存。
第三步:用钥匙打开这块内存,取出它里面的地址,*p 的值。
第四步:找到第二次取出的这个地址。
第五步:用钥匙打开这块内存,取出它里面的内容,这就是我们真正的数据,**p 的值

六、数组参数与指针参数

1.C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。函数本身是没有类型的,只有函数的返回值才有类型。

2.无法把指针变量本身传递给一个函数

void GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
}
int main()
{
char *str = NULL;
GetMemory(str,10);
strcpy(str,”hello”);
free(str);//free 并没有起作用,内存泄漏
return 0;
}

在运行 strcpy(str,”hello”)语句的时候发生错误。这时候观察 str 的值,发现仍然为 NULL。也就是说 str 本身并没有改变,我们 malloc 的内存的地址并没有赋给 str,而是赋给了_str。而这个_str 是编译器自动分配和回收的,我们根本就无法使用。所以想这样获取一块内存是不行的。

两个解决办法:

1,return

char * GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
str = GetMemory(str,10);
strcpy(str,”hello”);
free(str);
return 0;
}

2.用二级指针

void GetMemory(char ** p, int num)
{
*p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
GetMemory(&str,10);
strcpy(str,”hello”);
free(str);
return 0;
}

参数:C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。这条规则并不是递归的,也就是说只有一维数组才是如此,当数组超过一维时,将第一维改写为指向数组首元素首地址的指针之后,后面的维再也不可改写

七、函数指针

1.函数的返回值为一指针

(*(void(*) ())0)()是什么?

第一步:void(*) (),可以明白这是一个函数指针类型。这个函数没有参数,没有返回值。

第二步:(void(*) ())0,这是将 0 强制转换为函数指针类型,0 是一个地址,也就是说一

个函数存在首地址为 0 的一段区域内。

第三步:(*(void(*) ())0),这是取 0 地址开始的一段内存里面的内容,其内容就是保存

在首地址为 0 的一段区域内的函数。

第四步:(*(void(*) ())0)(),这是函数调用。

2.char * (*pf)(char * p)”定义的是一个函数指针 pf。

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

4.函数指针数组指针:char * (*(*pf)[3])(char * p);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值