第4章 指针和数组

1. int *p=NULL 和 *p=NULL 有什么区别

int *p = NULL;

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

int *p;
*p = NULL;

  第一行代码,定义了一个指针变量p,其指向的内存里面保存的是 int类型的数据,但是这时候p本身的是多少不得而知;第二行代码,给 *p赋值为NULL。

2.如何往内存地址 0x12ff7c 上存入一个整形数 0x100?

  •   第一种方法
  • int *p = (int *)0x12ff7c; //p
    *p = 0x100;
  •   第二种方法
  • *(int *)0x12ff7c = 0x100;

3.数组的内存布局

int a[] ;

  a作为右值时,代表数组首元素的首地址,而非数组的首地址。

  a[0],a[1]等为a的元素,但并非元素的名字,数组的每一个元素都是没有名字的。

  sizeof(a[5])的值在32为系统下为4,为什么没有出错?

其实函数求值是在运行的时候,而sizeof求值是在编译的时候。虽然并不存在a[5]这个元素,但是这里也并没有真正访问a[5],而是仅仅根据数组元素的类型来确定其值。所以没有出错。

4.&a[0]和&a的区别

  前者是数组首元素的首地址,而后者是数组的首地址。

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

x = y;

  左值:在这个上下文环境中,编译器认为x的含义是x所代表的地址。这个地址只有编译器知道,在编译的时候确定

  右值:在这个上下文环境中。编译器认为y的含义是y所代表的地址里面的内容。这个内容运行的时候确定。

  a作为右值时其意义与 &a[0] 是一样的,代表的是数组首元素的首地址,而不是数组的首地址。

  a不能作为左值!编译器认为数组名作为左值代表的意思是a的首元素的首地址,但是这个地址开始的一块内存是一个总体,我们只能访问数组的某个元素,而无法把数组当成一个总体来进行访问。

6.以指针的形式访问指针和以下标的形式访问指针

char *p = "abcdef";
  • 以指针的形式:*(p+4)。先取出p里面的地址值,假设为0x0000ff00,再加上4个字符的偏移量,得到新的地址0x0000ff04;然后再取出地址上的值。
  • 以下标的形式:p[4]。编译器总是把下标的形式的操作解析为以指针的形式的操作。先取出p里存储的地址值;再加上4个元素的偏移量,计算出新的地址;然后再从新的地址中取出值。

7.以指针的形式访问数组和以下标的形式访问数组

  对a的元素的访问必须先根据数组的名字a找到数组首元素的首地址,然后再根据偏移量找到相应的值。这是一种典型的“具名 + 匿名”的访问。

8.指针数组和数组指针

  指针数组:储存指针的数组

  数组指针:指向数组的指针

int *p1[]; //指针数组

  p1先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组里的每个元素。也就是说,这是一个数组,其包含10个指向int类型数据的指针,即指针数组

int (*p2)[]; //数组指针

  “*”和p2构成一个指针的定义,指针变量名为p2,int修饰的是数组的内容。数组在这里没有名字,是个匿名数组。也就是说,p2是一个指针,它指向一个包含10个int类型数据的数组,即数组指针。

9.再论a和&a之间的区别

 int main()
{
char a[] = {'A','B','C','D'};
char (* p3)[] = &a;
char (* p4)[] = a;
return ;
}

  编译器会给出警告。因为p4这个定义“=”两边的的数据类型不一致。左边的数据类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。

10.地址的强制转换

struct Test
{
int Num;
char *pcName;
short sDate;
char cha[];
short sBa[];
}*p;

  假设p的值为0x100000,那么

  p + 0x1 = 0x100014

  (unsigned long)p + 0x1 =0x100001

  (unsigned int *)p + 0x1 = 0x100004

  第一个表达式p + 0x1的值为 0x100000+sizeof(Test)*0x1。Test的大小为20字节

  第二个表达式其实就是一个无符号的长整数加上另一个整数

  第三个表达式的p被强制转换为指向无符号整型的指针

11.数组参数和指针参数

  C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。

  函数的返回值也不能是一个数组,而只能是指针。

void fun(char *p)
{
char c = p[];
}
void fun(char a[])
{
char c = a[];
}

两组代码等效,但第二个形式较好。

  main函数内的变量不是全局变量,而是局部变量,只不过它的生命周期和全局变量一样长而已。全局变量一定是定义在函数外部的。

11.二维数组参数和二级指针参数

  C语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。所以下面两个函数声明是一样的:

  void fun(char a[3][4]),void fun(char a[][4]) 和 void fun(char (*p)[4])

  void fun(char *p[4])和void fun(char **p)  这是因为*p[4]是一个包含4个指针的一位数组,把这个一位数组也写成指针的形式,就得到第二种写法。

12.函数指针

  函数指针就是函数的指针,它是一个指针,指向一个函数。

char * fun1(char * p1,char * p2);

fun1是函数名,p1,p2是参数,其类型为char *类型,函数的返回值是char *

char * * fun2(char * p1,char * p2);

与上例唯一不同的是,函数返回值类型为 char **

char * (* fun3)(char * p1,char * p2);

fun3是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。

