指针Word格式.docx
- 文档编号:19017842
- 上传时间:2023-01-03
- 格式:DOCX
- 页数:15
- 大小:25.45KB
指针Word格式.docx
《指针Word格式.docx》由会员分享,可在线阅读,更多相关《指针Word格式.docx(15页珍藏版)》请在冰豆网上搜索。
src+count)||(src>
dest+count));
//防止内存重叠,也可以用restrict修
Byte*
bdest
(Byte*)dest;
bsrc
(Byte*)
src;
While(count-->
0)
*bdest++
**bsrc++;
Return
dest;
}
intstrlen(constchar*str)//输入参数const
{
assert(strt!
=NULL);
//断言字符串地址非0
intlen=0;
while((*str++)!
='
\0'
){
len++;
}
returnlen;
}
char*strstr(constchar*s1,constchar*s2)
intlen2;
if(!
(len2=strlen(s2)))
return(char*)s1;
for(;
*s1;
++s1)
if(*s1==*s2&
strncmp(s1,s2,len2)==0)
return(char*)s1;
returnNULL;
嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。
采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。
例如,16bit宽的数0x1234在Little-
endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址存放内容
0x40000x34
0x40010x12
而在Big-endian模式CPU内存中的存放方式则为:
0x40000x12
0x40010x34
32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
0x40000x78
0x40010x56
0x40020x34
0x40030x12
0x40020x56
0x40030x78
数组作为函数的参数,类型自动转化为指针类型constchar*,因为如果将数组整体传递过来,
将会重新生成一个数组的副本,拷贝的开销太大,所以编译器都是将其转为指向数组首元素的指针的类型来传递
普通数组是在编译期间确定下来开辟的空间的大小;
变长数组将开辟空间推迟到程序运行期间。
//使用VLA,限制:
//1、不能声明全局的变长数组
//2、变长数组在声明的同时不允许初始化
//3、不允许声明static或extern变长数组
//4、不允许将变长数组作为结构体或联合体的成员,变长数组只能独立的作为一个数组存在
1.
//声明和定义的区别:
声明可以有多个,但是定义只能有一个,因为声明并不会为改变量开辟空间,定义会开辟。
//声明变量的含义:
1、为该变量在内存中开辟空间;
2、能够对该变量进行的操作
//普通变量:
以数值为值的变量
//指针变量:
以地址为值的变量(或者说:
指针变量保存了另外一个变量的地址)
//指针:
就是地址,地址本身的类型是size_t(32位系统)unsignedint
p=&
a;
//将a变量的地址赋给指针变量p(也可以说指针p指向了变量a)
printf("
a=%d\n"
a);
//直接访问:
通过变量名本身来访问
*p);
//间接访问:
通过指向该变量的指针来访问。
*p也被成为解引用指针。
也可以说是取内容操作。
//&
取地址运算符
//*取内容运算符
2.//1、野指针:
指针指向的空间不可知int*p1;
//2、空指针:
不指向内存中任何地址空间。
NULL是一个一旦访问必然段错误的符号常量。
在不同的编译器中该值可能不同,它是因具体实现而定义的。
NULL地址不是0,0可以代替NULL,NULL不可以代替0
//段错误(segmentfault):
访问我们无权访问的内存空间
//void表示空,用在类型上就是无类型
//voida;
无法通过编译,因为编译器无法确定下来该为此变量分配多少个字节的空间来容纳
void*p3;
通用类型的指针p3,被分配了4个字节的空间。
它可以接收任何类型的指针的值
3.//左值:
放在赋值号左边的就是左值。
如果某变量能够作为左值,那么它一定会占据一块内存空间。
//右值:
放在赋值号右边的是右值。
作为右值,它表示的是使用其所在空间中的值。
//强制类型转换运算符:
(想要转换成的类型)(int)p/int(int*)p/int*
编译器不能进行隐式转换时,需要我们手工转换,就被称为强制类型转换。
4.*(p+3)==(p+4)[-1];
/函数调用方法:
((void(*)(void))(0x80484a5))();
//通过函数的地址直接调用该函数
void(*pf)(void);
//函数指针
pf=foo;
//函数名称表示函数的入口地址
pfoo[0]();
//通过函数指针数组中的元素调用foo函数
//变量的属性:
//1、作用域:
变量可以被访问的区域。
//分为:
文件作用域、函数作用域、块作用域、函数原型作用域
//2、生命期:
变量存在于内存中的时间段。
//1)静态生命期(从编译期间就已经被开辟空间,直到程序结束都存在的变量)
//2)动态生命期(在函数被调用开始被开辟空间,调用结束自动回收所在空间的变量)、
//3、链接性:
特定指针静态变量来说的
外部链接(非静态全局变量)、内部链接(静态的全局变量)、无链接(静态的局部变量)
//函数内的局部变量的赋值是在函数被调用的一瞬间完成的,而不是逐行完成的。
//函数调用:
每一次的函数调用都会在该进程栈内开辟一个新的进程栈。
在调用函数层,需要保存现场;
被调用函数层在返回到调用函数层时,需要恢复现场。
//函数的参数传递:
在C中所有的参数传递都是值的传递,并不存在地址传递。
值传递:
将实际参数的副本传递给形式参数。
所谓的地址传递,同样是将实参的地址的副本传递给形式参数
所以本质上还是值传递。
只是效果看起来像是直接将实参的地址传过去一样。
//值传递并不会因为形式参数值的改变而影响实际参数的值的改变。
当传递的是地址的副本时,可以改变实参,是因为指针保存了实参所在的地址,也就是指向了实参。
//exit在任何函数中都可以使得程序终止;
而return只有在main函数中才能使得程序终止。
return在非main函数中,只能使得该函数返回到调用它的函数。
/回调函数:
不能直接通过函数名来调用,只能通过指向该函数的指针间接调用的函数,被称为回调函数。
//使用回调函数,实现了调用层和被调用层的分离,调用层根本无需关心将来被调用的函数是哪个,
它关心的仅仅是传递给它的函数的地址。
//回调函数是在某个事件的触发下被间接调用的,并不会被直接调用。
staticvoid*foo(void*,void(*)(void*));
staticvoidcallback1(void*arg)//回调函数
printf("
%s,everyone!
\n"
(char*)arg);
1.编译过程:
1、预处理(预处理器cpp):
将所有#开头的预处理指令进行替换//#define#include
gcc-Exx.c>
xx.i
2、编译(编译器cc1):
将上一步生成的预处理文件编译成汇编文件
gcc-Sxx.i-oxx.s
3、汇编(汇编器as):
将上一步生成的汇编文件汇编成目标文件
gcc-cxx.s-oxx.o
4、链接(连接器ln):
将上一步生成的目标文件链接成最终的可执行文件
gccxx.o-oxx(如果不使用-o指定生成的文件名,会自动生成名为a.out的可执行文件)
5、加载(加载器load):
将上一步生成的可执行文件加载进内存运行
2.
%s,%s,%d,%s,%s\n"
__FILE__,__FUNCTION__,__LINE__,__DATE__,__TIME__);
1.c,main,14,Mar142014,09:
48:
57
3.
printf是一个带有缓冲区的函数,它是将输出先暂存在输出缓冲区,直到缓冲区写满或者缓冲区被刷新时,才会从缓冲区输出到标准输出设备上.
\n1)换行2)刷新输出缓冲区
fflush只有刷新输出缓冲区的作用
scanf("
%d"
&
a);
在printf之后是接收输入的语句,同样可以刷新输出缓冲区
stderr:
是不带缓冲区的文件,它是立即将输出信息输出到标准错误输出上。
而sdtout是带有缓冲区的。
4.
printf函数的返回值是成功打印到标准输出的字符个数
%d\n"
printf("
%d"
43)));
//4321
shortint/hd(printf)短整型/ld长整型/uunsignedint/lfdouble/%x16进制
5.
a++<
==>
a,a+1
++a<
a+1,a
inta=10;
%d\n"
++a);
//11
a=10;
a++);
//10
6.
//条件运算符expr1?
expr2:
expr3
//当expr1为真,执行expr2,否则执行expr3
//逻辑与expr1&
expr2
//逻辑与短路:
当expr1表达式结果为假,不会再去执行expr2
//
逻辑或expr1||expr2
//逻辑或短路:
当expr1表达式结果为真,不会再去执行expr2
//sizeof包含'
strlen不包含
//typeof也是运算符,用于求得变量的类型
inta;
typeof(a)aa;
//<
intaa;
time_tt=time(NULL);
srand((t);
//srand函数,将time_t类型的时间值作为种子,可以改变rand函数的初始值,从而得到真正的随机数。
random=%d\n"
rand()%100);
7.
//break用于跳出距离它最近的那层循环,而continue也是跳过本次循环的后续语句,提前进入下一次循环是否继续的判断,如果该次判断为真,继续循环,否则跳出循环。
//while是先进行是否循环的判断,然后决定是否进入循环,所以当条件为假时一次也不做。
//do...while至少会执行一次,然后进行下次是否继续循环的判断,为真继续,否则退出。
//size_t<
unsignedint//32位系统
//ssize_t<
signedint//32位系统
unsignedlong//64位系统
signedlong//64位系统
8.//scanf在第一个输入不能成功匹配的情况下,不再接收后续输入;
在第一个输入成功匹配的情况下,会继续接收下一个输入,如果该次匹配失败,不会继续。
所以从不能匹配的那一次开始,不再继续。
//如果上面的输入有没匹配成功的情况,那么未匹配的输入将会放进输入缓冲区,
然后后续应该接收用户键盘输入时,发现输入缓冲区中有残留数据,并且能够匹配本次的输入,那么会直接从缓冲区中取出该数据赋给当前变量。
10.
//const表示只读read-only。
用它修饰的变量在C中从来都是只读类型的变量。
只有在C++中会优先将其作为常量,在无法视为常量(1、在作为sizeof的操作数时;
2、取该量的地址时)时,才作为只读变量。
constintb;
b=20;
//b是read-only的。
只读类型的变量在声明的同时未进行初始化,就再没有机会了。
intconst*p22;
//类型同下面的p2
constint*p2;
//p2指向的变量是read-only的,也就是说*p2是read-only的
int*constp3=&
//p3指针本身是只读的,也就是说它指向了某变量之后,不能再指向其他的变量
//constint*p6const;
//声明就是错误的
11.
typedefintelem;
//定义一个已有类型的别名
elemc;
intc;
intt[2][3];
typedefint(*ttf)[2][3];
//定义了数组指针类型的别名
typedefvoid(*tf)(int,int[*]);
//定义函数指针类型的别名tf
12.
//malloc和calloc都用于申请堆空间,只是calloc申请的堆空间会由编译器自动清0,而malloc的不会。
//malloc+memset==calloc
//可以使用valgrind+可执行文件名来检测是否出现了堆内存泄漏
//内存泄漏:
对堆内存,只申请,不释放,就会造成内存泄漏。
//free(pf.s1+1);
//error,free的参数必须是使用内存申请函数申请的堆空间的首地址
//内存使用中可能出现的问题:
//1、仍然使用释放的堆空间
//2、仅仅释放堆空间的一部分
//3、释放不是自己申请的堆空间
//4、对一块堆内存连续释放多次
//1、宏定义(格式:
#define宏名宏体)
//1)无参宏
//宏名:
必须是合法的标识符(必须以字母或下划线开头,开头不能是数字、空格等特殊符号
//宏体通常不会有;
号,而且不能有多余的特殊字符
//宏名和宏体之间可以有任意个空格或tab符号,替换时是直接变成一个空格
//不能用指针指向一个宏,因为在预处理之后,所有的宏定义替换后,就被删除了。
#defineM10//宏定义的作用范围:
从声明行开始,直到文件尾都可访问
//2)有参宏
//宏体部分,参数该加括号的地方都加括号,最外层还需要一层括号,为了避免出现优先级的运算问题
#defineA(3*2+1)
#defineB((A)+(A))
//#defineMAX(a,b)((a)>
(b)?
(a):
(b))
#defineSPLICE(a,b,c)a##b##c
SPLICE(1,2,3));
//123
SPLICE(1,,3));
//13
13.
//#include<
系统的头文件名>
//#include"
自己的头文件"
//<
>
和"
"
搜寻的路径不同。
直接到系统头文件目录下(/usr/include)查找需要包含的头文件,如果没找到,直接报错;
"
会先在当前目录下查找头文件,如果没找到,然后会去系统头文件所在目录查找。
#elif常量或常量表达式//当上面的条件不成立,该条件成立时,编译下面的代码
14.
//调试打印:
因为在代码发布release版本之前,会经常需要debug,不可避免使用大量的打印语句,用于调试代码。
产生的直接后果是会增大可执行文件的尺寸。
一般在代码调试通过后,都需要将这些打印语句去除掉,如果手工去除,不仅费时耗力,而且很容易遗漏。
此时用调试打印宏就是一个最好的选择,仅仅需要通过一句宏定义的打开和关闭就可以启用或关闭打印语句。
如果后续因为代码规模变大,只需简单的开启调试打印宏即可。
#defineNDEBUG
#ifdefNDEBUG
#defineDEBUG_PRINT(fmt,args...)
printf(fmt,##args)
//##可以删掉##之前未传递的参数的逗号
#else
#defineDEBUG_PRINT(fmt,args...)
#endif
1)p是指向函数的指针,并且此函数以字符型指针作为实际参数,函数返回的也是字符型指针。
char*(*p)(char*);
2)a是含有4个元素的数组,且每个元素都是指向函数的指针,而这些函数都是没有实际参数且无返回值的。
a的元素初始指向的函数名分别是foo1,foo2,foo3,foo4.
void(*a[4])(void)={foo1,foo2,foo3,foo4};
3)f是带有两个实际参数的函数:
一个参数是指向结构的指针,且此结构标签为t;
另一个参数是长整数类型的.f返回指向函数的指针,且指向的函数没有实际参数也无返回值。
void(*f(structt*,long))(void);
4)b是含有10个元素的数组,且每个元素都是指向函数的指针,而这些函数都有两个int型实际参数且返回标签为t的结构。
structt(*b[10])(int,int);
1.有符号和无符号数:
有符号数:
最高位作为符号位。
如果是非负数,最高位是0;
如果是负数,最高位是1
无符号数:
最高位不是符号位,是数值的一部分
2.//大小端转换0x12345678-->
0x78563412
structfoof{
intdata;
char*s;
chararr[0];
//零长度数组:
简称零长数组。
};
//零长数组:
//1、只能作为结构体的最后一个成员存在
//2、它不占据结构体的大小,仅仅标识结构体之后的那个地址
//3、它是为了扩展使用而生的一种语法
//结构体不支持判等操作
structfoof3=f1;
//是结构体唯一支持的运算操作
inta[5]={1,2,3,4};
//intb[5]=a;
//数组不允许整体赋值,=号左边类型是int[5],右边类型是int*
3.//数组是同类型元素的集合
//结构体是各种类型的元素的集合
//数组是顺序存取(根据数组首地址+偏移量来访问元素),结构体随机存取(根据成员名称访问成员)
//2、相同点
//在内存中是连续存放的
//大小都取决于所有成员(元素)的大小
//分量赋值,未赋值成员的值由编译器填0
//内存对齐是为了提高读取的效率
//linux+gcc平台下的内存对齐规则:
//1、每个成员必须在某个整数的整数倍的地址上,该整数是该成员自身大小和cpu字长这两者中小的那个
//2、结构体整体的大小必须是cpu字长的整数倍
//pragma用于执行系统预先定义好的行为
#pragmapack(4)
//()号中的值可以取1,2,4,8,将默认对齐值进行修改,超过cpu字长的将忽略
使用默认值
#pragmapack()//恢复默认对齐值
//可以使用__alignof或者__alignof__打印当前对齐系数
//printf("
当前对齐值(系数)=%d\n"
__alignof(structfoo2));
//如果需要结构体占据的空间最小:
可以按从小到大或者从大到小的顺序排列结构体的各个成员
structfoof={
10,'
c'
0.1,5,
};
f.a=%d\n"
f.a);
//.是成员运算符,或者叫“点运算符”
structfoo*pf=&
f;
pf->
c=%c\n"
pf->
c);
//->
是分量运算符,也叫“箭头运算符”
structfoof2={
.d=0.05,.a=100,
5.//联合体:
各种类型的成员共用一块内存空间的结构
//1、大小取决于其中最大成员的大小
//2、联合体变量的赋值,是根据第一个成员的类型来接收的
//3、在每一时刻,只能存在其中某一个成员,也就是说只能将该内存作为其中一个变量来使用
//4、联合体变量的值,是最后一次对该内存所赋的值
//联合体通常不会单独使用,而是和结构体嵌套混合使用的
6.//inline(内联)函数:
将普通的函数定义为inline函数,可以避免普通函数入栈出栈的开销,
它是将函数内的代码直接粘贴到调用处。
除此之外,它和普通函数无异。
//要成为inline函数必须具备以下几点:
//1、函数代码很短,通过不超过10行
//2、函数内不能出现循环、递归、switch...case
//3、需要在编译的时候开启优化选项-O
//否则加上inline修饰,编译器也会视而不见
//强制inline宏
#define__inline__attribute__((always_inline))
//inline放置的位置只要在函数的返回值的类型前面就可,如果函数又有static修饰,放在static的前后都可
//inline在不同编译器下放置的位置导致的结果是不同的,
某些编译器如果将inline放在函数的声明的前面是不被视为inline函数的,但大部分编译器对放在函数声明和函数定义的前面视为一样
//宏和inline函数的异同点:
//相同点:
都是将函数代码粘贴到函数调用处
//不同点:
1、编译器会检查inline函数的参数的类型,不检查宏的参数类型;
2、可以定义指向inline函数的指针,不能定义指向宏的指针
//内联函数:
函数调用时,直接把函数的代码拷到调用位置
//执行效力高,不发送传递和返回
//普通函数:
在内存中找到该函数,命名的区域然后发生传递执行和返回的过程
//程序中包含的结构有:
顺序、选择、循环
//递归:
自己直接或间接的调用自己
//完成递归必须满足的条件:
//1、必须有递归的出口条件(基线条件)
//2、每一次的递归面临的都是更小规模的同样
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 指针