指针的一般用途:
:构造链式数据结构
:引用动态分配的数据结构
:实现引用调用
:访问和迭代数据元素
:传递数组参数
:引用函数
:作为其他值的别名
:代表字符串
:直接访问系统内存
用作别名的指针:
指针经常用来创建特定值的别名
1.提高执行效率
2.引用静态初始化的数据
3.在全局中实现变量引用
3.1.8 指针和字符串
size_t strlen(const char *str)
{
register const char *s;
for(s=str;*s;++s)
;
return(s-str);
}
始终需要区别字符数组和字符指针:
将字符数组传递给函数时,会被自动转换成指向数组第一个字符的指针
比如,
1.static char *pw_file="/dev/password" //这时一个字符串常量
2.static char line[]="/dev/xtyXX"
内存直接访问
一般对硬件操作,可以设置一变量指向设备占用的内存区域,通过向该区域中由行和列指定的偏移地址执行写入操作,可以使得对应的字符显示在屏幕上
static void video_wchar(char c)
{
volatile unsigned short *video;
video=(unsigned char *)(0xe08b8000)//硬件的入口地址
-vid_ypos*80+vid_xpos;
*video=(*video&0xff00)|0x0f00 | (unsigned short)c;
}
现代操作系统一般都会阻止用户程序去直接访问硬件,除非特定的安排如此.在分析嵌入式操作系统时,才会遇到这样的代码.
关于结构体
:聚合数据元素
:返回多个数据
:构造链式结构
:映射数据在硬件设备,网络连接和存储介质上的组织方式 //不太明白
:实现抽象数据类型
:面向对象编程
就数据的组织方式:
当数据在网络上移动或者传入/传出辅助存储介质时,或当程序直接与硬件进行交互,经常用来表示数据在
其他介质上的组织方式.下面是intel的以太网卡的命令块.
struct fxp_cb_nop {
void *fill[2];
volatile u_int16_t cb_status;
volatile u_int16_t cb_command;
volatile u_int32_t link_addr;
};
volatile 该标识符表明,底层的内存字段要被程序之外的实体使用(),从而禁止编译器对这些字段进行优化,比如移除引用.
还有在定义网络数据报头的结构,等等
以面对对象的方式编程:
经常将数据元素和函数指针聚合成结构,模拟类的字段和方法,创建与类相仿的对象实体.
我们可以在C中实现类似的虚方法和多态.但是,由于同一类别的不同对象共享它们的方法(而非字段),所以,程序中常常在
"对象"结构体中只存储指向另外结构的指针,由这个结构给出指向实际方法的指针,从而达到不同对象间共享方法指针的目的
关于共用体
:有效的利用空间
:实现多态
:使用不同的内部表达方式对数据进行访问
就目前而言,公用体最常见的应用是实现多态.同一对象用于代表不同的类型.
可以写一个例子
enum msg_type{
CALL=0,
REPLY=1,
MSG_NUM
};
struct rpc_msg{
u_int32_t rm_xid;
enum msg_type rm_direction;
union {
struct call_body rm_cmb;
struct reply_body rm_rmb;
} ru;
}
访问不同的内部表达
将数据存入公用体的一个字段,然后访问另外的字段,以完成数据在不同内部表达方式之间的转换
比如我们可以看看这样的应用,共用体u被用来访问浮点数v的内部表示(尾数,指数和符号),该转换用来将浮点数拆分成标准化的
尾数和2的整数幂
double
frexp(value, eptr)
double value;
int *eptr;
{
union {
double v;
struct
{
u_int u_mant2 : 32;
u_int u_mant1 : 20;
u_int u_exp : 11;//指数位置?
u_int u_sign : 1;
} s;
} u;
if (value){
u.v = value;//将值存储到该字段
*eptr = u.s.u_exp - 1022;//取出并设置有符号指数
u.s.u_exp = 1022;//指数清零
return(u.v);//返回
} else {
*eptr = 0;
return((double)0);
}
}
3.4 动态分配内存
这一节将分析如何动态的对向量结构分配内存
这里介绍一种情况,数据存储在数组中,但需要存储的元素数目可能在所有的元素被读入之前是未知的.这样,在存储元素之前,将当前的数组索引和
分配的内存块中元素的数目做核查,如果需要更多的空间,则需要调用realloc--(void *realloc( void *ptr, size_t size ))
作用是将第一个参数所指向的空间调整为由函数的第二个参数指定的新大小,该函数返回一个指向调整后内存块的指针,它的地址可能和最初块的地址
并不相同,.原内存块中的内容被复制到新的位置
需要注意的是,任何指向原来内存块中某个位置的指针变量,现在指向的都是未定义的数据
void
remember_rup_data(host, st)
char *host;
struct statstime *st;
{
if (rup_data_idx >= rup_data_max) {//索引大于分配的大小
rup_data_max += 16;//16为新分配的大小
rup_data = realloc (rup_data,
rup_data_max * sizeof(struct rup_data));//调整分配大小
if (rup_data == NULL) {
err (1, "realloc");
/* NOTREACHED */
}
}
rup_data[rup_data_idx].host = strdup(host);
rup_data[rup_data_idx].statstime = *st;
rup_data_idx++;
}
管理空闲内存
要记得释放用malloc分配的内存
但是如果使用的内存分配在函数的stack(用来存储函数的返回地址和局部变量的内存区域,程序返回后自动销毁)上而不是程序的堆(heap,程序的通用内存块),这种用法在不同的文化上对待方式不同.比如freebsd禁止,因为其不可移植且依赖于机器.而GNU却支持这种方式,因为减少了偶尔的内存泄漏
含有动态分配数组的结构
有时,程序中会使用动态分配的单个结构,存储一些相关的字段和一个数组,数组中存储的是结构专有的变长数据
如果在结构中定义一个元素,使之指向变长数据,那么在使用该结构就要用到 指针间接寻址和更多的内存
对于上述结构,我们使用下面形式来尽量减少一些开销
typedef struct {
char *user
char *group
char *flags
char *data[1]
}NAMES;
作为结构元素的data数组,用作的是实际数据的占位符.分配了用来存储结构的内存之后.data的大小会根据数组元素的个数向上调整.此后,就可以使用数组中的元素,如同数组包含这些元素的空间一样.
if((np=malloc(sizeof(NAMES)+ulen+glen+flen+3) == NULL))
err(1,"");
np->user=&np->data[0];
(void)strcpy(np->user,user);
typedef声明
typedef可以增加程序的可移植性,主要通过以下两种方式
1.用一系列依赖于具体实现的声明,为已知的硬件量值创建可移植的名称
typedef unsigned int u_int32_t;
2.用前面声明的名称之一,为硬件表达方式已知的数量创建抽象名称
typedef u_int32_t in_addr_t;
3.最后,typedef还经常用来模拟C++和java中类型声明引入新类型的行为.
比如为struct引入同一名称标识的一个类型名
typedef struct path path;
struct path
{
[......];
};