13.函数指针的例子

 #include<stdio.h>
char * fun(char * p1,char * p2)
{
}
int main()
{
char * (* pf)(char * p1,char * p2);
pf = &fun;
(* pf)("aa","bb");
return ;
}

  给函数指针赋值时,可以用&fun或字节使用函数名fun。

14. *(int *)&p

 void fun()
{
}
int main()
{
void (* p)();
*(int *)&p = (int)fun;
(*p)();
return ;
}

  void (* p)();这行代码定义了一个指针变量p,p指向一个函数,这个函数的参数和返回值都是void。

 &p求指针变量p本身的地址。
 (int)fun表示将函数的入口地址转换成指向int类型的指针
  *(int *)&p = (int)fun;表示将函数的入口地址赋值给指针变量p
 (*p)()就是对函数的调用。
15.(*(void(*)())0)()
void(*)(),这是一个函数指针类型
(void(*)())0,将0强制转换为函数指针类型。
(*(void(*)())0),取0地址开始的一段内存内存里面的内容,其内容就是保存在首地址为0的一段区域内的函数
(*(void(*)())0)(),函数的调用
16.函数指针数组
char * (*pf[])(char *p);

  这里定义了一个函数指针数组。它是一个数组,数组名为pf,数组内存储了3个指向函数的指针,这些指针指向一些返回值类型为指向字符的指针,参数为一个指向字符的指针的函数。

17.函数指针数组指针

char * (*(*pf)[])(char * p)

  这里的函数指针数组指针不就是一个指针嘛。只不过这个指针指向一个数组,这个数组里面存的是指向函数的指针。

 
 
 
												

《C语言深度解剖》学习笔记之指针和数组的更多相关文章

  1. C语言深度解剖读书笔记

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  2. C语言深度解剖读书笔记(6.函数的核心)

    对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求 ...

  3. 【转】 C语言深度解剖读书笔记(1.关键字的秘密)

    本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...

  4. c语言深度解剖(笔记)

    1.1最宽恒大量的关键字----auto 函数内部变量,限制作用域为这个 1.2.1最快的关键字---- register函数. 关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中 1.2.2使 ...

  5. R语言与机器学习学习笔记

    人工神经网络(ANN),简称神经网络,是一种模仿生物神经网络的结构和功能的数学模型或计算模型.神经网络由大量的人工神经元联结进行计算.大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自 ...

  6. go语言,golang学习笔记4 用beego跑一个web应用

    go语言,golang学习笔记4 用beego跑一个web应用 首页 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/ 更新的命令是加个 -u 参数,g ...

  7. go语言,golang学习笔记3 用命令下载框架报错问题解决 设置环境变量

    go语言,golang学习笔记3 用命令下载框架报错问题解决 设置环境变量 下载安装:go get github.com/astaxie/beego 首页 - beego: 简约 & 强大并存 ...

  8. go语言,golang学习笔记2 web框架选择

    go语言,golang学习笔记2 web框架选择 用什么go web框架比较好呢?能不能推荐个中文资料多的web框架呢? beego框架用的人最多,中文资料最多 首页 - beego: 简约 & ...

  9. go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE

    go语言,golang学习笔记1 官网下载安装,中文社区,开发工具LiteIDE Go语言是谷歌2009发布的专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速 ...

随机推荐

  1. WPF学习笔记-用Expression Blend制作自定义按钮

    1.从Blend工具箱中添加一个Button,按住shift,将尺寸调整为125*125; 2.右键点击此按钮,选择Edit control parts(template)>Edit a cop ...

  2. Java问题解读系列之基础相关---抽象类和接口

    今天来说一波自己对Java中抽象类和接口的理解,含参考内容: 一.抽象类 1.定义: public abstract class 类名{} Java语言中所有的对象都是用类来进行描述,但是并不是所有的 ...

  3. js控制“鼠标点击按钮后,按钮消失“(可以自己添加video标签控制播放)

  4. leetcode 839 Similar String Groups

    题目 Two strings X and Y are similar if we can swap two letters (in different positions) of X, so that ...

  5. Oracle删除当前用户下所有的表的方法1

    1.如果有plsql客户端,则可以使用该用户登录,选中所有表 右键Drop即可. 2.如果有删除用户的权限,则可以: ? 1 drop user user_name cascade; 加了cascad ...

  6. Oracle树查询

    1.Oracle函数 sys_connect_by_path 语法: select sys_connect_by_path(column_name,'connect_symbo链接标志l')  fro ...

  7. Axure之母版窗口

  8. ubuntu安装django

    sudo apt-get install python-django -y#django操作mysql数据库时还需要安装python-mysqldb驱动,当然mysql安装是必须的前提 sudo ap ...

  9. Javascript-正则表达式常用验证

    <div> <h1>一.判断中国邮政编码匹配</h1> <p>分析:中国邮政编码都是6位,且为纯数字</p> <div>邮政编码 ...

  10. xml-apis.jar getTextContent() jar包冲突解决(getTextContent()方法无法找到)

    1.引用包: import org.w3c.dom.Document;import org.w3c.dom.Element;import org.w3c.dom.NodeList; 2.方法中应用: ...