C/C++语言是一种通用的编程语言,具有高效、灵活和可移植等特点。C语言主要用于系统编程,如操作系统、编译器、数据库等;C语言是C语言的扩展,增加了面向对象编程的特性,适用于大型软件系统、图形用户界面、嵌入式系统等。C/C++语言具有很高的效率和控制能力,但也需要开发人员自行管理内存等底层资源,对于初学者来说可能会有一定的难度。

定义并使用一维数组: 该数组是最通用的数组,也是最基本的.

#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;
}

定义并使用二维数组: 二维数组是相对于一维数组而言的,在内存中其都是一段线性的连续的存储空间.

#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]数据:%d\n", *(Pointer1));
printf("arrya[0][1]数据:%d\n", *(Pointer1 + 1)); int *Pointer2 = &array[1][2];
printf("array[1][2]基址: %x \n", Pointer2); printf("数组元素个数:%d\n", sizeof(array) / sizeof(int));
return 0;
}

使用指针遍历数组: 使用指针定位数组,并输出数组元素.

#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;
}

动态数组的定义: 我们可以自己手动分配空间并定义一个动态数组.

#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;
}

指针与数组之间的运算: 通过将两个指针做减法,我们就可以得到两个元素之间相差多少字节.

#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 %d\n", 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;
}

定义一维指针数组: 所为指针数组就是说该数组是用来存放其他的变量地址的,故称为指针数组.

#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;
}

定义二维指针数组: 同理我们可以通过指针遍历到二维数组中的数据,三维四维以此类推.

#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;
}

数组实现逆序排列: 所谓数组逆序就是讲一个正向数组反向排列,并输出排序后的结果.

#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;
}

用数组冒泡排序: 冒泡排序是经典的算法,也是学习数组必学知识点,这里总结一份冒泡排序.

#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;
}

定义常量指针: 一般为了高效传递数据,我们使用地址传递,这样就无须重复拷贝了,通常会加上常指针防止数据被误修改.

#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;
}

多级指针与野指针: 多级指针就是指针变量中存储有另一个指针变量,野指针就是非法指针,该指针被指向一段非法内存.

#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;
}

指针的间接赋值: 指针变量与普通变量赋值不同,指针代表一个地址,当该地址中的值被改变时,指向的变量都会发生变化.

#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万能指针: 万能指针就是什么数据类型的数据都能指,但是在指向其他数据时必须经过转换才可使用.

#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;
}

用指针实现冒泡排序: 通过指针的方式实现冒泡排序算法,主要是练习一下指针的使用技巧.

#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;
}

用指针实现选择排序: 针对字符串的选择排序,从小到大排列,这里我们使用了二级指针作为函数参数.

#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;
}

用指针完成数组逆序: 用指针实现逆序存放数组元素值,所谓逆序就是将传入的数组顺序反向颠倒.

#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;
}

用指针查找最大最小值: 使用指针查找数列中最大值/最小值,找到后分别返回到两个变量中.

#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;
}

指针实现数组操作: 如下案例中演示了利用指针实现基本取值,基本替换,正反向遍历等操作.

#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;
}

指针定位多维数组: 以下案例通过指针定位二三维指针数组中的元素,并输出元素值.

#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;
}

同理三维指针的定位同样如此,只是在解析时需要多加一层即可取出数据.

#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;
}

第二章:使用函数与指针

指针作为函数参数传递: 将指针直接作为函数参数传递,此时函数中接收到的就是数组的首地址.

#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;
}

指针作为函数返回值: 当一个函数执行结束后,会返回一些值,我们可以通过指针的方式间接返回.

#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;
}

多维数组做函数参数: 将多维数组当作函数参数传递到函数内,并在函数内部进行强转,将指针强转为二维数组.

#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;
}

同理将多级指针进行封装,函数只需传递指针即可实现分配与释放,更易于使用.

#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开辟一级指针,然后在一级指针里面开辟二级指针.

#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;
}

上方我们可以很直观的看出二级指针的分配方式,其实字符串指针默认就是二级指针,只是看起来像是一级而已.

#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;
}

指向函数的指针(函数指针): 函数指针通常用于指向一个函数首地址,通过指针可以调用该函数.

#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;
}

函数指针的参数传递: 将一个函数地址进行参数传递,实现动态的函数值传递,可作为函数定制使用.

#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;
}

函数指针传递数组: 除了传值以外,函数指针同样可以实现传递一个指针数组,并依次循环调用函数.

