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函数进行强制类型转换