【C语言】指针&位运算

文章目录

一、前言

指针是C语言中的一个重要概念,也是C语言的一个重要特色。正确而灵活地运用它,可以有效地表示复杂的数据结构;能动态分配内存;方便地使用字符串;有效而方便地使用数组;在调用函数时能获得1个以上的结果;能直接处理内存单元地址等,这对设计系统软件是非常必要的。掌握指针的应用,可以使程序简洁、紧凑、高效。每一个学习和使用C语言的人,都应当深入地学习和掌握指针。可以说,不掌握指针就是没有掌握C的精华。

二、地址和指针的概念

变量的属性:名、值和地址
变量的访问方式:直接访问、间接访问
变量的地址称为该变量的“指针”
指针变量是存放地址的变量

#include <stdio.h>
#define CHANGE 1

int main(int argc, char *argv[]) {

    int i,j;
    int *i_pointer,*j_pointer; // 创建指针变量


    i_pointer = &i; // 变量的地址:该变量的指针
    j_pointer = &j;
    // printf("%d\n",sizeof(i_pointer));

    printf("i=%d\n",i); // 通过变量访问:直接访问 
    printf("i=%d\n",*i_pointer); // 通过指针变量访问:间接访问 *i_pointer = i
    return 0;
}

三、变量的指针和指向变量的指针变量

变量的指针就是变量的地址。存放变量地址的变量指针变量,它用来指向另一个变量。为了表示指针变量和它所指向的变量之间的联系,在程序中用“*”符号表示“指向”。如果已定义i_pointer为指针变量,则(*i_pointer)是i_pointer所指向的变量。

#include <stdio.h>

int main(int argc, char *argv[]) {

    int i,j;
    int *i_pointer,*j_pointer; // 创建指针变量

    i = 12;
    i_pointer = &i; // 指向整型变量i,i_pointer存放的是变量i的地址
    j_pointer = &j;
                               
    printf("i=%d\n",i); // 通过变量访问:直接访问 
    printf("*i_pointer=%d\n",*i_pointer); // 通过指针变量访问:间接访问 *i_pointer = i
    return 0;
}

3.1 定义一个指针变量

	C语言规定所有变量在使用前必须定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其他类型的变量,它是用来专门存放地址的,必须将它定义为“指针类型”。
	定义指针变量的一般形式为
		基类型 * 指针变量名;
	可以用赋值语句使一个指针变量得到另一个变量的地址,从而使它指向一个该变量。

(1)指针变量前面的“*”表示该变量的类型为指针型变量
(2)在定义指针变量时必须指定基类型

3.2 指针变量的引用

	指针变量中只能存放地址(指针),不要将一个整数(或其他非地址类型的数据)赋给一个指针变量。
	有两个有关的运算符:
	(1)&:取地址运算符
	(2)*:指针运算符(或称“间接访问”运算符),取其指向的内容。

通过指针变量访问整型变量

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a, b;
  int *pointer_1, *pointer_2; 
  a = 100;
  b = 10;

  pointer_1 = &a;
  pointer_2 = &b;
  printf("%d, %d\n", a, b);
  printf("%d, %d\n", *pointer_1, *pointer_2);

  return 0;
}

在这里插入图片描述

3.2.1 “&” 和"*"说明

在这里插入图片描述
输入a和b两个整数,按先大后小的顺序输出a和b

#include <stdio.h>

int main(int argc, char *argv[]) {

  int *p1, *p2, a, b;
  scanf("%d,%d", &a, &b);
  p1 = &a;
  p2 = &b;

  if (a < b) {

    p1 = &b;
    p2 = &a;
    
  }

  printf("a=%d,b=%d\n", a, b);
  printf("max = %d,min = %d\n", *p1, *p2);
  return 0;
}

在这里插入图片描述

3.3 指针变量作为函数参数

在这里插入图片描述
1、输入a和b两个整数,按先大后小的顺序输出a和b

