C语言指针 动态内存分配

C语言

指针与动态内存分配



指针进阶(一)

1.一个数组的数组名就是它的首元素地址
int arr[5] = {0};         //声明一个有5个整型元素的数组
  printf("%p\n", arr);      //打印这个数组首元素的地址


输出一个16进制数代表地址

printf("%p\n", &arr[0]);     //用一种更加直接的方式去打印首元素的地址,别忘了取地址符

在这里插入图片描述
可以发现两个地址是相同的


2.常量字符串的概念

 char* p = "abcd";
  *p = 'w';     //wrong writing

课上的一个典型例子,我们来逐行分析

第一行的代码是对的,但是为什么左值对象是一个字符型的指针,赋值符号的右边却是字符串???
为了解决这个问题,我们先看下图所示的代码

char arr[5] = { 'a','b','c','d','\0' };
char brr[5] = "abcd";
printf("%c\n", arr[4] + 65);    // 输出字符A
printf("%c\n", brr[4] + 65);    // 输出字符A

arr,brr都是char型数组,他们的第5个元素都是’\0’
(注意第5个元素的索引值为4)

如果通过在屏幕输出的方式来比对数组arr,brr 就会发现它们每个元素的值都是相同的
因而我们就可以把字符串想象成1个字符型数组,但是字符串总是以’\0’结尾,所以要注意字符数组的长度,
"abcd"就相当于1个5元字符型数组,第5个元素就是’\0’


回到原来代码,我们此时就明白指针指向了相当于字符型数组的字符串

!!!那么第2行代码为啥是错的?

 *p = 'w';        // Error

很显然 p指针是 char* 类型的,它存储了字符型数组首元素的地址,也就是字符串中第一个字符的地址;
那么我们对 p指针进行解引用,间接访问它所指向的对象
通过解引用找到target,进而改变target的值,是我们非常熟悉利用指针的方式,在这里不行,原因在于target的值不能改变,是1个常量

由此我们引出字符串常量的概念,字符串不完全等价于字符常量,因为字符数组中的每个字符都不可改变,字符串是一个常量
换言之,字符串就相当于一个无法改变的字符型数组




指针进阶(二)

1.数组指针
char (*p) [10];     
     // p是1个指针变量,它指向的数组有10个元素,每个元素都是char型
p = &arr;       
     // p指向1个数组,故给p赋值时应利用数组的地址
   

2.数组的地址和数组首元素的地址
int arr[10];
printf("%p\n", arr);       // 00000052099ffde0
printf("%p\n", &arr);      // 00000052099ffde0
                               
                                 // 打印数组的地址与数组首元素的地址,结果相同
                                 
printf("%p\n", arr + 1);     //00000052099ffde4       增加4B
printf("%p\n", &arr + 1);  //00000052099ffe08        增加40B (10进制)
                                //  +1 引起的效果不同

对于保存数组首元素的地址的指针,它右移1次,就会指向数组的下一个元素
而保存数组地址的指针,它右移1次,就会跳过整个数组

3. 2维数组
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6}, {3,4,5,6,7} };     
  // 想象成1个3行5列的矩阵

如果把2维数组想成1维的,每个元素是一个行向量
1 2 3 4 5 第0个元素
2 3 4 5 6 第1元
3 4 5 6 7 第2元

利用数组指针打印2维数组

