天天看点

12.C语言-指针2

作者:数字双碳王亮

指针引用多维数组

#include<stdio.h>

void main() {
  int a[3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
  };

  //二维数组名,指向一维数组a[0],即0行首地址
  printf("%p\n", a);

  //0行0列元素地址
  printf("%p\n", a[0]);
  printf("%p\n", *a);

  //1行首地址
  printf("%p\n", a+1);

  //1行0列元素的地址
  printf("%p,%p\n", a[1],*(a+1));
}
           

*(a+i) 和 a[i] 是等价的

void main() {
  int a[3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
  };

  printf("%d\n", *(*a+3));//4

  printf("%d\n",*(*(a+1)));//5

  printf("%d,%d\n", *a[2],*(*(a+2)));//9,9
}
           

用指向数组元素的指针变量输出二维数组各元素的值

void main() {
  int a[3][4] = {
  {1,2,3,4},
  {5,6,7,8},
  {9,10,11,12}
  };

  int* p;

  for (p = a[0]; p < a[0]+12; p++)
  {
    if (p!=a[0] && (p - a[0]) % 4 == 0) {
      printf("\n");
    }
    printf("%4d", *p);
  }
}
           

通过用户输入确定元素的值

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

void main() {
  int a[3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
  };
  int x, y;
  scanf("%d,%d", &x, &y);
  printf("(%d,%d)=%d\n",x,y,a[x][y]);
  printf("(%d,%d)=%d\n",x,y,*(*(a+x)+y));
}
           

3个学生,各学4门课,计算总平均分数以及第n个学生成绩

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

void showscore();
void average();

void main() {
  float score[3][4] = { {65,67,70,60}, {80,87,90,81},{90,99,100,98} };
  average(*score, 12);
  printf("\n");
  showscore(*score, 1);
}

//计算平均数
void average(float* p, int n) {
  float sum = 0, avg = 0;
  float* p1;
  for (p1 = p; p1 < p + n; p1++)
  {
    sum += *p1;
  }
  avg = sum / n;
  printf("平均数:%f", avg);
}

void showscore(float* p, int n) {
  float* p1 = p + n * 4;//确定第几个学员
  for (int i = 0; i < 4; i++)
  {
    printf("%5.2f ", *(p1 + i));
  }
}
           

遍历数组

形式参数是一个已定义大小的数组

void main() {
  float score[3][4] = { {65,67,70,60}, {80,87,90,81},{90,99,100,98} };
  //average(*score, 12);
  //printf("\n");
  //showscore(*score, 1);
  sum1(score);
}

void sum1(float num[12]) {
  double sum = 0;
  for (size_t i = 0; i < 12; i++)
  {
    sum += num[i];
  }
  printf("%f", sum);
}

           
void main() {
  float score[3][4] = { {65,67,70,60}, {80,87,90,81},{90,99,100,98} };
  sum2(score);
}

void sum2(float num[3][4]) {
  double sum = 0;
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      sum += num[i][j];
    }
  }
  printf("%f", sum);
}
           

嵌套for

void average1(float* p) {
  float sum = 0, avg = 0;
  float* p1 = p;
  for (int i = 0; i < 3; i++)
  {
    p1 = p + (i) * 4;
    for (int j = 0; j < 4; j++)
    {
      printf("%5.2f ", *(p1 + j));
    }
    printf("\n");
  }
}
           

传入一维数组指针

void main() {
  float score[3][4] = { {65,67,70,60}, {80,87,90,81},{90,99,100,98} };
  sum3(score);
}

//二维数组的指针就是一个指向一维数组的指针
void sum3(float (*p)[4]) {
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 4; j++)
    {
      printf("%5.2f ", p[i][j]);
    }
    printf("\n");
  }
}
           

几个例子

一维数组

void show(int a[10]) {
  for (int i = 0; i < 5; i++)
  {
    printf("%d\n", a[i]);
  }
}

void main() {
  int num[5] = { 1,2,3,4,5 };
  show(num);
  system("pause");
}
           

