内存操作
在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的MOV指令,而除C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助C语言指针所具有的对绝对地址单元内容的读写能力。以指针直接操作内存多发生在如下几种情况:
(1) 某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于某特定地址;
(2) 两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单元(称为mail box)书写内容以在对方CPU产生中断;
(3) 读取在ROM或FLASH的特定单元所烧录的汉字和英文字模。
例如:
unsigned char *p = (unsigned char *)0xF000FF00;
*p="11";
以上程序的意义为在绝对地址0xF0000+0xFF00(80186使用16位段地址和16位偏移地址)写入11,在使用绝对地址指针的时候,指针自增自减操作的结果取决于指针指向的数据类别。
指针
指针:指针就是地址,凡是出现"指针"的地方,都可以用"地址"代替,例如,变量的指针就是变量的地址,指针变量就是地址变量。
指向:地址就意味着指向,因为通过地址能找到具有该地址的对象。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁。但应注意:并不是任何类型数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。例如:
int a, *p;
float b;
p=&a;
p=&b;
指针的运算:
(1)指针和整数的加减运算:
可以通过指针与整数的加减运算来移动指针p,实现对不同数据单元的访问操作。对不同的数据类型,移动的单位长度不同。单位长度一般是指针所指向的变量的数据类型长度。
格式一:p=p+n;(或p=p-n;)
格式二:p++;(或p–;)
格式三:++p;(或–p;)
注意:
指针变量的值加1(或减1),并不是给地址加1(或减1),而是加上(或减去)1个数据类型长度,
也就是指针所指向的变量在内存中所占的字节数。
指针和指针的赋值运算:
int a=10,*p,*q;
p=&a;
q=p;
//p和q的值都是变量a的地址。
指向指针的指针关系:
(1)*二级指针变量:代表所指向的以及指针变量。如:*q就代表p;
(2)**二级指针变量:代表它所指的一级指针变量所指向的变量。如:**q代表a;
(3)*一级指针变量:代表它所指向的变量。如:*p代表a。
数组和指针:一维数组名代表数组首元素的地址,例如:
int *p, a[10];
p=a;
p是指向 int 型类型的指针变量,显然,p 只能指向 a 数组中的元素,而不是指向整个数组。在进行赋值时一定要先确定赋值号两侧的类型是否相同,是否允许赋值。对p=a, 准确地说应该是:p 指向 a 数组的首元素,在不引起误解的情况下,有时也简称为:p指向a 数组。 同理,p 指向字符串,应理解为 p 指向字符串中的首字符
结构体指针
用指针引用结构体变量成员的方式:(*指针变量名).成员名:
# include <stdio.h>
# include <string.h>
struct AGE
{
int year;
int month;
int day;
};
struct STUDENT
{
char name[20]; //姓名
int num; //学号
struct AGE birthday; //生日
float score; //分数
};
int main(void)
{
struct STUDENT student1; /*用struct STUDENT结构体类型定义结构体变量student1*/
struct STUDENT *p = NULL; /*定义一个指向struct STUDENT结构体类型的指针变量p*/
p = &student1; /*p指向结构体变量student1的首地址, 即第一个成员的地址*/
strcpy((*p).name, "小明"); //(*p).name等价于student1.name
(*p).birthday.year = 1989;
(*p).birthday.month = 3;
(*p).birthday.day = 29;
(*p).num = 1207041;
(*p).score = 100;
printf("name : %s\n", (*p).name); //(*p).name不能写成p
printf("birthday : %d-%d-%d\n", (*p).birthday.year, (*p).birthday.month, (*p).birthday.day);
printf("num : %d\n", (*p).num);
printf("score : %.1f\n", (*p).score);
return 0;
}
简化方式:指针变量名->成员名,其中的->是“指向结构体成员运算符”,它的优先级同结构体成员运算符“.”一样高
# include <stdio.h>
# include <string.h>
struct AGE
{
int year;
int month;
int day;
};
struct STUDENT
{
char name[20]; //姓名
int num; //学号
struct AGE birthday; /*用struct AGE结构体类型定义结构体变量birthday, 生日*/
float score; //分数
};
int main(void)
{
struct STUDENT student1; /*用struct STUDENT结构体类型定义结构体变量student1*/
struct STUDENT *p = NULL; /*定义struct STUDENT结构体类型的指针变量p*/
p = &student1; /*p指向结构体变量student1的首地址, 即第一项的地址*/
strcpy(p->name, "小明");
p->birthday.year = 1989;
p->birthday.month = 3;
p->birthday.day = 29;
p->num = 1207041;
p->score = 100;
printf("name : %s\n", p->name); //p->name不能写成p
printf("birthday : %d-%d-%d\n", p->birthday.year, p->birthday.month, p->birthday.day);
printf("num : %d\n", p->num);
printf("score : %.1f\n", p->score);
return 0;
}
结构体数组的每一个元素都是一个结构体变量。如果定义一个结构体指针变量并把结构体数组的数组名赋给这个指针变量的话,就意味着将结构体数组的第一个元素,即第一个结构体变量的地址,也即第一个结构变量中的第一个成员的地址赋给了这个指针变量。
例题:有一个结构体变量stu,内含学生学号、姓名和三门学科的成绩,通过调用函数print使他们输出
struct student{
int num;
char *name;
int score[3];
};
void print(struct student stu,struct student *pstu){
printf("num:%d\n",stu.num);
printf("name:%s\n", stu.name);
printf("score[0]:%d\n", pstu->score[0]);
printf("score[1]:%d\n", (*pstu).score[1]);
printf("score[2]:%d\n", (*pstu).score[2]);
}
int main() {
struct student *p;
struct student stu;
stu.num = 007;
p->name = "zhangzhang";
(*p).score[0] = 59;
(*p).score[1] = 60;
(*p).score[2] = 61;
print(stu,p);
}
动态存储分配函数:
1、malloc函数:
函数原型为void *malloc(unsigned int size);
其作用是在内存的动态存储中分配一个长度为size的连续空间(size是一个无符号数)
此函数的返回值是一个指向分配域起始地址的指针(void)。
如果此函数未能成功地执行(例如内存空间不足),则返回Null.
2、calloc函数:
函数原型是void *calloc (unsigned n,unsigned size)
其作用是在内存的存储区中分配n个长度为size的连续空间。
函数返回一个指向分配域起始地址的指针。
如果分配不成功,返回Null
用calloc函数可以为一维数组开辟动态存储空间,n为数组个数,每个元素长度为size
3、free函数:
函数原型是void free(void *p)
其作用是释放由p指向的内存区,使这部分内存区能被其他区使用
p是最近一次调用calloc或者malloc函数的时候返回的值