#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;
}

函数指针绑定回调函数: 函数指针也可以实现绑定回调函数,当函数执行到指定位置时,执行回调输出一些数据.

#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;
}

2.1 C/C++ 使用数组与指针的更多相关文章

  1. 把《c++ primer》读薄(4-2 c和c++的数组 和 指针初探)

    督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1.我们知道,将一个数组赋给另一个数组,就是将一个数组的元素逐个赋值给另一数组的对应元素,相应的,将一个vector 赋给另 ...

  2. C语言核心之数组和指针详解

    指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...

  3. 《征服 C 指针》摘录3:数组 与 指针

    一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...

  4. C指针-数组和指针的归一

    int bArr[] = {1,2,3}; int *iarr = bArr; *iarr = 6; printf("%d\n",*iarr); printf("%d\n ...

  5. 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同

    数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...

  6. (C语言)数组与指针的区别

    以前常常听过这种说法,说数组和指针这两者比较像,但是不能混淆,可是一直没能理解.刚刚在李云的<专业嵌入式软件开发>中,看了讲述数组与指针区别的一章,似乎有所领悟.本着知乎上看到的这张图,我 ...

  7. C语言教学--二维数组和指针的理解

    对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ...

  8. 【C语言学习】《C Primer Plus》第10章 数组和指针

    学习总结 1.数组初始化方式: int a[]={1,2,3} int a[SIZE]={1,2,3} //SIZE是宏定义,数组初始化个数不能大于SIZE,否则报错:当个数小 //SIZE,自动补0 ...

  9. C语言学习004:数组与指针

    在C语言中,字符串实际上就是字符数组,在内存中字符串"Shatner"存储的形式是这样的

  10. C语言--指向多维数组的指针和指针数组

    #include <stdio.h> //void show(char *p[]); ]); int main(){ ][]={","abc","x ...

随机推荐

  1. 成为一个合格程序员所必备的三种常见LeetCode排序算法

    排序算法是一种通过特定的算法因式将一组或多组数据按照既定模式进行重新排序的方法.通过排序,我们可以得到一个新的序列,该序列遵循一定的规则并展现出一定的规律.经过排序处理后的数据可以更方便地进行筛选和计 ...

  2. OOALV总结

    1.1ALV屏幕 1.1.1定义无CONTAINER屏幕 1.屏幕中可以不使用定制控制控件画范围,直接定义一个屏幕即可. "--------------------------------- ...

  3. Docker 魔法解密:探索 UnionFS 与 OverlayFS

    本文主要介绍了 Docker 的另一个核心技术:Union File System.主要包括对 overlayfs 的演示,以及分析 docker 是如何借助 ufs 实现容器 rootfs 的. 如 ...

  4. C#设计模式10——外观模式的写法

    什么是外观模式? 外观模式(Facade Pattern)又称门面模式,是一种结构型设计模式,它提供了一个统一的接口,用来访问一个子系统中一群功能相关联的接口.外观模式定义了一个高层接口,让子系统更容 ...

  5. C# 排序算法5:归并排序

    归并排序,是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个有序的子序列,再把有序的子序列合并为整体有序序列.该算法是采用分治法. 原理: 1.申请空间,使其大小为两个已经排序 ...

  6. java进阶(1)--final、常量

    final是java的关键字,主要表示最终的 一.final修饰的类无法被继承

  7. Zookeeper 的 ZAB 协议 以及 zookeeper 与 nacos 注册中心比对

    本文为博主原创,未经允许不得转载: 目录: 1. ZAB 协议 2. zookeer 节点状态 3. zookeeper 注册中心与 nacos 注册中心比较 4. zookeeper 配置注册中心 ...

  8. 2023 SHCTF-校外赛道 PWN WP

    WEEK1 nc 连接靶机直接梭 hard nc 同样是nc直接连,但是出题人利用linux命令的特性,将部分flag放在了特殊文件中 利用ls -a查看所有文件,查看.gift,可以得到前半段 然后 ...

  9. JavaScript : 获取文件名后缀

               /** 获取文件后缀               *               * indexOf 和 lastIndexOf 都是索引文件            indexO ...

  10. vscode的配置文件

    vscode的配置文件 总述:vscode中一般会在项目文件夹下自动生成.vscode文件夹,其中存放若干配置文件(.json),一般有如下文件: 下面将解释每个文件的用途与表现. 1. c_cpp_ ...