所有的变量都存在RAM中(包括指针变量)
const(常量)存在Flash中
指针都是存放的地址,所以指针的大小是根据设备的大小,例如32位操作系统,一个指针的大小是4B,64位就是8B。
int *p1;
char *p2;
sizeof(p1);
sizeof(p2);
在32位操作系统中大小都为4B。
sizeof(*p1);
sizeof(*p2);
*p1的大小为4B,因为p1指向int型数据
*p2的大小为1B
关于volatile(易变的,易怒的)
就是让编译器不要优化,因为有些变量会在放在cpu里面,编译器优化就不去读内存的东西,就导致变量改变了不知道,一定要用volatile的地方:读取寄存器。
const 常量
static 关键字不能和extern一起用,加了static就只能作用于这个.c文件
结构体
typedef struct Person
{
char *name;
int age;
char *sex;
struct Person *classmates; 指针占用大小就4个字节是确定了的,可以这么用。
struct Person classmates 这样无法确定classmates的占用字节数会导致无限递归
}Person,*p; 这样表示可以用Person表示结构体,p表示结构体指针。
Person p1 = {'tsui',24,'male'};
Person p2 = {'chen',25,'female'};
p1.classmates = &p2;
p2.classmates = &p1;
可以打印 p1.classmates->sex 输出结果为female
函数指针。
void singsong(void)
{
printf("sing");
}
typedef struct Person
{
char *name;
int age;
char *sex;
struct Person *classmates; //指针占用大小就4个字节是确定了的,可以这么用。
void (*Specialty)(void); //函数指针占用四个字节
}Person,*p;
Person p ={'tsui',24,'male',&singsong};//对于函数而言这个&加不加都可以。
使用p.Specialty
关于typedef => type def 类型定义
typedef int A;
typedef struct person{} person;
与宏定义#define 的区别
不能使用typedef 1 A;
#define PI 3.14
进阶
typedef void (*method) (int,int); 定义了一个函数指针类型
method m1;
method m2;
插入链表
typedef struct Node
{
char *name;
struct spy *next;
}spy,*spy_node;
spy A = {'A',NULL};
spy B= {'B',NULL};
spy C= {'C',NULL};
spy D = {'D',NULL};
spy_node head = NULL;
void insert(spynode node){
spy_node last;
if (head == NULL) {
head = node;
node->next = NULL;
} else {
last = head;
while(last) {
if (last->next == NULL){
break;
}else {
last = last->next;
}
}
last -> node = node;
node -> next = NULL;
}
}
void remove(spynode node)
{
spynode left;
if(head == node) {
head ->next = NULL;
} else {
left = head;
while(left) {
if (left->next == node) {
break;
}else {
left = left->next;
}
}
if(left) {
left->next = node->next;
}
}
}
执行一段程序
int a = 1;
int b = 2;
a = a + b;
CPU一般只作为计算单元,内存是保存变量的地方。在CPU中会有寄存器,会保存内存中的值,这样可以快速进行计算后,再写入内存当中。因此上一段代码执行汇编会分三个步骤
1.读内存中的数据写入寄存器
2.CPU进行加法
3.把寄存器中的数据写回内存。
指令 【源】【目的】
load R0 a
load R1 b
ADD R0,R0,R1
STR R0 a
有值的全局变量初始化
类似memcpy,把flash上的数据段整体拷贝到RAM
初始化为0,没有初始化的全局变量,怎么初始化?
这些变量在内存都放在ZI段,类似于memset把ZI段全部清零。
做完上面一切才去调用main函数。
RO-data是 Read Only 只读常量的大小,如const型;
RW-data是(Read Write) 初始化了的可读写变量的大小;
ZI-data是(Zero Initialize) 没有初始化的可读写变量的大小。ZI-data不会被算做代码里因为不会被初始化;
简单的说就是在烧写的时候是FLASH中的被占用的空间为:Code + RO Data + RW Data
程序运行的时候,芯片内部RAM使用的空间为: RW Data + ZI Data
---------------------------------------------------------------------------------------------------------------------
16位系统指针大小为2个字节,64位为8个字节
int *p 表示指向int类型的指针p
指针的加加减减一般用于数组,为了避免指针越界。
数组名就是一个指针变量
普通的传递参数,是把一个这个参数在内存中复制一份,实际上传递的是复制的数据。这样做的原因类似于抄作业,抄作业不能更改源文件。但是一旦抄的作业过多那么就出现了问题,既浪费空间,又浪费时间。因此引出了指针传参,但是在子函数修改传入的数组,那么主函数的数组值也会改变,因此加入const只读。
指针加code是访问RAM
函数指针和指针函数(括号的优先级)
指针函数是 返回指针的函数 主体是函数,返回值是一个指针
int* fun(int,int);
int * fun(int,int);
int *fun(int,int);
以上三种声明都可以,第一种更加直观 返回值是 int* 类型
#include<stdio.h>
int* fun(int* x) //传入指针
{
int* tmp = x; //指针tmp指向x
return tmp; //返回tmp指向的地址
}
int main()
{
int b = 2;
int* p = &b; //p指向b的地址
printf("%d",*fun(p));//输出p指向的地址的值
return 0;
}
输出2
函数指针
函数指针是 指向函数的指针 主体是指针 指向的是一个函数的地址
注意 * 和函数名要用括号括起来,否则因为运算符的优先级原因就变成指针函数了
函数指针的参数列表要和函数指针指向的函数的参数列表一致
#include<stdio.h>
int add(int x,int y)
{
return x + y;
}
int (*fun) (int,int); //声明函数指针
int main()
{
fun = &add; //fun函数指针指向add函数
printf("%d ",fun(3,5));
printf("%d",(*fun)(4,2));
return 0;
}
输出结果:
8 6
上面的样例中,使用函数指针时使用fun(3,5)
和(*fun)(3,5)
都可以
函数指针最大的用法回调函数
#include<stdio.h>
#define LEN 10
//定义函数指针函数 CallBack升级为函数指针类型
typedef void(*CallBack)(int *);
//函数原型声明
void showPstSeq(int *);
void showRevSeq(int *);
void showArray(int *,CallBack);
int main()
{
int data[LEN] = {0,1,2,3,4,5,6,7,8,9};
showArray(data,&showPstSeq);
showArray(data,&showRevSeq);
}
void showPstSeq (int *data)
{
printf("正序输出数组:");
int i;
for(i = 0;i < LEN;i++){
printf("%d",data[i]);
}
printf("\r\n");
}
void showRevSeq(int *data)
{
printf("逆序输出数组:");
int i;
for(i=LEN-1;i >= 0;i--){
printf("%d",data[i]);
}
printf("\r\n");
}
//注册函数,入参为函数指针(回调函数指针)
void showArray(int *data,CallBack callback)
{
callback(data);
}
结果: