ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C语言:指针补充

C语言:指针补充 指针降级降级是数据名的隐式降级二维数组 int arr[3][4]数组名会在绝大多数的表达式中隐式降级数组名不显示即int[3][4]它等价于int(*)[4];指向一行4个int的行指针于是有表达式真实类型含义arrint(*)[4]指向第0行一行4个intarr[0]int *指向第0行第0个int元素补充区别int arr[3][4]:栈上分配3行×4列连续内存等价于一维数组int (*)[4]只是一个8字节指针64位仅存行首地址不分配数组空间常量区开辟空间的方法一、常量区只读数据段.rodata存放内容常量区内存只读程序加载时由操作系统分配生命周期全称跟随进程不能手动malloc开辟只能通过语法把数据放入该区1.字符串字面量最常用用双引号包裹的字符串自动存常量区char *str hello world;禁止修改str[0] H会触发段错误常量区不可写而数组形式char arr[] hello;//数组内容负责到栈内存栈可以修改不在常量区2.const修饰全局变量/static全局const int g_val 100;const char g_buf[] abc;不加const的全局变量存.data读写数据段局部函数内const int a10:大多放栈不进常量区3.枚举常量枚举值编译区确定符号对应数值存放常量区4.浮点字面量、整形字面量编译嵌入指令/常量区double d 3,1414//3.1415浮点常量存入rodataint x 999999;//大数常量放入常量区5.static const静态常量静态全局/静态局部const放入常量区void test(){//static const存rodata;单纯const存栈static const int num 666//存常量区const int length 20//存栈}static const float pi 3.14;//存常量区写法内存区域可写test字面量/char *ptest.rodata 常量区只读char arr[]test栈 / 全局 data可修改malloc分配字符串堆 heap可修改(1)为什么错了因为p是一个一级指针这个指针开辟的空间在堆区p的作用域只在GetMemory这个函数内部在Test函数里调用GetMemory这个函数并没有改变str的地址传str进去是传值在GetMemory中是改变的是形参的值开辟空间是改变了p的值从NULL值改变成堆地址,函数返回后p的内存被销毁但实参str的NULL不变并没有改变实际的str因为p在调用结束之后是在另一块空间只在GetMemory这个函数内部才能使用Test里面的str指向的地址还是NULLstr是一个指针如果直接在GetMemory里传str,就是把NULL这个地址传给p,而p在GetMemory里又开辟了一个空间并没有修改str的内存里的值str的内存地址还是NULL不是合法地址NULL不能用来赋值存数据(2)为什么错了char p[]hello world;//这个p是在栈区调用完这个函数的时候就结束了在Test里面调用GetMemory这个函数没用把p的地址传进来p使用完就回收了这个字符串传不进来要使Test函数能访问这个p就要把p这个数组改成指针*phello world;就让p指向常量区函数返回的是p这个指针指针执行的地址是常量区一直存在不会被销毁生命周期是整个程序运行期间所以printf可以通过这个地址找到字符串func函数里的ptr是局部变量它的地址调用完就销毁了而main里的ptr是另一个地址空间它们不指向同一块内存在main里面调用func函数ptr的地址还是原来的取i的地址结构体大小的计算1.结构体的总大小一定是结构体内最大内置类型的倍数内置类型只能为char、int、long4字节、long long8字节、double、short、float、指针这几种不能是struct student这种类型他只能是基本类型嵌入式内存芯片的存储大小很小一般是几M要节省空间就要改变结构体内存排布先后顺序实现缩小空间2.结构体的每个元素的位置一定是这个元素类型大小的倍数联合体联合体里的多个成员共享同一块内存这块内存大小是最大的一个成员类型的内存union D{int a;char ch;};D d;d.a 0x12345678;if(d.ch 0x78)//第一个字节是共享的{printf(小端\n);}elseprintf(大端\n);return 0;}sizeofbuf是buf的内存大小而不是存的实际字符串大小代表整个数组的大小不代表首地址字符串本质是一个地址值在常量区buf[100]是在栈区而p是在常量区memset(buf,0,sizeof(buf));//将buf这个数组的元素都置为零typedef给已有的数据类型起一个别名成环链表的书写只能用int作为返回值用void会判断错误int作为返回值1代表成环0代表不成环Int IsLoop(Node *head){if(head NULL || head-next NULL){printf(不成环\n);return 0;}Node *fast head-next;Node *slow head-next;for(fast,slow;fast-next!NULL,fast-next-next ! NULL;slow slow-next,fast fast-next-next){if(fast slow){printf(成环\n);return 1;}}printf(不成环\n);return 0;}函数声明是( *f());//f与*没括在一起就不是指针指针变量括在一起(*f)();//是函数指针void * (*f)(void*);//是一个指向void *类型参数的指针返回烈性为void *;memcpy与strcpy的区别memcpy的语法函数原型void *memcpy(void *dest, const void *src, size_t n);strcpy的语法函数原型void *strcpy(void *dest, const void *src);1. 拷贝结束的依据最核心的区别memcpy拷贝时完全不看内容只看长度。它把第三个参数指定的字节数严格搬运完就立刻停止。哪怕数据中间包含了无数个0x00它也照搬不误。strcpy只认字符串结束符\0即二进制里的0x00。它从源地址开始读一旦遇到0x00就认定“字符串结束了”立刻停止拷贝。2. 参数数量不同memcpy3 个参数。memcpy(目标地址, 源地址, 拷贝的字节数)strcpy2 个参数。strcpy(目标地址, 源地址)。因为它不需要知道长度它靠自己去找0x00来结束。注意为什么用strcpy处理Modbus是一个致命的杀手因为ModbusRTU的底层是一个纯二进制一个合法的Modbus报文经常出现0x00这个字节比如我控制灯灭的指令01 06 00 00 00 00 ...里面全是0x00如果用strcpy单片机刚把第 3 个字节0x00读进去strcpy立刻以为“到结尾了”直接停止搬运。剩下的 5 个字节全部丢弃随后你的程序去解析这包缺失了一半的报文就会访问数组越界瞬间触发你之前遇到的HardFault死机。如果用memcpy不管中间有多少个0x00它都会老老实实把 8 个字节完整搬完然后我可以去完整校验 CRC、解析功能码memset内存设置memory set将内存区域内的每一个字节全部填充我指定的一个数值。例如我写的memset(xUSART1.receiveData, 0, sizeof(xUSART1.receiveData));//意思是从我的xUSART1.receiveData这个数组的首地址开始设置后面的xUSART1.receiveData大小的字节元素都置为0void *memset(void *s, int c, size_t n);void *s是什么这是一个通用无类型指针代表你要开始填充的“内存目标首地址”。int c是什么你要填充的数值。size_t n是什么要填充的总字节数。size_t在 STM32 编译器中通常是一个无符号 32 位整数。
返回列表