二维数组

void show2(int score[2][3]) {
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      printf("%d\n", score[i][j]);
    }
    printf("\n");
  }
}

void main() {
  int score[2][3] = { {1,2,3},{3,4,5} };
  show2(score);
  system("pause");
}
           

一维数组名可以作为函数参数,多维数组名也可作函数参数。

用指针变量作形参,以接受实参数组名传递来的地址

数组作为函数参数,传递的是地址

void show(int *p) {
  for (int i = 0; i < 5; i++)
  {
    //这里就是一位位移动
    printf("%d\n", *(p + i));//printf("%d\n", p[i]);也是可以的
  }
}

void main() {
  int num[5] = { 1,2,3,4,5 };
  show(num);
  system("pause");
}
           

二维数组

void show(int(*p)[3]) {
  for (int i = 0; i < 2; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      printf("%5d", p[i][j]);
    }
    printf("\n");
  }
}
void main() {
  int score[2][3] = { {1,2,3},{3,4,5} };
  show(score);
  system("pause");
}
           

将数组num的数按反顺序输出

这里思路,就是数组折半,头尾互换

//数组没有副本,这里会直接修改原数据
void show(int a[],int n) {
  for (int i = 0; i < n/2; i++)
  {
    int tmp = a[i];
    a[i] = a[n - 1 - i];
    a[n - 1 - i] = tmp;
  }
}

void main() {
  int num[5] = { 1,2,3,4,5 };
  show(num, 5);
  for (int i = 0; i < 5; i++)
  {
    printf("%d,", num[i]);
  }
  system("pause");
}
           

数组排序

void sort(int* p, int n) {
  for (int i = 0; i < n-1; i++)
  {
    for (int j = 0; j < n-i-1; j++)
    {
      //主要在这里,判断前后两个数大小,交换
      if (*(p + j) > *(p + j + 1)) {
        int tmp = *(p + j);
        *(p + j) = *(p + j + 1);
        *(p + j + 1) = tmp;
      }
    }
  }
}

void main() {
  int num[6] = {21,22,3,84,15,60 };
  sort(num, 6);
  for (int i = 0; i < 6; i++)
  {
    printf("%d\n", num[i]);
  }
  system("pause");
}
           

函数指针

一个函数在编译时被分配一个入口地址,这个入口地址就称为函数的指针。

函数名代表函数的入口地址,这一点和数组一样。我们可以用一个指针变量来存放这个入口地址,然后通过该指针变量调用函数。

函数指针就是指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。

void (*p)(int data);//函数指针 

void show(int data) {
  data = data + 100;
  printf("%d", data);
}

void main()
{
  p = show;//给函数指针赋值
  (*p)(100);
  getchar();
}
           

使用typedef

//typedef来定义一个指针函数
typedef int (*PAdd)(int x, int y);

int add(int x, int y) {
  return x + y;
}

void main() {
  PAdd p = add;
  int sum=p(10, 20);
  printf("%d", sum);
}
           

这里PAdd等于定义了一个新的类型

p = add可以理解为将函数指针 p 指向 add函数的地址,p(10,20);相当于执行add(10,20);

//typedef来定义一个指针函数
typedef int (*P)(int x, int y);

int add(int x, int y) {
  return x + y;
}

int mul(int x, int y) {
  return x * y;
}

void main() {
  P p = add;
  int sum=p(10, 20);
  p = mul;
  int rate = p(5, 20);
  printf("%d,%d", sum,rate);
}
           

再来一个

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

void msg() {
  MessageBoxA(0, "你过来呀!", "信息", 0);
}

void main() {
  void (*p)() = msg;
  p();
}
           

函数返回值

一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址

int a = 10;
int b = 20;

//返回一个指针
int* cal() {
  int c = a + b;
  return &c;//这里是有风险的
}

void main() {
  printf("%d", *(cal()));
  getchar();
}
           

用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据

int a = 10;
int b = 20;
int c;