#include <stdio.h>

void swap(int *x, int *y) {

  //    int *temp;
  //    int i;
  //    temp = &i;
  //    *temp = *x; // i= a
  //    *x= *y;// a= b
  //    *y = *temp; // b= i

  int i;
  i = *x;
  *x = *y;
  *y = i;

  
}

int main(int argc, char *argv[]) {

  int a, b;
  scanf("%d,%d", &a, &b);
  if (a < b)
    swap(&a, &b);

  printf("max=%d min=%d\n", a, b);
  return 0;
}

在这里插入图片描述

2、输入a、b、c这3个整数,按大小顺序输出

#include <stdio.h>

void swap(int *x, int *y) {

  //    int *temp;
  //    int i;
  //    temp = &i;
  //    *temp = *x; // i= a
  //    *x= *y;// a= b
  //    *y = *temp; // b= i

  int i;
  i = *x;
  *x = *y;
  *y = i;
}

void exchange(int *q1, int *q2, int *q3) {
  if (*q1 < *q2)
    swap(q1, q2);
  if (*q1 < *q3)
    swap(q1, q3);
  if (*q2 < *q3)
    swap(q2, q3);
}

int main(int argc, char *argv[]) {

  int a, b, c;
  scanf("%d,%d,%d", &a, &b, &c);

  exchange(&a, &b, &c);

  printf("%d %d %d\n", a, b, c);
  return 0;

  
}

在这里插入图片描述

四、数组和指针

在这里插入图片描述

4.1 指向数组元素的指针

在这里插入图片描述

#include <stdio.h>


int main(int argc, char *argv[]) {

  int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  int *p;
  p = a;
  p = &a[9];// a[9]指针地址复制给指针p

  printf("*p = %d\n", *p);
  return 0;
}

在这里插入图片描述

4.2 通过指针引用数组元素

在这里插入图片描述

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  int *p;
  p = a;
  // p = &a[0];// a[9]指针地址复制给指针p
  *p = 1;

  printf("a[0]=%d\n", *p);
  return 0;
}

在这里插入图片描述
在这里插入图片描述

4.3 输出数组中的全部元素

在这里插入图片描述

1、下标法

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a[10];
  for (int i = 0; i < 10; i++) {
    scanf("%d", &a[i]);
  }


  for (int i = 0; i < 10; i++) {
    printf("%d\n", a[i]); // 下标法
  }

  return 0;
}

2、通过数组名计算数组元素地址,找出元素的值

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a[10];
  
  for (int i = 0; i < 10; i++) {
    scanf("%d", &a[i]);
  }

  for (int i = 0; i < 10; i++) {
    printf("%d", *(a + i)); // 下标法
  }

  return 0;
}

3、 指针变量指向数组元素

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a[10];
  int *p;
  p = a;

  for (int i = 0; i < 10; i++) {
    scanf("%d", &a[i]);
  }

  for (int i = 0; i < 10; i++) {
    printf("%d", *(p + i)); // 下标法
  }

  return 0;
}

4、通过指针变量输出a数组的10个元素

#include <stdio.h>

int main(int argc, char *argv[]) {

  int a[10];
  int *p;
  p = a;

  for (int i = 0; i < 10; i++) {
    scanf("%d", &a[i]);
  }

  for (int i = 0; i < 10; i++) {
    printf("%d", *(p + i)); // 下标法
  }

  return 0;
}

4.4 用数组名作函数参数

如果一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况

(1)形参和实参都用数组名
(2)实参用数组名,形参用指针变量
(3)实参形参都用指针变量
(4)实参为指针变量,形参为数组名

4.4.1 将数组a中n个整数按相反顺序存放

#include <stdio.h>

void inv(int x[], int n) {
    
  int i, j;
  for (i = 0, j = n - 1; i <= (n - 1) / 2; i++, j--) {
    int temp;
    temp = x[i]; // x[i]==*(x+i)
    x[i] = x[j]; // x[j]==*(x+j)
    x[j] = temp;
  }
}

