C语言-指针

指针

动态内存分配

要实现动态内存分配,需要使用标准C库提供的库函数,我们所说的动态内存分配,其实就是在堆区申请内存(此时的内存回收需要程序员自身来维护)

常用函数

malloc

头文件:#include <stdlib.h>

 void* malloc(size_t size)

功能:分配指定字节数的内存到堆区,返回指向内存块首地址的指针,内存内容未初始化(随机值)

size:要分配的内存大小(字节),这里的size_t是数据类型unsigned long int 的别名

返回值:

成功:返回内存指针(申请到的内存的首地址)

失败:失败返回NULL

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 ​
 int main(int argc,char *argv[])
 {
     int *p = malloc(sizeof(int));      //这里尽量不使用常量
     
     if(p == NULL)  //等价于 !p 
     {
         perror("内存申请失败!");
         return -1; 
      } 
      memset(p,0,sizeof(int));        //对指定空间进行赋值0,支持批量赋值
      
      *p = 100;
      
      printf("%d\n",&p);
      
      free(p);
      
      p = NULL;
      
      return 0; 
 } 

注:

分配内存后需要手动初始化内存,推荐 memset 或 calloc

内存空间连续,不可越界访问

calloc

头文件:#include <stdlib.h>

 void* calloc(size_t nitems, size_t size);

动态分配内存,并初始化为0

参数:

nitems :元素个数

size :每个元素的字节大小

返回值:

成功 :返回内存指针(申请到的内存的首地址)

失败 :失败返回 NULL

 
#include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 ​
 #define LEN 5
 ​
 int main(int argc,char *argv[])
 {
     int *arr = calloc(LEN, sizeof(int));  //等价于 int *arr = malloc(LEN*sizeof(int)); memset(arr,0,LEN*sizeof(int)) 
     
     if(arr == NULL)  //等价于 !arr
     {
         perror("内存申请失败");
         return -1;
     }
     
     for(int i = 0;i < LEN ; i++)
     {
         if(i % 2 == 0) continue;
         arr[i] = (i+1)*10;
      } 
      
     int *p = arr;
     for( ; p < arr +LEN ; p++)
     {
         printf("%-6d",*p);
     }
      printf("\n");
     
     free(arr);
     //置空,防止产生空悬指针 
     arr = p = NULL;
          
     return 0;
 }

使用场景:为数组分配内存时更安全高效。

realloc

头文件: #include <stdlib.h>

 void* realloc(void* ptr, size_t size);

功能:调整已分配内存块的大小,可能迁移数据到新地址。扩容后,空余位置是随机值,需要手动清零。

原理:

当前内存空间后面的剩余空间足够扩容,就在原空间基础上进行扩容,返回原地址;

当前内存空间后面的剩余空间不够扩容,会重新开辟一块空间,将原空间数据按顺序拷贝后,销毁原空间,返回新地址。扩容后的内存空间中的数据是随机的需要手动清零。

参数:

ptr :原内存指针(需要扩容的内存空间的指针,这个ptr的来源( malloc/calloc/realloc ))

size :指定重新分配内存的大小(字节)

返回值:

成功 :返回内存指针(申请到的内存的首地址)

失败 :失败返回 NULL

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #define LEN 5
 #define NEW_LEN 8
 #define NEW_LEN_MIN 3
 int main(int argc, char *argv[])
 {
     // 创建一个指针,指向堆内存分配的内存空间
     int *arr = (int*)calloc(LEN, sizeof(int));
     // 指针的非空检验,防止野指针
     if (!arr){
         perror("内存申请失败!");
         return -1;
     }
     // 使用for循环赋值
     for (int i = 0; i < LEN; i++)
     {
         arr[i] = (i+1)*10;
     }
     // 遍历数组
     int *p = arr;
     for (; p < arr + LEN; p++) printf("%-6d", *p); printf("\n");
     // 扩容数组
     int *temp = (int*)realloc(arr, NEW_LEN * sizeof(int)); // 扩容出来的空间,默认是随机值
     // 非空校验,防止野指针
     if(!temp){
         perror("内存申请失败!");
         free(arr); // 扩容失败,回收原空间
         return -1;
     }
     // 更新数组指针
     arr = temp;
     // 对扩容部分清零
     memset(arr+LEN,0,(NEW_LEN - LEN) * sizeof(int));
     // 修改数组元素
     arr[7] = 666;
     // 遍历数组
     for (int i = 0; i < NEW_LEN; i++) printf("%-6d", *(arr+i));
     printf("\n");
     // 缩容数组
     temp = (int*)realloc(arr, 0); // 此时,会回收掉内存,等价于
     free(arr);
     // 非空校验,防止野指针
     if(!temp){
         perror("内存申请失败!");
         return -1;
     }
     // 更新数组指针
     arr = temp;
     // 遍历数组
     for (int i = 0; i < NEW_LEN; i++) printf("%-6d", *(arr+i));
     printf("\n");
     // 释放内存
     free(arr);
     arr = NULL;
     p = NULL;
     return 0;
 }

注:

分配后需要手动初始化内存,推荐 memset

必须用临时变量接收返回值,避免直接覆盖原指针导致内存泄漏。

若 size 为0,等效于 free(ptr)

free

头文件: #include <stdlib.h>

函数原型: void free(void* ptr);

功能:释放动态分配的内存。

注意事项:

只能释放一次,重复释放会导致程序崩溃。

释放后应将指针置为 NULL ,避免野指针。

栈内存由系统自动释放,无需手动 free 。