//返回一个指针
int* cal() {
  c = a + b;
  return &c;
}

void main() {
  printf("%d", *(cal()));
  getchar();
}
           

看一下这样会不会出问题

int a = 10;
int b = 20;

//返回一个指针
int* cal() {
  int c = a + b;
  return &c;//这里是有风险的
}

void main() {
  int* p = cal();
  printf("hi\n");
  printf("%d", *p);//这里发现出问题了。
  getchar();
}
           

在数组中找最大数

int* maxdata(int a[], int n) {
  int* p = NULL;
  int max = a[0];
  p = &a[0];
  for (int i = 0; i < n; i++)
  {
    if (max < a[i]) {
      max = a[i];
      p = &a[i];
    }
  }
  return p;
}

void main() {
  int a[8] = { 1,2,32,112,9,12,98,33 };
  int* p = maxdata(a, 8);
  printf("%d", *p);
}
           

字符串拷贝

#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

void main() {
  char s1[50];
  strcpy(s1, "hello world");
  printf("%s", s1);
}
           

手动一个方法

char* mcopy(char* source, char* dest) {
  while (*source!='\0')
  {
    *dest++ = *source++;
  }
}

void main() {
  char s1[50] = "hello world";
  char s2[50]="";
  mcopy(s1, s2);
  printf("%s\n", s1);
  printf("%s", s2);
}
           

了解一个句柄

12.C语言-指针2
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>


void main() {
  HWND win;//就是指针
  win = FindWindowA("Notepad", "Notepad");//找到一个窗口的句柄,也就是地址
  if (win == NULL)
  {
    printf("没有找到Notepad");
  }
  else {
    while (1)
    {
      ShowWindow(win, SW_HIDE);//控制隐藏与显示
      Sleep(1000);
      ShowWindow(win, SW_SHOW);
      Sleep(1000);
    }
    
  }
}
           

Void与空指针

Void* 指针是一种特殊的指针,不指向任何类型的数据,如果需要用此地址指向某类型的数据,应先对地址进行类型转换。可以在程序中进行显式的类型转换,也可以由编译系统自动进行隐式转换。无论用哪种转换 大家必须了解要进行类型转换

指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:

p=NULL
           

用void一定要在取值时转换类型,其实就是确认长度

void main() {
  int num = 10;
  double db = 10.8;
  int* p1 = #
  void* p2 = p1;//可以接受任务类型地址
  //printf("%d", *p2);//这里会出错,void没有类型
  int* p3 = p2;
  printf("%d\n", *p3);
  printf("%d", *((int*)p2));//转换类型
}
           
void main() {
  char ch = 'B';
  int num = 99;
  double db = 3.14;
  //非常灵活,什么都可以指向
  void* p;
  p = &ch;
  printf("%c\n", *((char *)p));
  p = #
  printf("%d\n", *((int*)p));
  p = &db;
  printf("%f\n", *((double*)p));
}
           

空指针

void main() {
  int* p = NULL;
  if (p == NULL) {
    printf("指针为空!");
  }
}
           

memset函数

void memset ( void *s , char ch, unsigned n )

函数功能:将s为首地址的一片连续的n个字节内存单元都赋值为ch

#include<stdio.h>
#include<stdlib.h>

void main() {
  char s[50] = "hello world";
  //从首地址开始,将前5个字符赋值成A
  memset(s, 'A', 5);
  printf("%s", s);
}
           
#include<stdio.h>
#include<stdlib.h>

void main() {
  int a[10];
  //这里为什么是40,因为a是int类型数组,
  //每个元素是4个字节,所以int[10]实际是40个字节
  //而memset接受的char只有1个字节
  memset(a, 0, 40);
  for (int i = 0; i < 10; i++)
  {
    printf("%d\t", a[i]);
  }
}
           

这里有个坑,给0没问题,要是给1就出问题了,因为使用memset函数,对每个字节赋值为1。一个字节为8位,所以,数组变为

0000 0001 0000 0001 0000 0001 0000 0001