int main(int argc, char *argv[]) {
  int a[10];

  for (int i = 0; i <= 9; i++)
    scanf("%d", &a[i]);

  inv(a, 10);
  for (int i = 0; i <= 9; i++)
    printf("%d,", a[i]);

  return 0;
}

在这里插入图片描述

4.4.2 用实参指针改写4.4.1

#include <stdio.h>

void inv(int *x, int n) {

  int i, j;
  for (i = 0, j = n - 1; i <= (n - 1) / 2; i++, j--) {
      
    // int temp;
    // temp = x[i]; // x[i]==*(x+i)
    // x[i] = x[j]; // x[j]==*(x+j)
    // x[j] = temp;

    int temp;
    temp = *(x + i);
    *(x + i) = *(x + j);
    *(x + j) = temp;


  }
}

int main(int argc, char *argv[]) {
  int a[10];

  for (int i = 0; i <= 9; i++)
    scanf("%d", &a[i]);

  int *p;
  p = a; // 指向数组首地址
  // p = &a[0];
  inv(p, 10);

  for (int i = 0; i <= 9; i++)
    printf("%d,", a[i]);

  return 0;
}

4.4.3 用选择法对10个整数按由大到小顺序排序

#include <stdio.h>

void sort(int x[], int n) {
  for (int i = 0; i < n - 1; i++) {

    int p = i;
    for (int j = i + 1; j < n; j++)
      if (x[j] < x[p])
        p = j;// 更新指针

    if (p != i) {
      int temp;
      temp = x[p];
      x[p] = x[i];
      x[i] = temp;
    }
  }
}

int main(int argc, char *argv[]) {

  int a[10];
  int *p = a;

  for (int i = 0; i < 10; i++)
    scanf("%d", p++);

  p = a; // 初始化首地址
  sort(p, 10);

  for (int i = 0; i < 10; i++)
    printf("%d ", *(p + i));

  return 0;
}

在这里插入图片描述

4.5 多维数组和指针

在这里插入图片描述
在这里插入图片描述

输出二维数组有关的值

#include <stdio.h>

int main(int argc, char *argv[])
{

  int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
  printf("%x,%x\n", a, *a); //*a==a[0]

  printf("%x,%x\n", a[0], *(a + 0)); //*(a+0)==a[0]

  printf("%x,%x\n", &a[0], &a[0][0]); //&a[0]==a+0

  printf("%x,%x\n", a[1], a + 1);

  printf("%x,%x\n", &a[1][0], *(a + 1) + 0); //*(a+1)==a[1]

  printf("%x,%x\n", a[2], *(a + 2)); //*(a+2)==a[2]

  printf("%x,%x\n", &a[2], a + 2);

  printf("%d,%d\n", a[1][0], *(*(a + 1) + 0));// a+1:第一行 + 0:第一行第0个元素
  return 0;
}

在这里插入图片描述

4.5.1 指向多维数组元素的指针变量

4.5.1.1 指向数组元素的指针变量

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

#include <stdio.h>

int main(int argc, char *argv[])
{

  int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

  int *p; // 创建指针p
  for (p = a[0]; p < a[0] + 11; p++)
  {
    if ((p - a[0]) % 4 == 0) // 每四个元素换行
      printf("\n");
    printf("%d ", *p);

  }
  printf("\n");

  return 0;
}

在这里插入图片描述

4.5.1.2 指向由m个元素组成的一维数组的指针变量

输出二维数组任一行任一列的值

#include <stdio.h>

int main(int argc, char *argv[])
{

  int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

  int (*p)[4];  // p是一个指针变量,指向大小为4的一维数组

  p = a;// 指向二维数组的第一行的一维数组

  int i,j;
  scanf("%d %d",&i,&j);
  printf("%d\n", *(*(p+i)+j)); // p+i行, +j,第j列

  return 0;
}

