C/C 语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C 语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。
定义并使用一维数组: 该数组是最通用的数组,也是最基本的.
代码语言:javascript复制#include <stdio.h>
void PrintArray(int *Array, int len)
{
for (int x = 0; x < 10; x )
{
printf("%d n", Array[x]);
//printf("%d n", *(Array x));
}
}
int main(int argc, char* argv[])
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
PrintArray(Array, 10);
system("pause");
return 0;
}
定义并使用二维数组: 二维数组是相对于一维数组而言的,在内存中其都是一段线性的连续的存储空间.
代码语言:javascript复制#include <stdio.h>
int main(int argc, char *argv[])
{
int array[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
int *Pointer1 = &array[0][0];
printf("array[0][0]基址:%x n", Pointer1);
printf("array[0][0]数据:%dn", *(Pointer1));
printf("arrya[0][1]数据:%dn", *(Pointer1 1));
int *Pointer2 = &array[1][2];
printf("array[1][2]基址: %x n", Pointer2);
printf("数组元素个数:%dn", sizeof(array) / sizeof(int));
return 0;
}
使用指针遍历数组: 使用指针定位数组,并输出数组元素.
代码语言:javascript复制#include <stdio.h>
int main(int argc, char* argv[])
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr;
for (ptr = &Array; ptr < (Array 10); ptr )
{
printf("%d ", *ptr);
}
system("pause");
return 0;
}
动态数组的定义: 我们可以自己手动分配空间并定义一个动态数组.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
int ** p = (int **)malloc(sizeof(int *)* 3);
p[0] = (int *)malloc(sizeof(int)* 3);
p[1] = (int *)malloc(sizeof(int)* 3);
p[2] = (int *)malloc(sizeof(int)* 3);
p[0][0] = 1;
p[0][1] = 2;
p[0][2] = 3;
p[1][0] = 4;
for (int x = 0; x < 3; x )
printf("%d n", p[0][x]);
system("pause");
return 0;
}
指针与数组之间的运算: 通过将两个指针做减法,我们就可以得到两个元素之间相差多少字节.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr = Array;
ptr = &Array[10];
int ptr_len = ptr - &Array[0];
int arr_len = ptr - Array;
printf("当前Array数组总长度: %d %dn", ptr_len, arr_len);
int *ptr1 = &Array[0];
int *ptr2 = &Array[7];
int ptr3_len = ptr2 - ptr1;
printf("Array[7] - Array[0] 相隔 %d 个元素 %d 个字节 n", ptr3_len,ptr3_len * 4);
system("pause");
return 0;
}
定义一维指针数组: 所为指针数组就是说该数组是用来存放其他的变量地址的,故称为指针数组.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void PrintInt()
{
int x = 10, y = 20, z = 30;
int *Array[] = { &x, &y, &z }; // 定义指针数组(存放变量地址)
*Array[0] = 100; // 给x重新赋值
*Array[2] = 300; // 给z重新赋值
for (int x = 0; x < 3; x )
printf("地址: %x --> 数值:%d n", Array[x], *(Array[x]));
}
void PrintArray()
{
int x[] = { 1, 2, 3, 4, 5 };
int y[] = { 6, 7, 8, 9, 10 };
int *Array[] = { &x, &y };
printf("单独取地址: %x --> 取值: %d n", (Array[0] 1),*(Array[0] 1));
for (int x = 0; x < 2; x )
{
for (int y = 0; y < 5; y )
{
printf("循环取地址: %x --> 数值: %d n", Array[x] y, *(Array[x] y));
}
}
}
int main(int argc, char* argv[])
{
PrintArray();
system("pause");
return 0;
}
定义二维指针数组: 同理我们可以通过指针遍历到二维数组中的数据,三维四维以此类推.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
void PrintArray(int(*Array)[3], int row, int col)
{
for (int x = 0; x < row; x)
{
for (int y = 0; y < col; y)
{
// Array[x][y] 等价于 => *(*(Array x) y)
printf("地址: %x --> 遍历数组: %d n", (*(Array x) y), Array[x][y]);
}
}
}
int main(int argc, char* argv[])
{
int Array[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
int(*pArray) = Array;
printf("寻找 Array[0][0] --> %d n", (*pArray 0));
printf("寻找 Array[0][1] --> %d n", (*pArray 1));
printf("寻找 Array[1][2] --> %d n", (*pArray 1) 2);
printf("寻找 Array[1][3] --> %d n", (*pArray 1) 3);
PrintArray(Array, 2, 3);
system("pause");
return 0;
}
数组实现逆序排列: 所谓数组逆序就是讲一个正向数组反向排列,并输出排序后的结果.
代码语言:javascript复制#include <stdio.h>
void Swap_Array(int Array[], int Number)
{
int x = 0;
int y = Number - 1;
while (x < y)
{
int tmp;
tmp = Array[x];
Array[x] = Array[y];
Array[y] = tmp;
x ; y--;
}
}
int main(int argc, char* argv[])
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Swap_Array(Array, 10);
for (int x = 0; x < 10; x )
{
printf("%d ", Array[x]);
}
system("pause");
return 0;
}
用数组冒泡排序: 冒泡排序是经典的算法,也是学习数组必学知识点,这里总结一份冒泡排序.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void bubble(int *arr, int len)
{
int flag = 1;
for (int i = 0; i < len - 1; i )
{
for (int j = 0; j < len - i - 1; j )
{
if (arr[j] > arr[j 1])
{
flag = 0;
int temp = arr[j];
arr[j] = arr[j 1];
arr[j 1] = temp;
}
}
if (flag)
return;
flag = 1;
}
}
int main(int argc, char* argv[])
{
int Array[] = {1,2,3,4,5,6,7,8,9,10};
int len = sizeof(Array) / sizeof(int);
bubble(Array, len);
for (int x = 0; x < len; x )
printf("%d n", Array[x]);
system("pause");
return 0;
}
定义常量指针: 一般为了高效传递数据,我们使用地址传递,这样就无须重复拷贝了,通常会加上常指针防止数据被误修改.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
struct Student
{
int num;
char *name;
};
void MyPrint(const struct Student *stu)
{
// 常量无法修改,但是我们依然可以使用以下方法修改
struct Student *p = (struct Student *)stu;
p->num = 100;
printf("内部的打印: %d n", stu->num);
}
int main(int argc, char* argv[])
{
struct Student stu = { 1, "lyshark" };
// 传递指针要比传递参数效率更高 MyPrint(stu) --> MyPrint(&stu);
MyPrint(&stu);
printf("外部的打印: %d n", stu.num);
system("pause");
return 0;
}
多级指针与野指针: 多级指针就是指针变量中存储有另一个指针变量,野指针就是非法指针,该指针被指向一段非法内存.
代码语言:javascript复制#include <stdio.h>
#include <string.h>
void MyPrintA()
{
int number = 10;
int *ptr1 = &number; // 定义一级指针,指向number;
int **ptr2 = &ptr1; // 定义二级指针,里面存放一级指针的地址
printf("二级: %x --> 一级: %x --> 数据: %x (%d) n",ptr2,ptr1,&number,number);
int age = 20;
int *ptr3 = &age; // 定义一级指针
int **ptr4 = &ptr3; // 定义二级指针
int ***ptr5 = &ptr4; // 定义三级指针
***ptr5 = 100; // 改变指针中的数据
printf("三级: %x --> 二级: %x --> 一级: %x --> 数据: %x (%d) n", ptr5,ptr4,ptr3,&age,age);
}
void MyPrintB()
{
int number = 10;
int *ptr = &number; // 定义指针指向number
ptr = 0xffff; // 该指针是一个错误的地址(野指针)
*ptr = 200; // 尝试赋值会发生错误
}
int main(int argc, char* argv[])
{
MyPrintA();
system("pause");
return 0;
}
指针的步长与取值: 指针与指针之间可以强制类型转换通过步长索引元素,offsetof()
可用于计算步长.
#include <stdio.h>
#include <stddef.h>
void MyPrintA()
{
char buf[1024] = { 0 };
int number = 10;
int name = 20;
// 在buf 1的内存地址处,将number里面的数据,拷贝进去
memcpy(buf 1, &number, sizeof(int)); // 第一个数据位置 1
memcpy(buf 5, &name, sizeof(int)); // 第二个数据位置 1 4 => 5
char *number_ptr = buf;
// (int *) => 代表要强制取出int => 4字节的数据
// (number_ptr 1) => 代表要从此处向后取出4字节
int number_tmp = *(int *)(number_ptr 1); // 找到第一个变量的地址
printf("取出buf里面的4字节(int) --> 地址: %x --> 数据: %d n", &number_tmp, number_tmp);
char *name_ptr = buf;
int name_tmp = *(int *)(name_ptr 5); // 找到第二个变量的地址
printf("取出buf里面的4字节(int) --> 地址: %x --> 数据: %d n", &name_tmp, name_tmp);
}
void MyPrintB()
{
struct Person
{
int num; // 4 字节
char name[64]; // 64 字节
int age; // 4 字节
};
struct Person stu = { 1, "lyshark", 23 };
printf("age相对于Person首地址偏移量: %d n", offsetof(struct Person, age));
// 定位到Age的内存 (char *)&stu offsetof(struct Person,age)
// 整体括起来取首地址 ( (char *)&stu offsetof(struct Person,age) )
// 强制取出4字节数据 (int *) ((char *)&stu offsetof(struct Person,age))
int struct_age = *(int *)((char *)&stu offsetof(struct Person, age));
printf("结构体Person中的 Age 的数据 --> %d n", struct_age);
int struct_name = ((char *)&stu offsetof(struct Person, name));
printf("结构体Person中的 Name 的数据 --> %s n", struct_name);
}
int main(int argc, char* argv[])
{
MyPrintB();
system("pause");
return 0;
}
指针的间接赋值: 指针变量与普通变量赋值不同,指针代表一个地址,当该地址中的值被改变时,指向的变量都会发生变化.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void ChangeValue(int *ptr)
{ // 改变某个值
(*ptr) = 200;
}
void ChangePointer(int **val)
{ // 改变指针的值
*val = 0x09;
}
int main(int argc, char* argv[])
{
int number = 100;
ChangeValue(&number);
printf("改写后的数值: %d --> 当前number地址: %x n", number, &number);
int *ptr = NULL;
ChangePointer(&ptr);
printf("改写后的数值: %d --> 当前ptr地址: %x n", ptr,&ptr);
system("pause");
return 0;
}
定义Void万能指针: 万能指针就是什么数据类型的数据都能指,但是在指向其他数据时必须经过转换才可使用.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 万能指针,必须经过转换才可使用
void MyPrintA()
{
int number = 10;
void *int_ptr = &number; // 万能指针
*(int *)int_ptr = 100; // 赋值必须进行强转(告诉它需要读取多大的字节)
printf("数值: %d --> 地址: %x n", number, int_ptr);
}
// 针对数组类型的万能指针
void MyPrintB()
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
void *array_ptr = Array; // 赋值一个空指针
*(int *)array_ptr = 100; // 改变第一个值 Array[0]
*((int *)array_ptr 1) = 200; // 改变第二个值 Array[1]
for (int x = 0; x < 10; x )
printf("输出指针: %x 数据: %d n", ((int *)array_ptr x),Array[x]);
}
void MyPrintC()
{
int Array[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
void *array_ptr = Array;
//*(((int *)array_ptr 1) 2) = 400; // Array[1][0]
//*(((int *)array_ptr 2) 2) = 500; // Array[1][1]
printf("Array[0][0] -> %d n", *((int *)array_ptr 0));
printf("Array[1][0] -> %d n", *(((int *)array_ptr 0) 3)); // 步长为3
printf("Array[1][1] -> %d n", *(((int *)array_ptr 1) 3)); // 一维数组大小为3
}
int main(int argc, char* argv[])
{
MyPrintC();
system("pause");
return 0;
}
Calloc 分配堆指针: calloc()
函数,主要用于一次性分配内存空间,与之相似的函数有malloc()
使用与其大同小异.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
// 开辟 10 * sizeof(int) 大小的堆空间
int *ptr = calloc(10, sizeof(int));
// 循环将堆空间进行赋值
for (int x = 0; x < 10; x )
ptr[x] = x 1;
// 循环输出堆中数据
for (int x = 0; x < 10; x )
printf("堆地址: %x --> 数据: %d n", &ptr[x], ptr[x]);
// 释放堆空间
if (ptr != NULL)
{
free(ptr);
ptr = NULL;
}
system("pause");
return 0;
}
Realloc 扩充堆指针: 当堆空间容量不足时,我们就可以使用Realloc()
函数对堆空间进行动态的扩容.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
// 使用 malloc() 分配10个整数类型的数据
int *ptr = malloc(sizeof(int)* 10);
// 循环对堆空间初始化,并输出结果
for (int x = 0; x < 10; x )
{
ptr[x] = x;
printf("堆地址: %x --> 数据: %d n", &ptr[x],ptr[x]);
}
// 开始扩充堆空间
// realloc() 函数追加空间,如果空间够用则直接追加.
// 如果空间不够用了,那么将会重新分配一块空间,并将原数据拷贝到新的地址上
// 最后将原始的空间释放掉,只保留新的空间
int old_ptr = ptr;
ptr = realloc(ptr, sizeof(int) * 200);
int new_ptr = ptr;
if (old_ptr != new_ptr)
printf("原始空间: 0x%x --> 新空间: 0x%x n", old_ptr, new_ptr);
else
printf("原始空间: 0x%x 没有发生变化.n",old_ptr);
system("pause");
return 0;
}
定义指向数组的指针: 通过typedef()
方式重定义指向数组的指针变量,并可以通过指针灵活的遍历输出.
#include <stdio.h>
#include <stdlib.h>
// 第一种方式:间接定义数组指针类型
void MyPrintA()
{
// typedef 重定义一个数组指针类型
typedef int(Array_Type)[10];
// 相当于定义了 => int MyArray[10];
Array_Type MyArray;
for (int x = 0; x < 10; x )
{
MyArray[x] = x;
printf("输出 MyArray --> %d n", MyArray[x]);
}
// 定义数组指针,该指针是指向整个数组的指针
Array_Type *pArray = &MyArray;
// 由于 pArray 指向了整个数组,我们该如何遍历呢 ?
for (int x = 0; x < 10; x )
{
int Number = *(*pArray x);
printf("遍历 MyArray --> %d n", Number);
}
}
// 第二种方式:直接定义数组指针类型
void MyPrintB()
{
// 直接定义Array数组指针类型,并将pArray_Point指向数组
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
typedef int(*Array_Point)[10]; // 直接定义指针类型
Array_Point pArray_Point = &Array; // 将指针指向数组首地址
for (int x = 0; x < 10; x )
printf("遍历 pArray_Point --> %d n", *(*pArray_Point x));
}
// 第三种方式:直接定义数组指针变量
void PrintC()
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int(*pArray_Param)[10] = &Array; // 直接声明 pArray_Param
for (int x = 0; x < 10; x )
printf("遍历 pArray_Param --> %d n", *(*pArray_Param x));
}
int main(int argc, char* argv[])
{
PrintC();
system("pause");
return 0;
}
用指针实现冒泡排序: 通过指针的方式实现冒泡排序算法,主要是练习一下指针的使用技巧.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void BubbleSort(int *Array, int len)
{
for (int x = 0; x < len - 1; x )
{
for (int y = 0; y < len - 1 - x; y )
{
if (*(Array y) > *(Array y 1))
{
int tmp = *(Array y);
*(Array y) = *(Array y 1);
*(Array y 1) = tmp;
}
}
}
}
int main(int argc, char* argv[])
{
int Array[10] = { 4,7,8,2,1,8,9,3,4,10 };
int len = sizeof(Array) / sizeof(Array[0]);
int *ptr = Array;
BubbleSort(ptr, len);
for (int x = 0; x < 10; x )
{
printf("%d ", *(ptr x));
}
system("pause");
return 0;
}
用指针实现选择排序: 针对字符串的选择排序,从小到大排列,这里我们使用了二级指针作为函数参数.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void PrintArray(char **Array,int len)
{
for (int x = 0; x < len; x)
printf("%s n", Array[x]);
}
void SelectSort(char **Array, int len)
{
for (int x = 0; x < len; x)
{
int min = x;
for (int y = x 1; y < len; y)
{
if (strcmp(Array[y], Array[min]) < 0)
min = y;
}
if (min != x)
{
char *tmp = Array[min];
Array[min] = Array[x];
Array[x] = tmp;
}
}
}
int main(int argc, char* argv[])
{
char *pArray[] = { "ddd", "bbb","sss", "qqq", "yyy", "eee", "ooo" };
int len = sizeof(pArray) / sizeof(char *);
SelectSort(pArray, len);
PrintArray(pArray, len);
system("pause");
return 0;
}
用指针完成数组逆序: 用指针实现逆序存放数组元素值,所谓逆序就是将传入的数组顺序反向颠倒.
代码语言:javascript复制#include <stdio.h>
int ArrayReverse(int *Array, int len)
{
int *Ptr, *StartPtr, *EndPtr, Middle, Temporary;
StartPtr = Array; // 取出数组首地址
EndPtr = Array len - 1; // 取出数组结束地址
Middle = (len - 1) / 2; // 计算出中位数
Ptr = Array Middle; // 指向数组尾地址
for (StartPtr = Array; StartPtr <= Ptr; StartPtr , EndPtr--)
{
Temporary = *StartPtr;
*StartPtr = *EndPtr;
*EndPtr = Temporary;
}
return 0;
}
int main(int argc, char* argv[])
{
int Array[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
ArrayReverse(&Array, 10);
for (int x = 0; x < 10; x )
printf("%d ", Array[x]);
system("pause");
return 0;
}
用指针查找最大最小值: 使用指针查找数列中最大值/最小值,找到后分别返回到两个变量中.
代码语言:javascript复制#include <stdio.h>
int Lookup_Max_Min(int *Array, int len,int *max,int *min)
{
int *ptr;
*max = *min = *Array; // 假设最大最小都是Array
for (ptr = Array 1; ptr < Array len; ptr )
{
if (*ptr > *max)
*max = *ptr;
else if (*ptr < *min)
*min = *ptr;
}
return 0;
}
int main(int argc, char* argv[])
{
int Array[10] = { 6,5,7,8,9,0,3,2,1,5 };
int Array_Max, Array_Min;
Lookup_Max_Min(&Array, 10,&Array_Max,&Array_Min);
printf("最大值: %d --> 最小值: %d n", Array_Max, Array_Min);
system("pause");
return 0;
}
指针实现数组操作: 如下案例中演示了利用指针实现基本取值,基本替换,正反向遍历等操作.
代码语言:javascript复制#include <stdio.h>
#include <Windows.h>
int main(int argc, char * argv[])
{
int Array[] = {1,2,3,4,5,6,7,8,9,10};
DWORD *PEB = (DWORD *)Array;
DWORD *Ldr = *((DWORD **)((DWORD *)Array 1));
printf("Ldr = > %x Value = %d n", &Ldr,Ldr);
// 基本的取值
printf("PEB = %x &PEB = %x n", PEB, &PEB);
Ldr = (DWORD *)&PEB;
printf("LDR = %x &LDR = %x *LDR = %x n", Ldr, &Ldr, *Ldr);
printf("(unsigned char *)PEB = %x n", (unsigned char *)PEB);
printf("(unsigned char *)PEB 4 = %x n", (unsigned char *)PEB 4);
printf("取出第一个元素内存地址: %x n", (DWORD **)((unsigned char *)PEB 4));
printf("取出其中的值: %d n", *(DWORD **)((unsigned char *)PEB 4));
// 取值与替换值
DWORD ref = (DWORD)*((unsigned char *)Array 4); // 取出第2个值
*((DWORD *)(Array 1)) = 10; // 替换数组中第二个值
printf("取出的值: %d 替换后: %d n", ref, *((DWORD *)(Array 1)));
// 正向遍历元素
for (int x = 0; x < 10; x )
{
DWORD ref1 = *((DWORD *)((unsigned char *)Array (x * 4)));
printf("正向元素输出: %d n", ref1);
}
// 反向输出元素
DWORD *Frist = NULL;
DWORD *Last = NULL;
for (int x = 0; x < 10; x )
{
Frist = *(DWORD **)((DWORD *)Array x);
Last = (DWORD *)((DWORD *)Array (9 - x));
printf("反向输出: %d --> %d n", Frist, *Last);
}
system("pause");
return 0;
}
指针定位多维数组: 以下案例通过指针定位二三维指针数组中的元素,并输出元素值.
代码语言:javascript复制#include <stdio.h>
#include <Windows.h>
int main(int argc, char * argv[])
{
DWORD Array[] = { 1, 2, 3, 4, 5 };
DWORD *ArrayPtr[] = { &Array[0], &Array[1], &Array[2], &Array[3], &Array[4] };
DWORD *ArrayPtrS[] = { ArrayPtr[0], ArrayPtr[1], ArrayPtr[2], ArrayPtr[3], ArrayPtr[4] };
DWORD *PEB;
// 这三种方式均可定位二级数组
PEB = *((DWORD **)((DWORD *)ArrayPtr 1));
printf("%x %d n", PEB,*PEB);
PEB = *((DWORD **)((DWORD *)(ArrayPtr 1)));
printf("%x %d n", PEB, *PEB);
PEB = *(DWORD **)((unsigned char *)(ArrayPtr) (1*4));
printf("%x %d n", PEB, *PEB);
PEB = *(DWORD **)ArrayPtr;
printf("得到ArrayPtr地址: %x --> 得到Array元素地址: %x --> 得到元素值: %x n", &PEB,PEB,*PEB);
// 二级元素赋值操作
printf("得到第一个指针地址: %x n", (DWORD)*(DWORD **)ArrayPtr);
printf("得到第二个指针地址: %x --> 数据: %d n", (*((DWORD **)ArrayPtr) 1), *(*((DWORD **)ArrayPtr) 1));
printf("原始数据为: %x n", *ArrayPtr[1]);
*((DWORD *)(ArrayPtr 1)) = (DWORD)*(DWORD **)ArrayPtr;
printf("更改数据为: %x n", *ArrayPtr[1]);
printf("原始指针数据为: %x n", *ArrayPtr[1]);
**((DWORD **)(ArrayPtr 1)) = (DWORD)*(DWORD **)ArrayPtr;
printf("更改指针数据为: %x n", *ArrayPtr[1]);
for (int x = 0; x < 5; x )
{
printf("地址: %x --> 数据: %d n", (*((DWORD **)ArrayPtr) x), *(*((DWORD **)ArrayPtr) x));
}
system("pause");
return 0;
}
同理三维指针的定位同样如此,只是在解析时需要多加一层即可取出数据.
代码语言:javascript复制#include <stdio.h>
#include <Windows.h>
int main(int argc, char * argv[])
{
DWORD Array[] = { 1, 2, 3, 4, 5 };
DWORD *ArrayPtr[] = { &Array[0], &Array[1], &Array[2], &Array[3], &Array[4] };
DWORD *ArrayPtrS[] = { ArrayPtr[0], ArrayPtr[1], ArrayPtr[2], ArrayPtr[3], ArrayPtr[4] };
// 输出三级指针中的数据
DWORD *PtrA = (DWORD *)((DWORD **)((DWORD ***)ArrayPtrS));
printf("获取到ArrayPtr[0]地址 = %x t 获取到Array[0]地址 = %x n", PtrA,*PtrA);
DWORD *PtrB = (DWORD *)((DWORD **)((DWORD ***)(ArrayPtrS 1)));
printf("获取到ArrayPtr[1]地址 = %x t 获取到Array[1]地址 = %x n", PtrB, *PtrB);
DWORD PtrC = *((DWORD *)((DWORD **)((DWORD ***)(ArrayPtrS 1))));
printf("获取到里面的数值: %d n", *(DWORD *)PtrC);
// 三级指针 遍历指针数据
printf("ArrayPtrS => ");
for (int x = 0; x < 5; x )
printf("0x%x ", &ArrayPtrS[x]);
printf("n");
printf("ArrayPtr => ");
for (int x = 0; x < 5; x )
printf("0x%x ", ArrayPtr[x]);
printf("n");
// 输出特定指针
DWORD *PtrBB = ((DWORD *)((DWORD **)((DWORD ***)(ArrayPtrS 1))));
printf("ArrayPtrS[1] => %x n", PtrBB);
DWORD **PtrAA = *((DWORD ***)(ArrayPtrS) 1);
printf("ArrayPtr[1] => %x n", PtrAA);
DWORD Ref = *(DWORD *) (*((DWORD *)((DWORD **)((DWORD ***)(ArrayPtrS 1)))));
printf("原始数值 ArrayPtrS[1] = %x n", Ref);
system("pause");
return 0;
}
第二章:使用函数与指针
指针作为函数参数传递: 将指针直接作为函数参数传递,此时函数中接收到的就是数组的首地址.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
void MyPrintA(const char *String)
{
printf("输出指针内容: %s n", String 5);
}
void MyPrintB(const **String, int len)
{
// 此处的 *(String x) => *(String 0) == String[0]
for (int x = 0; x < len;x )
printf("首地址: 0x%x 参数: %s n", *(String x), String[x]);
}
int main(int argc, char* argv[])
{
// 一级指针的传递方法
char *ptr = malloc(sizeof(char)* 100); // 开辟 char * 100
memset(ptr, 0, 100); // 初始化空间为0
strcpy(ptr, "hello lyshark"); // 拷贝数据到内存
MyPrintA(ptr);
// 二级指针的传递方法
char *String[] = { "admin", "guest", "lyshark", "root" };
int len = sizeof(String) / sizeof(String[0]);
MyPrintB(String, len);
system("pause");
return 0;
}
指针作为函数返回值: 当一个函数执行结束后,会返回一些值,我们可以通过指针的方式间接返回.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
void AllocteSpace(char **Point)
{
char *ptr = malloc(100);
memset(ptr, 0, 100);
strcpy(ptr, "hello lyshark");
*Point = ptr; // 将分配的地址直接甩出去
}
int main(int argc, char* argv[])
{
char *recv_ptr = NULL;
AllocteSpace(&recv_ptr);
printf("指针地址: %x --> 输出: %s n", &recv_ptr, recv_ptr);
if (recv_ptr != NULL)
{
free(recv_ptr);
recv_ptr = NULL;
}
system("pause");
return 0;
}
多维数组做函数参数: 将多维数组当作函数参数传递到函数内,并在函数内部进行强转,将指针强转为二维数组.
代码语言:javascript复制#include <iostream>
#include <Windows.h>
// 传入数组基地址以及一维元素个数
int GetArray(void* Pointer,int ArrayLen)
{
// 将指针转为二维数组
char(*ptr)[10];
ptr = (char(*)[10])Pointer;
int count = 0;
for (int x = 0; x < ArrayLen; x )
{
// 判断字符串是否为空
if (strlen(ptr[x]) != 0)
{
count = count 1;
}
}
return count;
}
int main(int argc, char* argv[])
{
char array[5][10] = {"zhangsan","lisi","wangwu"};
// 定义指针
void* Pointer = &array[0][0];
// 统计一维数组元素个数
int ref = GetArray(Pointer, 5);
std::cout << "元素个数: " << ref << std::endl;
return 0;
}
多级指针做函数参数: 如下代码中PrintArray()
函数传递了一个常量形式的二级指针作为参数,常量不可被修改.
#include <stdio.h>
#include <stdlib.h>
void PrintArray(const int **point)
{
for (int x = 0; x < 10; x )
printf("地址: %x --> 数据: %d n", &point[x],*point[x]);
}
int main(int argc, char* argv[])
{
int ary[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// 堆区分配指针,接收指针(地址)的指针
int **pArray = malloc(sizeof(int *) * 10);
for (int x = 0; x < 10; x )
// *(pArray x) => pArray[x] = &ary[x];
*(pArray x) = &ary[x];
PrintArray(pArray);
system("pause");
return 0;
}
同理将多级指针进行封装,函数只需传递指针即可实现分配与释放,更易于使用.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
// 分配空间并初始化
void allocateSpace(int **Point)
{
int * Array = malloc(sizeof(int)* 10);
for (int x = 0; x < 10; x )
Array[x] = x;
*Point = Array;
}
// 输出元素
void PrintArray(int *Point)
{
for (int x = 0; x < 10; x )
printf("地址: %x --> 数据: %d n", &Point[x],Point[x]);
}
// 释放空间
void freeSpace(int **Point)
{
free(*Point);
*Point = NULL;
Point = NULL;
}
int main(int argc, char* argv[])
{
int *pArray = NULL;
allocateSpace(&pArray);
PrintArray(pArray);
freeSpace(&pArray);
system("pause");
return 0;
}
分配二级堆指针: 二级堆指针的含义是首先通过malloc开辟一级指针,然后在一级指针里面开辟二级指针.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
void PrintArray(const int **tmp)
{
for (int x = 0; x < 10; x )
printf("%d ", *tmp[x]);
}
int main(int argc, char* argv[])
{
// 开辟二级指针空间,里面用于存放一级指针
int **pArray = malloc(sizeof(int *)* 10);
for (int x = 0; x < 10; x )
{
pArray[x] = malloc(4); // 动态开辟一级整数空间
*(pArray[x]) = x; // 给整数空间赋值
}
PrintArray(pArray);
// 释放堆内存
for (int x = 0; x < 10; x )
{
if (pArray[x] != NULL)
{
free(pArray[x]); // 先释放小的空间
pArray[x] = NULL; // 小空间指针初始化
}
free(*pArray); // 最后释放大空间
*pArray = NULL; // 大空间指针初始化
}
system("pause");
return 0;
}
上方我们可以很直观的看出二级指针的分配方式,其实字符串指针默认就是二级指针,只是看起来像是一级而已.
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char *p[] = { "package", "housing", "pace", "unleash" };
char **ptr = (char **)malloc(sizeof(char*) * 4);
// 开辟空间并将字符串拷贝到空间中
for (int x = 0; x < 4; x )
{
ptr[x] = (char*)malloc(10 * sizeof(char));
sprintf(ptr[x], "%s", p[x]);
}
// 输出指针ptr指向的数据
for (int x = 0; x < 4; x )
{
printf("地址: %x --> 数据: %s n", &ptr,*ptr);
ptr ;
}
system("pause");
return 0;
}
指向函数的指针(函数指针): 函数指针通常用于指向一个函数首地址,通过指针可以调用该函数.
代码语言:javascript复制#include <stdio.h>
#include <string.h>
int MyPrintA(int x,int y)
{
printf("MyPrintA --> %d --> %d n", x, y);
return 1;
}
int main(int argc, char* argv[])
{
printf("函数名入口: %d n", &MyPrintA);
// 直接定义并调用函数指针
int *FuncAddr = (int *) &MyPrintA; // 获取函数地址
int(*MyFunc)(int,int) = FuncAddr; // 定义函数指针
int result = MyFunc(10,20); // 调用函数指针
// 定义函数类型,并通过类型定义函数指针
typedef int(Func_Type)(int, int); // 先定义类型
Func_Type *pFunc = MyFunc; // 定义函数指针
pFunc(100, 200); // 调用(1)
(*pFunc)(1000, 2000); // 调用(2)
// 直接定义函数类型,并使用(1)
typedef int(*Func_Ptr)(int, int);
Func_Ptr pFunc2 = MyFunc;
pFunc2(10000, 20000);
// 直接定义函数类型,并使用(2)
int(*pfunc)(int, int) = NULL;
pfunc = MyFunc;
pfunc(100000, 200000);
// 如果拿到了一个地址,我们该如何将其转换为函数指针,并调用?
// 此处应该关闭基址随机化 ASLR
printf("函数名入口: %x n", &MyPrintA);
// (int (*)(int, int)) 0x4110e6;
int(*Print)(int, int) = 0x4110e6; // 转换为函数指针 MyPrintA
Print(1, 2); // 直接调用
system("pause");
return 0;
}
函数指针的参数传递: 将一个函数地址进行参数传递,实现动态的函数值传递,可作为函数定制使用.
代码语言:javascript复制#include <stdio.h>
int add(int x, int y) { return x y; }
int sub(int x, int y) { return x - y; }
// 函数可以作为另外一个函数的参数
void doLogic(int(*pFunc)(int, int),int x,int y)
{
int result = pFunc(x, y);
printf("X与Y进行运算后的结果是: %d n", result);
}
int main(int argc, char* argv[])
{
// 手动将指针指向add函数,然后调用
int(*ptr)(int, int) = add;
int result = ptr(10, 20);
printf("两个数相加的结果是: %d n", result);
// 通过使用dologic函数实现间接调用
doLogic(&sub, 100, 10);
doLogic(&add, 100, 10);
system("pause");
return 0;
}
函数指针传递数组: 除了传值以外,函数指针同样可以实现传递一个指针数组,并依次循环调用函数.
代码语言:javascript复制#include <stdio.h>
int add(int x, int y)
{
int result = x y;
printf("x y => %d ", result);
return result;
}
int sub(int x, int y)
{
int result = x - y;
printf("x - y => %d ", result);
return result;
}
int main(int argc, char* argv[])
{
int(*func_array[2])(int, int); // 定义函数指针数组
func_array[0] = add; // 分别给与不同的函数地址
func_array[1] = sub; // 并将函数地址存入函数指针数组
// 循环调用两个函数,并以依次传入 x = 200 ,y=20
for (int x = 0; x < 2; x )
{
int z = func_array[x](100, 20);
printf(" -> %d n", z);
}
system("pause");
return 0;
}
函数指针绑定回调函数: 函数指针也可以实现绑定回调函数,当函数执行到指定位置时,执行回调输出一些数据.
代码语言:javascript复制#include <stdio.h>
// 该函数用于输出数据.
void Print_Array(void *Array, int eleSize, int len, void(*print)(void *))
{
char *start = (char *)Array; // 转为每次读取一个字节
for (int x = 0; x < len; x)
{
//printf("数据: %d n", start x * eleSize);
print(start x * eleSize);
}
}
// 此处就是我们定义的回调函数.
void MyPrint(void *recv)
{
int *ptr = (int*)recv; // 指针强转为4字节
printf("回调函数 --> 输出地址: %x --> 数据: %d n", ptr,*ptr);
}
void MyPrintPerson(void *recv)
{ // 指针强转为结构体类型,才能读数据.
struct Person *stu = (struct Person *)recv;
printf("回调函数 --> 输出地址: %x n", stu);
}
int main(int argc, char* argv[])
{
// 第一种传递数组的方式,实现回调
int arry[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Print_Array(arry, sizeof(int), 10, MyPrint);
// 第二种传递结构体的方式实现的回调
struct Person
{
char name[64];
int age;
};
struct Person stu[] = { { "admin", 22 }, { "root", 33 } };
Print_Array(stu, sizeof(struct Person), 2, MyPrintPerson);
system("pause");
return 0;
}
本文作者: 王瑞 本文链接: https://www.lyshark.com/post/f65386a4.html 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!