指针总结(2)
程序员文章站
2024-01-01 20:05:16
...
指针:1、指针的总结;
2、理解指针数组;
3、理解数组指针;
4、理解函数指针;
5、理解函数指针数组(转移表);
6、理解指向函数指针数组的指针;
7、理解回调函数的使用;
8、练习使用qsort函数排序各种类型的数据;
9、模仿qsort的功能实现一个通用的冒泡排序;
10、几个指针和数组的相关练习。
2、
扩展:数组中存放的元素为指针类型:一级指针(int *arr[10])、二级指针(int **arr[10])...
指针的类型:整型指针(int *arr[10])、字符型指针(char *arr[10])、浮点型指针(float *arr[10])...
观察可知:指针数组的大小始终是:4*元素个数(指针就是地址,均占4字节)
3、
扩展:数组指针,指向数组的指针:该数组的类型可以是整型(int (*p)[10])、字符型(char (*p)[10])、浮点型(float (*p)[10])...
且数组的类型也可为指针类型,此时就是指针数组,则p为指向指针数组的数组指针(int *(*p)[10])
而将此指针存放在数组中,则p为指向指针数组的数组指针的指针数组(int *(*p[10])[10])
4、
扩展:函数指针,指向函数的指针,函数的返回值可以是整型(int (*pfun)( ))、字符型(char (*pfun)( ))、浮点型(float (*pfun)( ))...
函数的返回值也可以是指针类型(int * (*pfun)( ))、指针数组类型(int* (*pfun)[10]( ))、
数组指针类型(int (*)(*pfun)[10]( ))、
函数也可以是不同的参数列表,int (*pfun)(int, char)...
5、
扩展:函数指针数组是数组,是存放函数地址的数组,函数可以有参数(int (*parr[10])(int, char)),函数的返回值可以是整型、字符型、也可以是指针(int* (*parr[10])( ))...
应用:转移表的实现(简单计算器)
#include<stdio.h>
#include<assert.h>
#include<Windows.h>
#pragma warning(disable:4996)
//函数指针数组(转移表):简单计算器
int my_add(int a, int b)
{
return a + b;
}
int my_sub(int a, int b)
{
return a - b;
}
int my_mul(int a, int b)
{
return a * b;
}
int my_div(int a, int b)
{
//assert(b != 0);
if (!b){
printf("error num2 by zero!\n");
return -1;
}
return a / b;
}
void menu()
{
printf("******************************\n");
printf("**** 1:add 2:sub ****\n");
printf("**** 3:mul 4:div ****\n");
printf("**** 0:exit! ****\n");
printf("******************************\n");
printf("please input your chioce!\n");
}
int main()
{
//定义函数指针数组
int(*parr[4])(int a, int b) = { my_add, my_sub, my_mul, my_div };
int choice;
int a, b;
do{
menu();
scanf("%d", &choice);
if (choice > 0 && choice <= 4){
printf("please enter your nums<a, b>:");
scanf("%d%d", &a, &b);
printf("the result is: %d\n", parr[choice - 1](a, b));
}
else if (choice == 0){
exit(0);
}
else{
printf("input error! you should enter your choice at interval of [0,4]\n");
}
} while (1);
system("pause");
return 0;
}
6、扩展:数组指针:int (*p)[10]; 当指针指向的这个数组是存放函数地址时,则p就是指向函数指针数组的指针(int *(*p)[10])( ))
注意:由于数组元素个数也是数组类型的一部分,所以此刻声明的指针变量中的[10]必须和它指向的函数指针数组的元素个数一致!!!
7、回调函数:
定义:将一个函数的指针(也就是该函数的地址)作为参数传递给另一个函数,当此函数指针被用来调用其所指向的函数时,此时,该函数指针指向的函数即为回调函数。
作用:节省函数调用时间。
应用:使用回调函数,模拟实现qsort(快排)。
8、练习使用qsort函数排序各种类型的数据:整型(int)、浮点型(double)、字符串(char *)。
认识qsort函数:Performs a quick sort.
头文件:<stdlib.h>
函数原型:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
有四个参数,第一个:要比较的数据(数组);
第二个:数据个数;
第三个:数据类型大小;
第四个:返回值为int型、参数列表为 const void *elem1, const void *elem2 的函数(该函数是自己实现的比较当前要排序的许多元素中的两个元素大小,并且返回1或-1或0(可根据升序降序自己设置)!!!(该函数就为回调函数)
#include<stdio.h>
#include<Windows.h>
int my_int_cmp(const void * num1, const void * num2)
{
int *x = (int *)num1;
int *y = (int *)num2;
if (*x > *y){
return 1;//升序
}
else if (*x < *y){
return -1;
}
else{
return 0;
}
}
int my_double_cmp(const void * num1, const void * num2)
{
double *x = (double *)num1;
double *y = (double *)num2;
if (*x > *y){
return -1;//降序
}
else if (*x < *y){
return 1;
}
else{
return 0;
}
}
int my_string_cmp(const void * num1, const void * num2)
{
char **x = (char **)num1;//存放字符串地址的变量x
char **y = (char **)num2;
char *p1 = *x;//定义指针变量指向字符串
char *p2 = *y;
while (*p1 == *p2 && *p1 != '\0'){
p1++;
p2++;
}
if (*p1 != *p2){//说明while循环跳出的原因是第一个条件不满足
if (*p1 > *p2){
return -1;//降序
}
else{
return 1;
}
}
else{//执行到这,说明while的第一个条件成立,第二个不成立,说明两个字符串相等
return 0;
}
}
int main()
{
/*int arr1[] = { 1, 3, 2, 4, 1, 7, 6, 3, 8, 9 };
int size = sizeof(arr1) / sizeof(arr1[0]);
qsort(arr1, size, sizeof(int), my_int_cmp);*/
/*double arr2[] = { 1.2, 4.3, 6.6, 2.1, 3.9, 5.8, 9.3, 0.3 };
int size = sizeof(arr2) / sizeof(arr2[0]);
qsort(arr2, size, sizeof(double), my_double_cmp);*/
//字符串排序
char *arr3[] = { "abcd", "abcde", "abce", "zxcbv", "zxca", "qwsa", "qeas" };
int size = sizeof(arr3) / sizeof(arr3[0]);
qsort(arr3, size, sizeof(char *), my_string_cmp);
system("pause");
return 0;
}
9、模仿qsort的功能实现一个通用的冒泡排序。
#include<stdio.h>
#include<Windows.h>
int my_int_cmp(const void * num1, const void * num2)
{
int *x = (int *)num1;
int *y = (int *)num2;
if (*x > *y){
return 1;//升序
}
else if (*x < *y){
return -1;
}
else{
return 0;
}
}
int my_double_cmp(const void * num1, const void * num2)
{
double *x = (double *)num1;
double *y = (double *)num2;
if (*x > *y){
return -1;//降序
}
else if (*x < *y){
return 1;
}
else{
return 0;
}
}
int my_string_cmp(const void * num1, const void * num2)
{
char **x = (char **)num1;//存放字符串地址的变量x
char **y = (char **)num2;
char *p1 = *x;//定义指针变量指向字符串
char *p2 = *y;
while (*p1 == *p2 && *p1 != '\0'){
p1++;
p2++;
}
if (*p1 != *p2){//说明while循环跳出的原因是第一个条件不满足
if (*p1 > *p2){
return -1;//降序
}
else{
return 1;
}
}
else{//执行到这,说明while的第一个条件成立,第二个不成立,说明两个字符串相等
return 0;
}
}
void my_swap(const void * num1, const void * num2, int size)
{
//交换两个元素:按字节交换,交换size(类型大小)次
char *p1 = (char *)num1;
char *p2 = (char *)num2;
int i = 0;
while (i < size){
char tmp = *(p1 + i);
*(p1 + i) = *(p2 + i);
*(p2 + i) = tmp;
i++;
}
}
//模仿qsort的功能实现一个通用的冒泡排序
void my_qsort(void *base, int count, int size, int(*cmp)(const void *, const void *))
{
for (int i = 0; i < count - 1; i++){
int flag = 0;//冒泡优化
for (int j = 0; j < count - 1 - i; j++){
if (cmp((char *)base + size*j, (char *)base + size*(j + 1)) > 0){
//base是void*,所以根据字节乘类型大小来确定比较元素的类型
flag = 1;
my_swap((char *)base + size*j, (char *)base + size*(j + 1), size);
}
}
}
}
int main()
{
/*int arr1[] = { 1, 3, 2, 4, 1, 7, 6, 3, 8, 9 };
int size = sizeof(arr1) / sizeof(arr1[0]);
my_qsort(arr1, size, sizeof(int), my_int_cmp);*/
/*double arr2[] = { 1.2, 4.3, 6.6, 2.1, 3.9, 5.8, 9.3, 0.3 };
int size = sizeof(arr2) / sizeof(arr2[0]);
my_qsort(arr2, size, sizeof(double), my_double_cmp);*/
//字符串排序
char *arr3[] = { "abcd", "abcde", "abce", "zxcbv", "zxca", "qwsa", "qeas" };
int size = sizeof(arr3) / sizeof(arr3[0]);
my_qsort(arr3, size, sizeof(char *), my_string_cmp);
system("pause");
return 0;
}
10、练习指针与数组
#include<stdio.h>
#include<Windows.h>
void pointArr1()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *p = (int *)(&a + 1);//下一个数组的地址,强转为int*则为跳过该数组的下一个地址(int *)
printf("%d,%d", *(a + 1), *(p - 1));//2,5
}
void pointArr2()
{
typedef struct
{
int num;//4
char *pname;//4
short date;//2
char ch[2];//1*2
short year[4];//2*4
}*p, test;//该结构体的大小:20
p p1 = (p)malloc(sizeof(test));
printf("%d\n", p1);//030A95A0
printf("%d\n", p1 + 0x1);//030A95B4(p1+1实际是加上20)
printf("%d\n", (unsigned long)p1 + 0x1);//030A95A1(此处p1是类型为无符号长整型变量+1就是+1)
printf("%d\n", (unsigned int *)p1 + 0x1);//030A95A4(p1的类型为int*,加1就是加4)
}
void pointArr3()
{
int a[4] = { 1, 2, 3, 4 };
int *p1 = (int *)(&a + 1);//p1指向元素4后面的一个int大小的空间
int *p2 = (int *)((int)a + 1);//p2指向从a首元素的第二个字节开始后面一个int大小的空间
printf("%x,%x", p1[-1], *p2);//4(p1[-1] = *(p1-1)),2000000
}
void pointArr4()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//注意:1、二维数组初始化时,一对{ }代表一个一维数组;
//2、()代表一个元素,括号中是个逗号表达式,
//所以数组int a[3][2] = {1, 3, 5}
int *p;
p = a[0];
printf("%d", p[0]);//1(p[0] = *(p + 0))
}
void pointArr5()
{
int a[5][5];
int(*p)[4];//数组指针
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC(%p:表示无符号数),-4
}
void pointArr6()
{
int a[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//int a[2][5] = { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 0 } };
int *p1 = (int *)(&a + 1);//p1指向数组元素0后的一个int大小的地址空间
int *p2 = (int *)(*(a + 1));//(a+1):代表数组a的第二个元素;
//而*(a+1):代表当前指向数组的首元素;强转后,则p2就指向的元素是6的地址
printf("%d,%d", *(p1 - 1), *(p2 - 1));//0,5
}
void pointArr7()
{
char *a[] = { "work", "at", "abcd" };//指针数组
char **pa = a;//指向数组
pa++;//+1不是加1,实质上是加上每个字符串的大小
printf("%s\n", *pa);//at
}
void pointArr8()
{
char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };
char **cp[] = { c + 3, c + 2, c + 1, c };
char ***cpp = cp;
printf("%s\n", **++cpp);//POINT
printf("%s\n", *--*++cpp + 3);//ER
printf("%s\n", *cpp[-2] + 3);//ST
printf("%s\n", cpp[-1][-1] + 1);//EW
}
int main()
{
//pointArr1();
//pointArr2();
//pointArr3();
//pointArr4();
//pointArr5();
//pointArr6();
//pointArr7();
pointArr8();
system("pause");
return 0;
}
pointArr3()详解
pointArr5()详解
pointArr8()详解