在这里插入图片描述

4.5.2 用指向数组的指针作函数参数

一维数组名可以作为函数参数传递,多维数组名也可作函数参数传递。在用指针变量作形参以接受实参数组名传递来的地址时,有两种方法:
1、用指向变量的指针变量
2、用指向一维数组的指针变量

4.5.2.1 案例一

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

#include <stdio.h>

float average(float *p, int n)
{

  float *start;
  float sum = 0.0;

  for (start = p; p <= start + n - 1; p++)
  { // 将指针p赋给start指针,start+n-1:一共的元素

    sum += *p; // 统计分数和
  }
  return sum / n; // 返回平均分
}




void search(float (*p)[4], int n)
{ // p是一个指针变量,指向大小为4的一维数组

  for (int i = 0; i < 4; i++)
  {
    printf("%f ", *(*(p + n) + i)); // p+n 第n行 +i 第i列
  }
  printf("\n");
}



int main(int argc, char *argv[])
{

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

  printf("average=%f\n", average(*score, 12)); //*score==score[0]

  search(score, 2); // 行标是2的人的分数

  return 0;
}

在这里插入图片描述

4.5.2.2 案例二

在上题基础上,查找有一门以上课程不及格的学生,输出他们的全部课程的成绩

#include <stdio.h>

void search(float (*p)[4], int n) { // p是一个指针变量,指向大小为4的一维数组

  for (int i = 0; i < n; i++) {

    int flag = 0;
    for (int j = 0; j < 4; j++) {
      if (*(*(p + i) + j) < 60.0) {
        flag = 1;
        break;
      }
    }


    // 打印
    if (flag == 1) {
      printf("%d:\n",i);
      for (int j = 0; j < 4; j++) {
        printf("%f ", *(*(p + i) + j)); // 第几行第几列
      }
      printf("\n");
    }
  }
}

int main(int argc, char *argv[]) {

  float score[3][4] = {{55, 67, 70, 60}, {50, 87, 90, 81}, {90, 99, 100, 98}};
  search(score, 3); // 行标是2的人的分数

  return 0;
}

在这里插入图片描述

4.6 字符串和指针

在C程序中,可以用两种方法访问一个字符串

4.1 用字符数组存放一个字符串,然后输出该字符串。

1、定义一个字符数组,对它初始化,然后输出该字符串
#include <stdio.h>

int main(int argc, char *argv[])
{

  char string[] = "I love China!"; // !后面会自动加一个\0

  printf("%s\n", string);

  //string++;

  return 0;
}

4.2 用字符指针指向一个字符串

4.2.1 定义字符指针

#include <stdio.h>

int main(int argc, char *argv[])
{

  // char string[] = "I love China!"; // !后面会自动加一个\0
  char *string = "I love China!";
  printf("%s\n", string);

  return 0;
}

4.2.2 将字符串a赋值为字符串b

#include <stdio.h>

int main(int argc, char *argv[])
{
  int i;
  char a[] = "I am a boy.", b[20];
  for (i = 0; *(a + i) != '\0'; i++)
  {
    *(b + i) = *(a + i);
  }

  *(b + i) = '\0'; // 字符串结束标识

  for (i = 0; b[i] != '\0'; i++)
    printf("%c", b[i]);

    
  printf("\n");
  printf("%s\n", b);

  return 0;
}

在这里插入图片描述

4.2.3 用指针变量来处理例4.2.2问题

#include <stdio.h>

int main(int argc, char *argv[])
{
  int i;
  char a[] = "I am a boy.", b[20];

  char *p, *q;

  for (p = a, q = b; *p != '\0'; p++, q++) // p,q分别指向数组a[]和b[]第一个元素
  {
    *q = *p;
  }

  *q = '\0';
  q = b; // 重新初始化

  printf("%s\n", q);

  return 0;
}

在这里插入图片描述

