内存和地址
CPU(中央处理器)在处理数据的时候,中央的数据在内存中读取,处理后的数据也会放回内存中。
内存会被划分为一个个单元格,每个内存单元的大小取一个字节。每个字节可以放8个比特位,每个内存单元也有一个编号,有了这个编号,CPU可以迅速找到一个内存空间。C语言中把这个编号也叫地址
C语言中给地址起了一个名字叫指针
指针变量
1.和地址
在C语言中创建变量就是向内存申请空间我们已经知道啦,比如,创建整型变量就是向内存申请4个字节,每一个字节都有地址,
2.和解引用操作符
我们通过取地址操作符拿到是一个地址的数值,这个数值有时候需要存储下来,方便后期使用,所以我们这个放在 指针变量 中。
#include<stdio.h> int main() { int a = 0; int* pa = &a; return 0; }
这里出现的Int *,*就是代表这个是指针变量。
我们拿到了指针就可以找到指针指向的对象,需要用到一个操作符叫解引用操作符----*
#include<stdio.h> int main() { int a = 0; int* pa = &a; *pa = 0; printf("%d", a); return 0; }
这里就改变了a的值。
指针变量的大小
32位平台下地址就是32个bit,指针变量的大小的4个字节
6位平4台下地址就是64个bit,指针变量的大小的8个字节
并且指针变量的大小与类型是无关的,管他什么整型,浮点型,在相同的平台下都是一样的。
指针变量类型的意义
指针的类型决定了对指针解引用的时候有多大的权限,比如char类型只能1个字节,而int类型可以4个字节。
void*指针
可以理解为无具体类型的指针,但是不可以进行指针的+-运算和解引用运算。
const修饰指针
变量是可以被修改的,如果我们需要不可以被修改,那我们需要用到const,
对于 int*pa=&a;
const 放在*左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以改、
const放在*右边,修饰是指针变量本身
const char *ptr="hello ", 本质就是把hello的首元素存到ptr中
指针运算
1.指针+-整数
2.指针-指针
3.指针的关系运算
1.指针+-整数:数组在内存中是连续存放, 所以我们只要知道第一个元素的地址,就可以找到所有元素。
2.指针-指针:
#include<stdio.h> int strlen(char* s) { char* p = s; while (*p != '\0') { p++; } return p - s; } int main() { printf("%d\n", strlen("abc")); return 0; }
野指针
野指针就是指针指向的位置是不可知的。
原因:没有初始化,指针越界访问,指针指向的空间释放(就是可能创建的指针变量被销毁了)
数组名的理解
1.数组名就是数组首元素的地址,如下图
注意:
sizeof(数组名):计算的是整个数组的大小,单位是字节
&数组名:表示的是取出的是整个数组的地址
使用指针访问数组
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { int arr[10]; int sz = sizeof(arr) / sizeof(arr[0]); int* p = arr;//首元素 int i = 0; for (i = 0; i < sz; i++) { scanf("%d", p+i); } for (i = 0; i < sz; i++) { printf("%d ", *(p + i)); } return 0; }
一维数组传参的本质
本质来说,一维数组传参的本质传的是数组的首元素的地址
并且一维数组传参形参部分可以写成数组,也可以写成指针
#include<stdio.h> void test(int arr[]) { printf("%d\n", sizeof(arr)); } void test(int* arr) { printf("%d\n", sizeof(arr)); } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; test(arr); return 0; }
二级指针
二级指针就是存放一级指针的地址
因为你的一级指针存放地址的时候,也需要向内存申请一块空间,它自己本身是需要地址
#include <stdio.h> int main() { int a = 0; int* p = &a; int** pa = &p; return 0; }
指针数组
整型数组:存放整型的数组
字符数组:存放字符的数组
指针数组:存放指针的数组,也就是说,这个数组里面的每一个元素都是指针
指针数组模拟二维数组
int main() { int arr1[] = { 1,2,3,4,5 }; int arr2 [] = {2,3,4,5,6}; int arr3[] = { 3,4,5,6,7 }; //数组名是元素首元素的地址 int* p[3] = { arr1,arr2,arr3 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ", p[i][j]); } printf("\n"); } return 0; }
数组指针变量
定义
整型指针变量: int*pint,存放的是整型变量的地址,指向的是整型数据的指针
浮点指针变量:float*pint,存放的是浮点型变量的地址,指向的是浮点型数据的指针
数组指针变量:int(*p)[ ] 存放的是数组的地址,能够指向数组的指针变量
数组指针变量初始化
#include<stdio.h> int main() { int arr[10] = { 0 }; int(*p)[10] = &arr; return 0; }
&arr与*p的类型是完全一致的
二维数组传参的本质
二维数组的首元素就是第一行,是一个一维数组
在本质上传的也是地址,传递的就是这个一维数组的地址
函数指针变量
函数是有地址的,函数名就是函数的地址,也可以用 &函数名 来表示
int (*p) (int x, int y)........p指向函数的参数类型和个数的交代
. .
. .
. .
. 函数指针变量名字
.
p指向函数返回的类型
函数指针变量的使用
#include<stdio.h> int Add(int x, int y) { return x + y; } int main() { int (*p)(int, int) = Add; printf("%d\n", (*p)(2, 3)); printf("%d\n", p(2, 3)); return 0; }
typeof关键字
可以将复杂的类型简单化
比如,你觉得unsigned int 写起来不方便
可以改成 typeof unsigned int uint;
之后你使用就可以直接使用 unit
指针类型也可以改名字
typeof int * ptr-t;
回调函数
回调函数就是通过函数指针调用的函数
如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数,被调用的函数就是回调函数
回调前
#include<stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } int main() { int x, y; int input = 1; int ret = 0; do { printf("******************************\n"); printf("**** 1. add 2. sub ****\n"); printf("**** 3. mul 4. div ****\n"); printf("**** 0. exit ****\n"); printf("******************************\n"); scanf("%d", &input); switch (input) { case 1: printf("输入操作数:"); scanf("%d%d", &x, &y); ret = add(x, y); printf("ret=%d\n", ret); break; case 2: printf("输入操作数:"); scanf("%d%d", &x, &y); ret = sub(x, y); printf("ret=%d\n", ret); break; case 3: printf("输入操作数:"); scanf("%d%d", &x, &y); ret = mul(x, y); printf("ret=%d\n", ret); break; case 4: printf("输入操作数:"); scanf("%d%d", &x, &y); ret = div(x, y); printf("ret=%d\n", ret); break; case 0: break; } } while (input); return 0; }
回调后
#include<stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return a / b; } void calc(int(*pf)(int, int)) { int ret = 0; int x, y; pintf("输入操作数:"); scanf("%d%d", &x, &y); ret = pf(x, y); printf("ret=%d\n", ret); } int main() { int x, y; int input = 1; int ret = 0; do { printf("******************************\n"); printf("**** 1. add 2. sub ****\n"); printf("**** 3. mul 4. div ****\n"); printf("**** 0. exit ****\n"); printf("******************************\n"); scanf("%d", &input); switch (input) { case 1: calc(add); break; case 2: calc(sub); break; case 3: calc(mul); break; case 4: calc(div); break; case 0: break; } } while (input); return 0; }
qsort函数使用举例
函数实现排序
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> int int_cmp(const void* p1, const void* p2) { return (*(int*)p1 - *(int*)p2); } int main() { int arr[] = { 1,3,5,6,8,2,4 }; int i = 0; qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }
非常感谢您的阅读,多多关注呀,后面我也会尽心尽力为大家带来优质好文的