Chap 9:指针

9.1 指针概念

预备知识

1 内存

总线:地址线(64M=26根) 数据线(32根) 控制线(读写)

地址:最小单位(0地址-0字节,1地址-1字节) 对齐(指令是按4字节对齐的)

2 指令

访存指令: 可以读写内存,用2个寄存器,一个存地址,一个放数据

LDR r0, [r1]	// mem(r1) -> data(r0)
STR r0, [r1]	// data(r0) -> mem(r1)

怎么区分内存里面的数据和指令?

PC 程序计数器,一般初值为0,指向第一条指令
除非碰到跳转指令,否则通常情况下,pc = pc + 4	

3 变量

变量的本质是什么? 变量是C语言出现之后才有的

2个属性:变量名 int a = 100; 变量值 a + 2;

变量的本质是:在C语言中,由C编译器分配的,表示内存地址和存储单元内容的一种联系。

4 指针变量

指针的本质是什么? 指针也是一种变量,但这个变量值它就是内存地址。

0xbfxxxxxx 函数栈内,局部变量

0x8048xxx 数据段,全局变量

9.2 指针用法

1 指针变量定义:

int * p;

2 指针变量赋值:

p = &a;		// good
p = 0x8048xxx;	// no error, not good.

补充: int * p = &a; 应该看待为 int* p; 同时 p = &a; 而不是 p = &a;

3 指针变量使用:

*p = 200;	// 赋值
printf("%d", *p);	// 读内容 
p--;		// 指针调整

4 指针变量类型:

char * p;
short * p;
int * p;
指针变量的类型,决定了用 *p 去访问内存时候的读取的字节数

5 易混淆的错误

1) &a 的用法

&a 是一个常量(由编译器决定),代表内存地址
&a 是一个32位整型,与a是什么类型无关
&a = 100; 	试图修改a的地址,这样的用法是错误的

2) p 的用法

int * p;
*p = 1;		// wrong!
p = 1;		// no error
给指针本身赋值p=1是允许,但如果用指针取内容 *p=1 这是有风险的,会出段错误。

6 指针的重要用法-传参

用于获取用户输入

int a;
scanf("%d", &a);

int * p = &a;
scanf("%d", p);

用于交换 swap 函数

void swap(int * a, int * b);
// 可以用于修改上一级主调函数内部的变量的值

也可以通过传指针来获取多于1个以上的返回值

void decompose(double x, long * int_part, double * frac_part);

课堂练习: 请使用 char * 指针,对一个整型数 a = 0x12345678 修改为大端存储 提示: void swap(char * p1, char * p2);

7 指针的重要用法-const保护

int main(int argc, const char * argv[]);
int printf(const char * format, ...);
int scanf(const char * format, ...);
char * strcpy(char * dst, const char * src);

const int * p;	// const 修饰 *p
*p = 100;	// error
*(p+1) = 100;	// next pos is error too
a = *p;		// ok
p++;		// ok

对比学习 const 的另一种用法

int * const p;	// const 修饰 p
*p = 100;	// ok
a = *p;		// ok
p++;		// error

8 指针的重要用法-用作返回值

用传入的指针作为函数返回的指针,这是允许的。

重要结论: 如果是函数内部局部变量的地址作为指针返回,这是有风险的。(编译没错,但不好)

9.3 指针和数组

1 指针的算术运算

1) 加一个整数 p+1 	(地址的增加值取决于指针的类型,整型指针则加4)
2) 减一个整数 p-1	(地址的减少值取决于指针的类型,整型指针则减4)
3) 两个指针相减	p1-p2	(结果是两个指针之间的元素个数,而不是字节数)
	只有相同类型的指针才能作减法,不同类型不能相减,编译器会报error
4) 自增加 p++, ++p
5) 自减少 p--, --p
6) 指针的比较	(p1 > p2)

2 特殊类型指针 void * p

void * p = &a;	// ok
p = 100;	// ok
a = *p;		// error
*p = b;		// error
p+1 (整数加1)	// ok
p++/++p	(加1)	// ok
void * malloc(size_t);

3 指针名和数组名

int a[10];
int * p = a;
有何异同?
	a[0]	vs	p[0]	=> 相同的
	*p	vs 	*a	=> 相同的
	*(p+1)	vs 	*(a+1)	=> 相同的
	p+1	vs	a+1	=> 相同的

	p++	vs 	a++	=> 不同的(数组名是一个常量,不能修改)

4 指针和二维数组

int a[5][6];
int * p = &a[0][0];
int * p = a;
int * p = a[0];
int * p = a[2];

课堂作业: 请用指针实现对一个数组的调整,要求奇数在左边,偶数在右边。 要求: 尽可能不占用额外的存储空间。

编程方法

最重要的就是分解(设计好的函数),更重要的,在于设计程序时,从何种角度考虑解法?

判定标准:
1) 逻辑简单 (嵌套少一些)
	能用1个while,就不用2个
	能不用else,就不用
	while, if, for 之间的递进逻辑少
	代码行数来作为参考

2) 学会设计函数
	原则上,能够复用的函数是好的设计,能多次复用最好
	函数的功能最好少一些,一个函数只完成一个单一功能
	函数内部除了调试语句打印外,最好不打印

3) 数据驱动编程
	《Unix 编程艺术》 -> Data Drive
	精心考虑所谓算法处理过程中要发生变化的数据是什么,以及何种条件下变化?

5 动态内存分配 allocate

堆heap 和 栈stack 的概念

&a = 0xbf9cb86c 	栈空间 3G 向下

p = 0x8129008 		堆空间 向上

&b = 0x804a018 		数据段(全局变量)跟着代码段
main = 0x8048414 	代码段最小

常用函数

void * malloc(size_t size);
void free(void * ptr);

void * calloc(size_t nmemb, size_t size);	
void realloc(void * ptr, size_t size);

void * alloca(size_t size);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值