4.2.4 字符指针作为函数参数

将一个字符串从一个函数传递到另一个函数,可以用地址传递的方法,即用字符数组名作参数,也可以用指向字符的指针变量作参数。在被调用的函数中可以改变字符串的内容,在主调函数中可以得到改变了的字符串。

用函数调用实现字符串的复制

4.2.4.1 用字符数组作参数

#include <stdio.h>

void copy_string(char from[], char to[])
{

  int i = 0;
  while (from[i] != '\0')
  {
    to[i] = from[i];
    i++;
  }
  to[i] = '\0';
}

int main(int argc, char *argv[])
{
  char a[] = "I am a teacher.";
  char b[] = "You are student.";

  printf("string a=%s string b=%s\n", a, b);
  printf("copy string a to string b:\n");
  copy_string(a, b);
  printf("string a=%s string b=%s\n", a, b);

  return 0;
}

在这里插入图片描述

4.2.4.2 形参用字符指针变量

#include <stdio.h>

void copy_string(char *from, char *to)
{

  // 方式一
  //   for (; *from != '\0'; from++,to++)
  //   {
  //     *to = *from;
  //   }
  // *to = '\0';

  // 方式二
  // while(*from != '\0'){
  //   *to = *from;
  //   from++;
  //   to++;
  // }

  // 方式三
  // while (*from != '\0')
  // {
  //   *to++ = *from++;
  // }

  // *to = '\0';

  // 方式四
  while(*to++ = *from++);

}

int main(int argc, char *argv[])
{
  char a[] = "I am a teacher.";
  char b[] = "You are student.";

  printf("string a=%s string b=%s\n", a, b);
  printf("copy string a to string b:\n");

  char *from = a;
  char *to = b;

  copy_string(from, to);
  printf("string a=%s string b=%s\n", a, b);

  return 0;
}

在这里插入图片描述

4.2.5 使用字符指针变量和字符数组的讨论

虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们两者之间是有区别的,不应混为一谈,主要有以下几点。
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址,决不是将字符串放到字符指针变量中。
(2)赋值方式。
(3)对字符指针变量赋初值。
(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址,也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。
(5)指针变量的值是可以改变的。

4.2.5.1 改变指针变量的值

int main(int argc, char *argv[])
{
  char *a = "I love China!";
  a = a + 7;

  // char a[] = "I love China!";
  // a = a + 7;

  printf("%s", a);

  return 0;
}

4.2.5.2 用带下标的字符指针变量引用字符串中的字符

用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。

int main(int argc, char *argv[])
{


  char *a = "I love China!";
  printf("The sixth character is %c\n", *(a + 5)); //a[5]==*(a+5)

  for (int i = 0; *(a + i) != '\0'; i++)
    printf("%c", a[i]);
  printf("\n");

  char *format = "a=%d,b=%d\n";

  int aa = 1, bb = 2;
  printf(format, aa, bb);

  return 0;
}

在这里插入图片描述

五、指向函数的指针

5.1 函数指针变量调用函数

可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。

5.1.1 求a和b中的大者

#include <stdio.h>

int max(int a, int b)
{
  if (a > b)
    return a;
  else

    return b;
}

int min(int a, int b)
{
  if (a > b)
    return b;
  else
    return a;
}


int main(int argc, char *argv[])
{

  int a, b;
	scanf("%d %d", &a, &b);
  printf("max=%d\n", max(a, b));

  int (*p)(int,int); // (*p):指针变量---指向函数的类型--->(int,int):函数形参类型

  p = max; // 指向函数首地址
  printf("max=%d\n", (*p)(a, b));

  
  return 0;
}

在这里插入图片描述

说明
(1)指向函数的指针变量的一般定义形式为:数据类型 (*指针变量名) (函数参数表列);
(2)函数的调用可以通过函数名调用,也可以通过函数指针调用(即用指向函数的指针表量调用)。
(3)“int (*p)(int,int);”表示定义一个指向函数的指针变量p,它不是固定指向哪一个函数的,而是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。
(4)在给函数指针变量赋值时,只需给出函数名而不必给出参数。
(5)用函数指针变量调用函数时,只需将(*p)代替函数名即可,在(*p)之后的括号中根据需要写上实参。
(6)对指向函数的指针变量,像p+n、p++等运算是无意义的。

5.2 指向函数的指针做函数参数

函数指针变量通常的用途之一是把指针作为参数传递到其他函数。函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。

5.2.1 案例:函数process,在调用它的时候,每次实现不同的功能

设一个函数process,在调用它的时候,每次实现不同的功能。输入a和b两个数,第一调用process时找出a和b中大者,第二次找出其中小者,第三次求a与b之和

#include <stdio.h>

int max(int a, int b)
{
  if (a > b)
    return a;
  else

    return b;
}

int min(int a, int b)
{
  if (a > b)
    return b;
  else
    return a;
}

int add(int a, int b)
{

  return a + b;
}

void process(int a, int b, int (*p)(int, int))  // 指向函数的指针做函数参数
{

  printf("%d\n", (*p)(a, b));
}

int main(int argc, char *argv[])
{

  int a, b;
  scanf("%d %d", &a, &b);

  process(a, b, max);
  process(a, b, min);
  process(a, b, add);

  return 0;
}

在这里插入图片描述

5.3 返回指针值的函数:类型名 * 函数名(参数表列);

一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。其概念与以前类似,只是返回的值的类型是指针类型而已。
这种返回指针值的函数,一般定义形式为
		类型名 * 函数名(参数表列);

5.3.1 指针函数来实现案例

有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号后,能输出该学生的全部成绩。用指针函数来实现

#include <stdio.h>

int *search(int n, int (*p)[4]) // 一维数组指针类型
{
  return *(p + n); //*(p+n)==score[n]
}



int main(int argc, char *argv[])
{
  int score[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

  //score score[0] score[1] score[2]
  int *p = search(1, score);

  for (int i = 0; i < 4; i++)
    printf("%d ", *(p + i));

  return 0;
}

在这里插入图片描述

5.3.2 优化5.3.1

对上例中的学生,找出其中不及格课程的学生及其学生号

#include <stdio.h>

int *search(int (*p)[4])
{
  for (int i = 0; i < 4; i++)
  {
    if (*(*p + i) < 60) // 当前一维数组*p + i第i个元素
    {
      return *(p + 1); // 指向下一个一维数组指针
    }
  }

  return *p; // 一维数组
}

int main(int argc, char *argv[])
{
  int score[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

  for (int i = 0; i < 3; i++)
  {
    int *p = search(score + i);

    if (p != *(score + i)) // 行类型指针
    {
      printf("student number=%d\n", i);

      for (int j = 0; j < 4; j++)
        printf("%d ", *(*(score + i) + j));
      printf("\n");
    }
  }
  return 0;
}

在这里插入图片描述

5.4 指针数组和指向指针的指针

一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为

	类型名 * 数组名[数组长度];
	例如:int * p[4];

5.4.1 指针数组案例

将若干字符串按字母顺序输出

#include <stdio.h>
#include <string.h>

void sort(char *name[], int n) // 字符指针数组类型
{

  for (int i = 0; i < n - 1; i++) // 选择法,n个元素需要n-1次排序
  {

    int p = i;
    for (int j = i + 1; j < n; j++)
    {
      if (strcmp(name[j], name[p]) < 0)
      {
        p = j; // 改变指针的位置
      }

    }

    if (p != i)
    {
      char *temp;
      temp = name[i];
      name[i] = name[p];
      name[p] = temp;
    }
  }
}

void print(char *name[], int n)
{

  for (int i = 0; i < n; i++)
  {
    printf("%s\n", name[i]);
  }
}




int main(int argc, char *argv[])
{
  char *name[5] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"};


  sort(name, 5);
  print(name, 5);
  return 0;

}

在这里插入图片描述

5.4.2 指向指针的指针案例

1、使用指向指针的指针

#include <stdio.h>

int main(int argc, char *argv[])
{
  char *name[5] = {"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"}; // 字符指针的数组

  char **p; // 指向字符指针的指针
  // for (int i = 0; i < 5; i++)
  // {
  //   p = name + i; // 字符指针的指针
  //   printf("%s\n", *p); // 第i个元素
  // }

  p = name + 1;
  printf("%c\n",**p);
  printf("%c\n", *(*p+1)); // *p--->name[1]

  return 0;
}

在这里插入图片描述

2、指针数组的元素指向整型数据

#include <stdio.h>

int main(int argc, char *argv[])
{
  int a[5] = {1, 2, 3, 4, 5};

  int *num[5] = {&a[0], &a[1], &a[2], &a[3], &a[4]}; // 指针数组

  int **p; // 指向指针的指针

  p = num; // 指针数组的第一个元素

  for (int i = 0; i < 5; i++)
  {
    printf("%d ", **p);
    p++;
  }

  

  return 0;
}

在这里插入图片描述

5.4.3 指针数组作main函数的形参

指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式:void main()括号中是空的。实际上,main函数可以有参数,例如:void main(int argc, char * argv[])

5.4.3.1

#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  for (int i = 0; i < argc; i++)
  {
    printf("%s\n", argv[i]);
  }
  
  getchar();

  return 0;
}

在这里插入图片描述

六、有关指针的数据类型和指针运算的小结

6.1 有关指针的数据类型的小结

在这里插入图片描述

6.2 指针运算小结

(1)指针变量加(减)一个整数

#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a[5] = {1, 2, 3, 4, 5};
  int *p;
  p = a;
  //printf("%x\n",*p);
  printf("%x\n", p);
  p++;
  printf("%x\n", p);

  return 0;

  
}
(2)指针变量赋值
#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a[5] = {1, 2, 3, 4, 5};
  int *p;
  p = a;
  
  int i = 5;
  p = &i;


  printf("%d\n",*p);

  return 0;
}