内存操作

我们对于内存操作需要依赖于 string.h 头文件中相关的库函数。

常用函数

内存填充

头文件: #include <string.h>

函数原型:

 void* memset(void* s, int c, size_t n);

函数功能:将内存块 s 的前 n 个字节填充为 c ,一般用于初始化或者清零操作。

参数说明:

s :目标内存首地址

c :填充值(以 unsigned char 形式处理(0~255))

n :填充字节数

返回值:

成功:返回 s 的指针

失败:返回NULL

注意事项:

常用于动态初始化, c 通常设置为0(清零)

按字节填充,非整型初始化需要谨慎(如填充 int 数组时,0是安全的)

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 ​
 #define LEN 4
 ​
 int main(int argc, char *argv[])
 {
     // 在堆内存申请4个int的连续空间
     int *p = (int*)malloc(LEN * sizeof(int));
     // 非空校验
     if (!p){
         perror("内存申请失败!");
         return -1;
     }
     
     // 初始化堆内存空间,填充0,我们也可以称作 清零操作
     memset(p, 0, LEN * sizeof(int));
     
     // 测试输出
     printf("%d,%d\n", p[1], *(p+1));
     
     // 释放内存
     free(p); // 等价于 realloc(p,0);
     
     // 对指针置空,防止空悬指针
     p = NULL;
     
     return 0;
 }
内存拷贝

头文件: #include <string.h>

函数原型:

源与目标内存无重叠时使用

 void* memcpy(void* dest, const void* src, size_t n);

安全处理内存重叠

 void* memmove(void* dest, const void* src, size_t n);

函数功能:将 src 的前 n 个字节拷贝到 dest

参数说明:

dest :目标内存首地址,支持指针偏移

src :源内存首地址,支持指针偏移

size_t n :拷贝的字节数

返回值:

成功:返回 dest 的首地址

失败:返回NULL

注意事项:

memmove 能正确处理内存重叠,推荐优先使用

确保目标内存足够大,避免溢出。

 #include <stdio.h>
 #include <string.h>
 ​
 int main(int argc, char *argv[])
 {
     // 准备两个数组,用来存储源和目标
     int src[4] = {11,22,33,44}; // 拷贝 22,33 从src+1开始,拷贝2个sizeof(int)
     int dest[6] = {111,222,333,444,555,666}; // 目的地 dest+1,拷贝后:111,22,33,444,555,666
     register int i = 0;
     printf("拷贝前:");
     for (i = 0; i < 6; i++) printf("%-6d", dest[i]);
     // 进行拷贝
     // memcpy(dest+1,src+1,2 * sizeof(int));
     memmove(dest+1, src+1, 2 * sizeof(int));
     printf("\n拷贝后:");
     for (i = 0; i < 6; i++) printf("%-6d", dest[i]); printf("\n");
     return 0;
 }
内存比较

头文件: #include <string.h>

函数原型:

 int memcmp(const void* s1, const void* s2, size_t n);

函数功能:比较 s1 和 s2 的前 n 个字节

返回值:

0 :内存内容相同

>0 : s1 中第一个不同字节大于 s2

<0 : s1 中第一个不同字节小于 s2

注:比较按字节进行,非字符串需确保长度一致(总字节数一致)。

 
#include <stdio.h>
 #include <string.h>
 ​
 int main(int argc, char *argv[])
 {
     // 准备两个测试用的数组
     int src[] = {111,22,33,44};
     int dest[] = {111,22,10,44,555,666};
     
     // 进行比较
     int result = memcmp(src, dest, 4 * sizeof(int));
     printf("%d与%d的比较结果是%d\n",*src, *dest, result);
     
     return 0;
 }
内存查询

头文件: #include <string.h>

函数原型:

正向查找,C语言标准库函数

 void* memchr(const void* s, int c, size_t n);

逆向查找,这个不是C语言标准库函数,属于GNU扩展

 void* memrchr(const void* s, int c, size_t n);

函数功能:在 s 的前 n 个字节中查找字符 c

返回值:

成功:返回找到内容对应的地址

失败:返回NULL

注意事项:

memrchr 是GNU扩展函数,需手动声明(只要不是C语言标准提供,编译的时候都需要手动声明或链接)

查找单位为字节值,非整型数据需要注意内存布局

 #include <stdio.h>
 #include <string.h>
 // #include <stddef.h>
 ​
 // 逆向查找函数 memrchr是GNU扩展函数,这个函数需要额外声明
 extern void* memrchr(const void*, int, size_t);
 ​
 //void* memrchr(const void* s, int c, size_t n) {
 // const unsigned char* ptr = (const unsigned char*)s + n;
 // while (n--) {
 // if (*--ptr == (unsigned char)c) {
 // return (void*)ptr;
 //      }
 //  }
 // return NULL;
 //}
 ​
 int main(int argc, char *argv[])
 {
     // 准备一个测试数组
     char str[] = {'A','B','C','B'};
     // 查找字符 B
     char* first = (char*)memchr(str, 'B', sizeof(str)); // 返回 低地址对应的字符地址
     char* last = (char*)memrchr(str, 'B', sizeof(str)); // GNU扩展函数,返回 高地址对应的字符地址
     
     printf("first=%p,last=%p\n", first, last);//first=0x7fff65a47875,last=0x7fff65a47877
     printf("第1个B的位置:%ld\n", first - str);// 1
     printf("最后1个B的位置:%ld\n", last - str);// 3
     
     return 0;
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值