void print(int(*p)[5], int x, int y) {
    for (int i = 0; i < x; i++) {
        for (int j = 0; j < y; j++) {
            printf("%d ",*(*(p + i) + j));
        }
        printf("\n");
    }
4.小结
int arr[5];             // 含5个元素的数组  
int *pa[10] ;          //  指针数组,每个元素的类型是int *
int (*pa2) [10] ;     //   指针,指向1个10元整型数组
int(*pa3 [10]) [5];
                      //     pa3首先是1个10元数组,该数组每个元素是指向5元整型数组的指针
                      //     pa3 是存放数组指针的数组



指针进阶(三)

1.数组参数,指针参数

如果传入一个数组
可以用数组来接收(最好不指定数组的大小)

void test(int arr[])
{}

int arr[5];               //  在main函数中

也可以用指针来接收数组首元素的地址

void test(int *arr)
{}

int arr[5];     // in main function

2.接收二维数组

传参时最多仅可省略行数
最好用相同规模的虚参来接收


二维数组的数组名是第一行的地址
因此要用数组指针来接收二维数组




指针进阶(四)

(* (void(*)() ) 0) ();           // 调用函数

将0强制类型转换成一个函数指针
再对地址0解引用(调用地址为0的函数)

typedef void(* pfun) (int);  // 给函数指针这一类型起别名,但要注意名字的位置
函数指针数组
int (*p[4]) (int, int);   // 函数指针数组

先和中括号结合,表示这是一个数组,数组中的元素均为函数指针




指针进阶(五)

利用函数指针数组实现简易计算器
//
// Created by Lanbo on 2021/4/24.
//

#include <stdio.h>
int Add(int x, int y){
  return x+y;
}

int Mul(int x, int y){
  return x*y;
}

int Div(int x, int y){
  if(y!=0)
    return x/y;
  else
    printf("illegal input");
}

int Sub(int x, int y){
  return x-y;
}
// the above functions are aimed to achieve Binary Operation


int main(){
  int input;
  do {
    printf("Please select a mode\n");
    scanf("%d",&input);
    
    if(input>=1&&input <=4)
    {
    int x,y;
    printf("please input 2 numbers\n");
    scanf("%d%d",&x,&y);
    
    int (*pa[4])(int, int) ={0, Add, Sub, Mul, Div};
    
    int res= pa[input](x,y);
    
    printf("res = %d\n",res);
    }
    
    else if(input == 0){
      printf("Exit\n");
    }
    
    else
      printf("Illegal input\n");

  }while(input);
  return 0;
}



指针进阶(五)

万能指针 void*(无类型指针)

1.利用void*去接收任意类型元素的地址

int a = 10;
void* p = &a;

那么我们用无类型指针做什么?
调用快排函数 qsort (from <stdlib.h>)时我们需要传递一个函数指针

int compare_int(const void* e1, const void* e2){
	return *(int*)e1 - *(int*)e2;
//  先强制类型转换
}

2.qsort
实现快速排序的函数
需要四个参数(数组首元素地址,数组元素个数,数组元素所占字节数,比较函数的地址)

arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), cmp
// cmp is a function
第四个参数由使用者自己实现

// 
// Created by Lanbo on 2021/4/24.
//
#include <stdio.h>
#include <stdlib.h>

int cmp_int(void* e1, void* e2){
  return *(int*)e1-*(int*)e2;
}              /* This function is aimed to return an integer 
indicating size relationship of 2 elements */

int main(){
  int arr[5] = {5,4,3,2,1};
  int sz = sizeof(arr) / sizeof(arr[0]);    // length of the target

  qsort(&arr[1], sz-1, sizeof(arr[0]), cmp_int);    // call function Qsort
  // &arr[1]: where is the target
  return 0;
}

理论部分finished




practice

int arr[] = {1,2,3,4};
   printf("%d\n",sizeof(arr));

把数组放入sizeof运算符,计算数组大小
本题为4*4=16

数组名是首元素地址,但有2个例外
1)sizeof(数组名) 数组名表示整个数组
2)&arr arr表示整个数组





第二部分 动态内存分配

局部变量在栈区开辟内存空间
全局变量在静态区开辟内存空间

堆区——动态内存分配

1.为什么需要动态内存分配

首先可以解决数组长度不能是变量的问题!!!

调用函数malloc
prototype如下

void* malloc( n * sizeof(type) );

使用malloc函数,需要引头文件<stdlib.h>
并对返回一个指针的malloc函数进行强制类型转换

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,可以使用指针来进行动态内存分配动态内存分配是指程序运行时根据需要动态地分配内存空间,而不是在编译时静态地分配固定大小的内存。 要进行动态内存分配,可以使用C语言中的两个关键字:`malloc`和`free`。`malloc`用于申请一块指定大小的内存空间,而`free`用于释放已经申请的内存空间。 下面是一个简单的示例,演示了如何使用指针进行动态内存分配: ```c #include <stdio.h> #include <stdlib.h> int main() { int* ptr; // 定义一个指向整数类型的指针 int size; printf("请输入要分配的整数个数: "); scanf("%d", &size); // 使用malloc动态分配内存 ptr = (int*)malloc(size * sizeof(int)); if (ptr == NULL) { printf("动态内存分配失败\n"); return 1; } // 为分配的内存空间赋值 for (int i = 0; i < size; i++) { ptr[i] = i; } // 打印分配的内存空间中的值 for (int i = 0; i < size; i++) { printf("%d ", ptr[i]); } // 使用完后记得释放内存 free(ptr); return 0; } ``` 在上面的示例中,首先定义了一个指向整数类型的指针`ptr`。然后根据用户输入的个数,使用`malloc`函数动态分配了一块内存空间,并将返回的指针赋值给`ptr`。接下来,使用循环将分配的内存空间中的值进行初始化,并打印出来。最后,使用`free`函数释放了动态分配的内存空间。 需要注意的是,使用完动态分配的内存空间后,必须调用`free`函数来释放该空间,以免造成内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值