在这里插入图片描述
(3)指针变量可以有空值,即该指针变量不指向任何变量

#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a[5] = {1, 2, 3, 4, 5};
  int *p;
  p = NULL;

  // if(p == NULL){

  // }else
  // {
  //   /* code */
  // }

  printf("%x\n", p);

  return 0;
}
(4)两个指针变量可以相减
#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a[5] = {1, 2, 3, 4, 5};
  int *p1, *p2;

  p1 = &a[0];
  p2 = &a[1];

  printf("p2-p1 = %d\n", (p2-p1));

  return 0;
}
(5)两个指针变量比较
#include <stdio.h>

int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a[5] = {1, 2, 3, 4, 5};
  int *p1, *p2;

  p1 = &a[0];
  p2 = &a[4];

  printf("p2==p1 = %d\n",  p2==p1);

  return 0;
}

在这里插入图片描述

6.3 void指针类型

在这里插入图片描述

#include <stdio.h>
#include <malloc.h>


int main(int argc, char *argv[]) // *argv[]:指针数组
{

  char *string;
  string = NULL;

  string = (char *)malloc(5);

  if (string == NULL)
    printf("Insufficient memory available\n");
  else
  {

    *(string + 0) = 'a';
    *(string + 1) = 'b';
    *(string + 2) = 'c';
    *(string + 3) = 'd';
    *(string + 4) = '\0';

    printf("%s\n", string);

    free(string);
    printf("Memory freed\n");
  }

  return 0;
}

在这里插入图片描述

七、位运算

在这里插入图片描述

7.1 位运算符和位运算

在这里插入图片描述

7.1.1 按位与运算符“&”

在这里插入图片描述

7.1.2 按位或“|”

在这里插入图片描述

7.1.3 异或运算符:“^”

在这里插入图片描述
在这里插入图片描述

#include <malloc.h>
#include <stdio.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a = 3;
  int b = 4;
  a = a ^ b;
  b = b ^ a;
  a = a ^ b;

  printf("a=%d b=%d\n", a, b);

  return 0;
}

在这里插入图片描述

7.1.4 取反运算符“~”

在这里插入图片描述

  int a = 122388;
  int c = ~a;
  
  printf("a=%d\n", c);

7.1.5 左移运算符:“<<”

在这里插入图片描述

#include <malloc.h>
#include <stdio.h>

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  int a = 2;
  int c =a<<1;
  
  printf("a=%d\n", c);

  return 0;
}

在这里插入图片描述

7.1.6 右移运算符:“>>”

在这里插入图片描述

7.1.7 位运算赋值运算符

在这里插入图片描述

  int a = 0x80000001;
  a = a>>1;
  a>>=1;

7.1.8 不同长度的数据进行位运算

在这里插入图片描述

7.1.9 位段

实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。
向一个字节中的一个或几个二进制位赋值和改变它的值,可以用以下两种方法:
(1)可以人为地将一个整型变量data分为几部分。
(2)段位结构体
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这样以位为单位的成员称为“位段”或称“位域”。利用位段能够用较少的位数存储数据。例如:

struct packed_data
		{
			unsigned a:2;
			unsigned b:6;
			unsigned c:4;
			unsigned d:4;
			int i;
		} data;
#include <malloc.h>
#include <stdio.h>

struct packed_data
{

  unsigned int a : 2; // 占用两个二进制位
  unsigned int b : 3;
  unsigned int c : 4;
   
  int i;

}data;
// 主函数
int
main(int argc, char *argv[]) // *argv[]:指针数组
{

  data.a = 3;
  data.b = 7;
  data.c =15;
  data.i = 2345678;
  printf("%d %d %d %d\n", data.a, data.b, data.c, data.i);

  return 0;
}

在这里插入图片描述
说明
在这里插入图片描述
案例
1、编写一个函数getbits,从一个16位的单元中取出某几位(即该几位保留原值,其余位为0)。函数调用形式为getbits(value, n1, n2),value为该16位(2个字节)中的数据值,n1为欲取出的起始位,n2为欲取出的结束位,例如:getbits(0101675,5,8)表示对八进制101675这个数,取出它的从左面起第5位到第8位。

#include <malloc.h>
#include <stdio.h>

unsigned short getbits(unsigned short int value, int n1, int n2)
{
  return (unsigned short int)(value << (n1 - 1)) >> (16 - (n2 - n1 + 1));
  //00000000 00000000 00000000 10000011
}

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{

  unsigned short int a;

  printf("%x\n", getbits(0101675, 5, 8));

  return 0;
}

在这里插入图片描述

2、 编写一函数用来实现左右循环移位。函数名为move,调用方法为move(value, n),其中value为要循环位移的数,n为位移的位数。例如,n<0表示为左移;n>0为右移。n=4表示要右移4位;n=-3表示要左移3位。

#include <malloc.h>
#include <stdio.h>
unsigned int move(unsigned int value, int n)
{
  unsigned int t1, t2;
  if (n > 0)
  {
    t1 = value << (32 - n);
    t2 = value >> n;
  }
  else
  {
    t1 = value >> (32 + n); // 负数
    t2 = value << (-n);
  }
  return t1 | t2;

}

// 主函数
int main(int argc, char *argv[]) // *argv[]:指针数组
{
  unsigned int a;
  a = 0x12345678;
  printf("%x\n", a);
  printf("right>>%x\n", move(a, 4));
  printf("left<<%x\n", move(a, -4));

  return 0;
}

在这里插入图片描述

发布了213 篇原创文章 · 获赞 303 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/Jiajikang_jjk/article/details/88974880