当前位置: 首页 > article >正文

谭浩强C语言程序设计(4) 8章(上)

注意:指针就是地址,指针p指向变量q的地址意思就是指针变量p存储的是变量q的地址。

举个栗子:

int q = 10;              //int型的变量q,初始化为10

int* p = &q;           //表示p是个指针(地址),int表示该指针存储的地址(q的地址)中存储的数据是int型的。这里的int*是一起的,是个指针类型。

*p                         //表示的是p地址里面的数据也就是10,*p = q

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

#include <cstdio> // 包含标准输入输出库

int main(){
    int n  = 10, m = 20; // 定义两个整数变量n和m,并分别赋值为10和20
    int* point_n = &n; // 定义一个int型的指针point_n,让它指向变量n的地址
    int* point_m = &m; // 定义一个int型的指针point_m,让它指向变量m的地址

    // 打印变量n的地址
    printf("the address of n is: %p\n", (void*)point_n);
    // 打印变量m的地址
    printf("the address of m is: %p\n", (void*)point_m);

    // 通过指针解引用获取n和m的值,并计算它们的和
    int sum = *point_n + *point_m;
    // 打印n和m的和
    printf("n+m=%d\n", sum);

    // 检查通过指针解引用得到的值是否等于变量n的值
    if (*point_n == n){
        printf("123\n"); // 如果相等,打印"123"
    }

    return 0; // 程序正常结束
}

  • printf("the address of n is: %p\n", (void*)point_n); 使用 %p 格式化符打印变量 n 的地址。为了确保地址以十六进制形式显示,将指针 point_n 强制转换为 void* 类型。

2、输入A和B两个整数,按照大小顺序输出,使用指针

#include <cstdio> // 包含标准输入输出库

int main(){
    int a, b; // 定义两个整数变量a和b
    int* point_a, * point_b, * temp; // 定义三个指针变量,分别指向a、b和临时变量

    // 提示用户输入两个数字
    puts("input the num:");
    // 从用户输入中读取两个整数,分别存储到变量a和b中
    scanf("%d,%d", &a, &b);

    // 让指针point_a指向变量a的地址
    point_a = &a;
    // 让指针point_b指向变量b的地址
    point_b = &b;

    // 比较指针point_a和point_b的大小
    if (point_a > point_b){
        // 如果point_a大于point_b,交换两个指针的值
        temp = point_a;
        point_a = point_b;
        point_b = temp;
    }

    // 打印交换后的最大值和最小值
    printf("the max is %d and the min is %d\n", *point_a, *point_b);
    // 打印变量a和b的值
    printf("the a is %d and the b is %d", a, b);

    return 0; // 程序正常结束
}

3、输入A和B两个整数,按照大小顺序输出,使用指针和函数,函数的参数是指针

错误写法:

#include <cstdio> // 包含标准输入输出库

void swap(int* point_a, int* point_b){
    int *temp;
    temp = point_a;
    point_a = point_b;
    point_b = temp;
}

int main(){
    int a, b;
    int* point_a, * point_b;

    puts("input the a,b:");
    scanf("%d,%d", &a, &b);

    point_a = &a;
    point_b = &b;

    if (a < b){
        swap(point_a, point_b);
    }

    printf("the max is %d and the min is %d\n", *point_a, *point_b);
    printf("the a is %d and the b is %d\n", a, b);

    return 0;
}

分析:

        首先,看一下swap函数的定义。函数接收两个int指针参数point_a和point_b。然后在函数内部,temp被声明为int指针类型。接下来,temp赋值给point_a,point_a赋值给point_b,point_b再赋值temp。看起来这里是想交换两个指针的指向。但是,这可能有问题。

        因为在C语言中,函数参数是按值传递的。也就是说,当调用swap(point_a, point_b)的时候,实际上是把point_a和point_b的值(也就是a和b的地址)复制给了函数内的局部变量point_a和point_b。所以函数内部交换的是这两个局部变量的指针值,而不会影响到main函数中的point_a和point_b。因此,当swap函数执行完后,main中的point_a和point_b还是原来的地址,指向a和b。所以交换并没有成功,导致输出的max和min还是原来的顺序。

        原来的swap函数交换的是指针变量本身的地址,而外部的point_a和point_b并没有改变。所以当swap结束后,main中的point_a和point_b仍然指向原来的a和b,导致输出结果错误。

        那正确的做法应该是,swap函数应该交换指针所指向的值,而不是交换指针本身。或者,如果确实要交换指针的话,应该传递指针的指针。

正确写法(值交换): 

#include <cstdio> // 包含标准输入输出库

// 定义一个函数swap,用于交换两个整数变量的值
void swap(int* point_a, int* point_b){
    int temp; // 定义一个临时变量temp,用于存储交换过程中的值
    temp = *point_a; // 保存point_a指向的值
    *point_a = *point_b; // 将point_b指向的值赋给point_a指向的变量
    *point_b = temp; // 将保存的值赋给point_b指向的变量
}

int main(){
    int a, b; // 定义两个整数变量a和b
    int* point_a, * point_b; // 定义两个指针变量,分别指向a和b

    // 提示用户输入两个数字
    puts("input the a,b:");
    // 从用户输入中读取两个整数,分别存储到变量a和b中
    scanf("%d,%d", &a, &b);

    // 让指针point_a指向变量a的地址
    point_a = &a;
    // 让指针point_b指向变量b的地址
    point_b = &b;

    // 如果a小于b,调用swap函数交换a和b的值
    if (a < b){
        swap(point_a, point_b);
    }

    // 打印交换后的最大值和最小值
    printf("the max is %d and the min is %d\n", a, b);
    // 打印变量a和b的值
    printf("the a is %d and the b is %d\n", a, b);

    return 0; // 程序正常结束
}

 正确写法(传递指针的指针):

#include <cstdio>  // 包含C标准输入输出库(用于printf/scanf)

// 交换两个指针的指向(注意参数是指针的指针)
// 参数类型:int** 表示"指向int型指针的指针"
void swap(int** point_a, int** point_b) {
    int* temp = *point_a;  // 解引用一次:获取main函数中point_a指针的值(即原&a)
    *point_a = *point_b;   // 将main函数中point_a指针的值改为point_b的值(即让point_a指向原point_b的地址)
    *point_b = temp;       // 将main函数中point_b指针的值改为temp(即原point_a的地址)
}

int main() {
    int a, b;             // 定义两个普通整型变量
    int* point_a, *point_b; // 定义两个指向整型的指针(此时尚未初始化)

    puts("input the a,b:"); // 提示用户输入
    scanf("%d,%d", &a, &b); // 读取输入,存入a和b的地址中(注意逗号分隔格式)

    // 让指针分别指向变量a和b的地址
    point_a = &a;  // point_a存储变量a的内存地址(例如0x1000)
    point_b = &b;  // point_b存储变量b的内存地址(例如0x2000)

    if (a < b) {
        // 关键操作:当a < b时,交换两个指针的指向
        // 传递指针的地址(即指针变量的内存地址)
        // &point_a 的类型是 int**(指向指针的指针)
        swap(&point_a, &point_b);
    }

    // 打印结果:此时point_a始终指向较大的值,point_b指向较小的值
    printf("the max is %d and the min is %d\n", *point_a, *point_b); // 解引用指针获取值
    // 注意:a和b的实际内存值未被修改,只是指针的指向被交换了
    printf("the a is %d and the b is %d\n", a, b); 

    return 0;
}

指针本身是一个变量,存储另一个变量的地址。而指针的指针则是存储这个指针变量的地址。

当我们需要在函数内修改外部指针的指向时(而不仅仅是修改指针指向的值),必须传递指针的地址。

4、输入三个数实现从小到大输出,使用指针

#include <cstdio>  // 包含标准输入输出库(用于printf/scanf)

// 交换两个整型变量值的函数
// 参数:p 和 q 是指向整型变量的指针(存储的是变量的地址)
void swap(int* p, int* q) {
    int temp = *p;  // 通过解引用获取p指向的变量的值,存入临时变量temp
    *p = *q;        // 将q指向的值赋给p指向的变量(直接修改变量a/b/c的值)
    *q = temp;      // 将临时变量temp的值赋给q指向的变量
}

int main() {
    int a, b, c;              // 定义三个整型变量,用于存储输入的值
    int *pointA, *pointB, *pointC; // 定义三个整型指针,将分别指向a、b、c的地址

    puts("input a,b,c:");     // 提示用户输入
    scanf("%d,%d,%d", &a, &b, &c); // 读取输入,按格式存入a、b、c的地址中(注意逗号分隔)

    // 让指针分别指向变量的地址
    pointA = &a; // pointA 存储变量a的地址(通过*pointA可以访问a的值)
    pointB = &b; // pointB 存储变量b的地址
    pointC = &c; // pointC 存储变量c的地址

    // 通过三次比较和交换,实现降序排列(a >= b >= c)
    if (b > a) {          // 如果b的值大于a
        swap(pointB, pointA); // 交换a和b的值(让a成为当前最大值)
    }
    if (c > a) {          // 如果c的值大于当前a的值
        swap(pointC, pointA); // 交换a和c的值(确保a是全局最大值)
    }
    if (c > b) {          // 如果c的值大于当前b的值
        swap(pointC, pointB); // 交换b和c的值(确保b >= c)
    }

    // 输出排序后的结果(此时a >= b >= c)
    printf("the order is:%d>%d>%d", a, b, c); 
    return 0;
}

5、有十个元素的整型数组,使用指针输出全部的元素

#include <cstdio>  // 包含标准输入输出库(用于printf函数)

int main() {
    // 定义一个包含10个整数的数组并初始化
    int arr[10] = {1345, 2, 32, 43, 54, 36, 7, 2348, 239, 10};
    
    // 定义指针指向数组首地址
    // 数组名arr本身会隐式转换为指向数组第一个元素的指针(即&arr[0])
    int* pointArr = arr;  // 等价于 int* pointArr = &arr[0];

    // 通过指针遍历数组并打印元素
    for (int i = 0; i < 10; ++i) {
        // 使用指针算术访问数组元素
        // (pointArr + i) 表示指针向后移动i个整数的位置(地址偏移量 = i * sizeof(int))
        // *(pointArr + i) 解引用得到对应位置的整数值
        printf("%d ", *(pointArr + i));
    }

    // 输出结果:1345 2 32 43 54 36 7 2348 239 10
    return 0;
}

其中for循环中的代码还能变成这个样:

    // 使用指针遍历数组的更简洁写法
    // i被定义为指针类型,初始指向数组首地址(arr == &arr[0])
    // 循环条件:i < arr+10 表示指针移动到数组末尾时停止(arr+10指向数组最后一个元素的下一个内存位置)
    // ++i 使指针移动到下一个元素的地址(注意:指针运算会自动考虑数据类型大小)
    for (int* i = arr; i < arr + 10; ++i) {
        // *i 解引用当前指针,获取指向的整数值
        printf("%d ", *i);  // 等价于 printf("%d ", arr[i - arr]);
    }

 6、使用指针将数组中的数据反转存放输出

#include <cstdio>    // 包含标准输入输出库(用于printf/puts)
#define N 10         // 定义数组长度为10的宏常量

// 数组逆序函数(直接修改原数组)
// 参数:point - 指向数组首元素的指针
void reserve(int* point) {
    // 只需要遍历数组前半部分(N/2次)
    // 若N为奇数,中间元素无需交换(例如索引4在N=9时不需要处理)
    for (int i = 0; i < N/2; ++i) {
        // 使用指针算术交换对称位置的元素
        int temp = *(point+i);            // 保存前部元素(等价point[i])
        *(point+i) = *(point+(N-i-1));    // 将后部元素赋给前部(point[i] = point[N-i-1])
        *(point+(N-i-1)) = temp;          // 将保存的前部元素赋给后部
    }
}

int main() {
    int arr[N] = {1,2,3,4,5,6,7,8,9,10};  // 初始化原始数组
    int* arrPoint = arr;                  // 创建指向数组的指针(arr本身已是首地址指针)

    reserve(arrPoint);  // 调用逆序函数(直接修改原数组)

    puts("the reserve result is:");       // 输出提示信息
    for (int i = 0; i < N; ++i) {         // 遍历逆序后的数组
        printf("%d ", *(arrPoint+i));     // 通过指针算术访问元素(等价arrPoint[i])
    }


    return 0;
}

 7、使用指针将10个数从大到小排序

#include <cstdio>       // 包含标准输入输出库(用于printf/scanf)
#define N 10            // 定义数组长度常量

// 折半插入排序函数(升序)
// 参数:point - 指向待排序数组首元素的指针
void sort(int* point) {
    int high, low, mid, temp;
    // 外层循环:从第二个元素开始处理每个元素(i从1到N-1)
    for (int i = 1; i < N; ++i) {
        temp = *(point + i);  // 保存当前待插入元素(等价point[i])
        low = 0;              // 查找范围的左边界
        high = i - 1;         // 查找范围的右边界(已排序区的最后一个元素)

        // 折半查找插入位置(二分搜索)
        while (low <= high) {
            mid = (low + high) / 2;         // 计算中间位置
            if (*(point + mid) > temp) {    // 中间元素大于待插入元素
                high = mid - 1;             // 插入位置在左半区
            } else {                        // 中间元素小于等于待插入元素
                low = mid + 1;              // 插入位置在右半区
            }
        } // 循环结束后,low即为插入位置

        // 元素后移操作:将[low, i-1]区间的元素整体右移一位
        // 注意:必须从后向前移动,避免覆盖未处理的元素
        for (int j = i - 1; j >= low; --j) {  // j从i-1递减到low
            *(point + (j + 1)) = *(point + j); // 后移元素(等价point[j+1] = point[j])
        }

        // 将暂存的元素插入正确位置
        *(point + low) = temp;  // 等价point[low] = temp
    }
}

int main() {
    int arr[N], *arrPoint;  // 声明数组和指针

    // 输入数组元素
    for (int i = 0; i < N; ++i) {
        printf("input the No.%d:", i + 1);
        scanf("%d", &arr[i]);  // 使用数组下标直接访问
    }

    // 输出原始数组
    puts("\nthe original is:");
    for (int i = 0; i < N; ++i) {
        printf("%d ", arr[i]);
    }
    printf("\n");  // 换行分隔

    arrPoint = arr;        // 指针指向数组首地址
    sort(arrPoint);        // 调用排序函数(直接修改原数组)

    // 输出排序结果(注意描述应为"sorted")
    puts("the sorted result is:");  // 原代码中"reserve"应为笔误
    for (int i = 0; i < N; ++i) {
        printf("%d ", *(arrPoint + i));  // 通过指针算术访问元素
    }

    return 0;
}

/* 算法特性说明:
1. 时间复杂度:
   - 最好情况(已有序):O(n)
   - 平均/最坏情况:O(n²)(虽然查找插入位置为O(logn),但元素移动仍需O(n))

2. 空间复杂度:O(1)(原地排序)

3. 稳定性:
   - 是稳定排序(条件判断使用 > 而不是 >=,相等元素保持原顺序)

4. 指针操作要点:
   - *(point + i) 完全等价于 point[i]
   - 指针算术自动考虑数据类型大小(int类型每次+1移动4字节)
*/

其中折半排序还能写为:

void sort(int* point){
    int high,low,mid,temp;
    for (int i = 1; i < N; ++i) {
        temp = point[i];
        low = 0;
        high = i-1;
        while(low <= high){
            mid = (low+high)/2;
            if (point[mid] > temp){
                high = mid -1;
            } else{
                low = mid +1;
            }
        }
        for (int j = i-1; j>=low; --j) {
            point[j+1] = point[j];
        }
       point[low] = temp;
    }
}

 8、使用指针输出二维数组的全部元素

错误写法:

#include <cstdio>
#define M 3
#define N 4



int main(){
    int arr[M][N],* point;
    for (int i = 0; i < M; ++i) {
        printf("input the line%d:",i+1);
        for (int j = 0; j < N; ++j) {
            printf("input the No.%d",j+1);
            scanf("%d",&arr[i][j]);
        }
    }
    point = arr[0];

    for (int i = 0; i < M; ++i) {
        for (int j = 0; j < N; ++j) {
            printf("%d",point[i][j]);
        }
    }
}

问题出在 printf("%d",point[i][j]);

        point是一个int*类型的指针,而point[i][j]这样的写法实际上是将point视为一个指向数组的指针数组,这在语法上是错误的。因为point被赋值为arr[0],即第一行的首地址,它是一个一维数组的指针。因此,直接使用point[i][j]会导致编译错误,因为point是一级指针,无法用两个下标访问。

        正确的做法应该是将二维数组当作一维数组来访问,或者调整指针的类型。例如,可以通过计算偏移量来访问元素:*(point + i*N + j)。或者,将point声明为指向数组的指针,即int (*point)[N],这样point[i][j]就可以正确访问二维数组的元素。

#include <cstdio>
#define M 3
#define N 4

int main() {
    int arr[M][N];   // 定义3行4列的二维数组
    int* point;      // 定义整型指针(将用于模拟一维数组访问)

    // 输入二维数组数据
    for (int i = 0; i < M; ++i) {
        printf("Input line %d:\n", i+1);
        for (int j = 0; j < N; ++j) {
            printf("  Enter element %d: ", j+1);
            scanf("%d", &arr[i][j]);  // 标准二维数组下标输入
        }
    }

    point = &arr[0][0];  // 让指针指向数组第一个元素的地址

    // 通过指针遍历输出二维数组
    printf("\nArray contents:\n");
    for (int i = 0; i < M; ++i) {
        for (int j = 0; j < N; ++j) {
            printf("%d ", point[i*N+j]);
        }
        printf("\n");  // 每行输出后换行
    }

    return 0;
}

首先这里的point存储的就是二维数组的首地址也就是arr[0][0]的地址,要计算arr[i][j]元素的值需要先找到该元素的位置,因为二维数组在内存中是一一维数组的形式存放的:

9、输出二维数组的随便一个行或者列的元素

#include <cstdio>  // 包含标准输入输出库

int main() {
    // 定义一个3行4列的二维数组并初始化
    int arr[3][4] = {3,7,9,2,3,4,5,6,7,10,11,12}; // 内存按行连续存储
    int hang, lie; // 定义存储用户输入的行和列的变量

    printf("input hang and lie:\n");
    // 输入格式要求:用逗号分隔行和列(例如:"1,2")
    scanf("%d,%d", &hang, &lie);

    // 定义指向"包含4个整数的数组"的指针(行指针)
    // point+1 会跳过一行(4个int的长度)
    int (*point)[4];
    point = arr; // 将二维数组首地址赋给指针(arr退化为指向第一行的指针)

    // 通过指针访问元素:
    // 1. point + hang: 移动到目标行的行首
    // 2. *(point + hang): 解引用得到目标行的首地址(即该行的第一个元素地址)
    // 3. *(point + hang) + lie: 在目标行内偏移lie个位置
    // 4. 最终解引用得到元素值
    printf("the [%d,%d] is: %d", hang, lie, *(*(point + hang) + lie));

    return 0;
}

int *(point) [4]表示point指针指向的是有四个元素的一维数组。

#include <cstdio>

int main(){
    int a[4] = {1,2,3,4};
    int (*p)[4];
    p = &a;
    printf("%d",(*p)[2]);
}

为啥不能写为p = a呢?

        `int (*p)[4];`。这里`p`是一个指向含有4个整数的数组的指针,即数组指针。它的类型是`int(*)[4]`,也就是说,`p`存储的是一个数组的地址,而不是单个整数的地址。当对`p`进行解引用时,会得到一个`int[4]`类型的数组。

        如果尝试`p = a;`,这里`a`作为数组名,会退化为指向首元素`a[0]`的指针,即`int*`类型。此时,将一个`int*`类型的值赋给`int(*)[4]`类型的变量`p`,显然类型不匹配,编译器会报错。

        虽然`a`和`&a`的地址值相同(数组的起始地址和其首元素的地址相同),但它们的类型不同。`a`的类型是`int*`,而`&a`的类型是`int(*)[4]`。当进行指针运算时,这种类型差异会体现出来。例如,`p + 1`会根据指向的数组大小进行偏移,即增加`4 * sizeof(int)`字节,而`int*`类型的指针加1只会增加一个`int`的大小。

 10、3个学生4门成绩输出总平均分和第N个学生的成绩,使用函数指针为参数

#include <cstdio>
#define H 3
#define L 4

//计算平均成绩
float average(float (*p)[4]){  //p指针是行指针,也即是指向一维数组的指针
    float sum = 0;
    for (int i = 0; i < H; ++i) {
        for (int j = 0; j < L; ++j) {
           sum += *(*(p+i)+j);  //*(p+i)的类型是float[4]
        }
    }
    return sum/(H*L);
}

//输出某个学生的成绩
void search(float (*p)[4],int n){
    for (int i = 0; i < H; ++i) {
        if (n == i){
            for (int j = 0; j < 4; ++j) {
                printf("%3.2f ",*(*(p+i)+j));
            }
            break;
        }
    }
}

int main(){
    float score[H][L];

    for (int i = 0; i < H; ++i) {
        printf("input the No.%d's score.\n",i+1);
        for (int j = 0; j < L; ++j) {
            printf("  the No.%d score:",j+1);
            scanf("%f",&score[i][j]);
        }
        printf("\n");
    }

    float (*p)[4];
    p = score;
    float result = average(p);
    printf("The average is: %3.2f",result);
    int n;
    printf("\n");
    printf("input the some score:\n");
    scanf("%d",&n);
    search(p,n-1);
}

二维数组的内存布局

float score[3][4] = {
    {10, 20, 30, 40},  // 第0行
    {50, 60, 70, 80},  // 第1行
    {90, 100, 110, 120} // 第2行
};

内存中连续存储如下(假设每个 float 占4字节):

地址: 0x1000 → 10 (第0行第0列)
地址: 0x1004 → 20 (第0行第1列)
地址: 0x1008 → 30 (第0行第2列)
地址: 0x100C → 40 (第0行第3列)
地址: 0x1010 → 50 (第1行第0列)
地址: 0x1014 → 60 (第1行第1列)
...
地址: 0x1028 → 120 (第2行第3列)

2. 指针 p 的类型和初始值

定义指针:

float (*p)[4] = score; // p指向score的第0行
  • p 的类型是 float (*)[4],即“指向包含4个float元素的一维数组的指针”。

  • p 的初始值为 0x1000(第0行的起始地址)。

3. 分步解析 *(p + i) + j

以 i = 1(第1行),j = 2(第2列)为例:

步骤1:p + i
  • p 是行指针,p + 1 会跳过 一行(4个float元素)。

  • 计算地址:p + 1 = 0x1000 + 1 * (4 * sizeof(float)) = 0x1010

  • 结果:指向第1行的起始地址(0x1010), 也就是score[1]地址

步骤2:*(p + i)
  • *(p + 1) 得到的是第1行的数组 score[1],类型是 float[4]

  • 数组名 score[1] 退化为指向该行首元素的指针,即 &score[1][0],类型为 float*

  • 结果:得到一个指向第1行第0列元素的指针(0x1010)。

步骤3:*(p + i) + j
  • 在指针 &score[1][0] 的基础上,加上 j(即2),得到 &score[1][2]

  • 计算地址:0x1010 + 2 * sizeof(float) = 0x1010 + 8 = 0x1018

  • 结果:指向第1行第2列元素(70 的地址)。

4. 最终解引用

通过 *(*(p + i) + j) 获取值:

float value = *(*(p + 1) + 2); // 值为70

5. 对比 p[i][j]

p[i][j] 本质是语法糖,等价于 *(*(p + i) + j)

p[1][2] ⇨ *(*(p + 1) + 2) ⇨ 70

6. 常见误区

误区1:运算符优先级错误

若写成 *(p + i)[j],实际解析为 *((p + i)[j]),即:

*(p + i + j) // 完全错误!

这将导致访问错误的地址(例如 i=1, j=2 时访问第3行)。

误区2:直接使用 p + i + j
*(p + i + j) // 错误!

p 的类型是 float(*)[4]p + i + j 会跳过 i + j 行,而非元素。

 11、在10题的基础上找出一门以上成绩不合格的并输出全部的成绩

#include <cstdio>
#define H 3    // 定义行数
#define L 4    // 定义列数

// 定义一个函数,用于查找每个学生的所有科目中成绩不及格的人数多于一个的学生并输出其成绩
void search(float (*p)[4]) {
    int count = 0;   // 用于统计单个学生的不及格科目数量
    int flag = 0;    // 标志位,用于判断是否有学生不及格

    // 外层循环遍历每个学生(行)
    for (int i = 0; i < H; ++i) {
        // 内层循环遍历每个学生的所有科目(列)
        for (int j = 0; j < L; ++j) {
            // 检查当前成绩是否不及格(小于 60 分)
            if (*(*(p + i) + j) < 60) {
                count++;  // 不及格科目数加 1
            }
            // 如果当前学生的不及格科目数超过 1 个
            if (count > 1) {
                // 输出该学生的编号和所有成绩
                printf("The fail student is No.%d, His scores are: ", i + 1);
                // 遍历该学生的所有科目成绩并输出
                for (int k = 0; k < L; ++k) {
                    printf("\t%3.2f", *(*(p + i) + k));
                }
                printf("\n");  // 换行
                count = 0;    // 重置不及格科目数
                flag = 1;     // 标记有不及格的学生
                // 跳出当前学生的遍历,检查下一个学生
                break;
            }
        }
    }

    // 如果没有学生不及格
    if (flag == 0) {
        printf("No fail students.\n");
    }
}

int main() {
    // 定义一个二维数组,存储学生成绩
    float score[H][L] = {
        {10, 34, 69, 66},  // 学生 1 的成绩
        {34, 50, 89, 100}, // 学生 2 的成绩
        {67, 78, 89, 90}   // 学生 3 的成绩
    };

    // 定义一个指向二维数组的指针
    float (*p)[4] = score;

    // 调用 search 函数,处理成绩并输出结果
    search(p);

    return 0;  // 程序正常结束
}

12、通过字符指针变量输出 I love China

#include <cstdio>

int main(){
    char * str = "I love China";
    printf("%s",str);
}

str指针指向的是 ”I“ 字符,在输出str的时候,系统会自动让str+1,直到碰到字符串结束标志‘\0’。

13、 使用指针实现字符数组的复制

#include <cstdio>  // 引入标准输入输出库
#define N 20      // 定义一个宏 N,表示字符数组的最大长度为 20

int main() {
    char strA[N] = "I love China";  // 定义一个字符数组 strA,并初始化为字符串 "I love China"
    char strB[N];                  // 定义一个字符数组 strB,用于存储复制后的字符串
    int i = 0;                     // 定义一个整型变量 i,用于循环计数

    // 使用 while 循环逐字符复制 strA 到 strB
    while (*(strA + i) != '\0') {  // 判断 strA 的第 i 个字符是否为字符串结束符 '\0'
        *(strB + i) = *(strA + i); // 将 strA 的第 i 个字符复制到 strB 的第 i 个位置
        i++;                       // 计数器 i 自增 1
    }

    *(strB + i) = '\0';  // 在 strB 的末尾添加字符串结束符 '\0',确保 strB 是一个完整的字符串

    printf("%s", strB);  // 输出复制后的字符串 strB

    return 0;  // 程序正常结束
}

14、使用函数指针调用函数,输出两个整数的最大的

#include <cstdio> // 引入C++标准输入输出库,用于使用scanf和printf等函数

// 定义一个函数max,用于比较两个整数并返回较大的值
int max(int a, int b) {
    return a > b ? a : b; // 使用三元运算符,如果a大于b返回a,否则返回b
}

int main() {
    int a, b; // 声明两个整数变量a和b,用于存储用户输入的值
    puts("input the a,b"); // 提示用户输入两个整数
    scanf("%d,%d", &a, &b); // 从标准输入读取两个整数,分别存储到变量a和b中
    // 注意:输入格式要求用逗号分隔,例如:3,5

    // 声明一个函数指针p,类型为指向一个接收两个整数参数并返回整数的函数
    int (*p)(int, int);
    p = max; // 将函数指针p指向max函数

    // 调用函数指针p指向的函数(即max函数),并将a和b作为参数传递
    int c = (*p)(a, b); // (*p)(a, b)相当于max(a, b),返回较大的值,并将其存储到变量c中

    // 输出结果,显示较大的值
    printf("The max is:%d", c);
    return 0; // 主函数返回0,表示程序正常结束
}

int c = (*p)(a,b);
int c = p(a,b);
  1. int c = (*p)(a, b);

    • 这里使用了显式的解引用操作符 *

    • p 是一个函数指针,(*p) 表示显式地将 p 解引用为它所指向的函数。

    • 然后调用这个函数,传入参数 ab

    • 这种写法更清晰地表明了 p 是一个函数指针,并且需要通过解引用来调用。

  2. int c = p(a, b);

    • 这里省略了显式的解引用操作符 *

    • C语言中,当函数指针用于调用时,可以直接使用函数指针的名称(如 p),而不需要显式地解引用。

    • 编译器会隐式地将 p 解引用为它所指向的函数。

    • 这种写法更简洁,是更常用的写法。

 15、输入两个整数,用户输入1调用max,2调用min,3计算两个数的和

#include <cstdio>

int max(int a,int b){
    return a>b?a:b;
}
int min(int a,int b){
    return a>b?b:a;
}
int sum(int a,int b){
    return a+b;
}

int main(){
    int a,b,kind,result;
    puts("input the a,b");
    scanf("%d,%d",&a,&b);
    puts("output the result 1 is max,2 is min,3 is sum:");
    scanf("%d",&kind);
    int (*p)(int,int);
    if (kind == 1){
        p = max;
        printf("The max is:%d",(*p)(a,b));
    } else if (kind == 2){
        p = min;
        printf("The min is:%d",(*p)(a,b));
    } else if (kind == 3){
        p = sum;
        printf("The sum is:%d",(*p)(a,b));
    }
}

16、a个学生,每个学生b个成绩,输入学生的学号输出该学生的全部成绩,要求函数的返回值是指针

#include <cstdio>
#define M 3  // 定义学生数量为3
#define N 4  // 定义每门成绩数量为4

// 定义一个函数,用于根据索引找到特定学生的成绩
float* getStudent(float (*p)[N], int index) {
    return *(p + index);  // 返回指向该学生的成绩数组的指针
}

int main() {
    float scores[M][N];  // 定义一个二维数组,存储M个学生每门成绩的N个分数
    int index = 0;        // 存储用户输入的索引

    // 循环输入每个学生每门成绩
    for (int i = 0; i < M; ++i) {
        printf("input the No.%d student's scores:\n", i + 1);  // 提示用户输入第i+1个学生的成绩
        for (int j = 0; j < N; ++j) {
            printf("input the No.%d score:", j + 1);  // 提示用户输入第j+1门成绩
            scanf("%f", &scores[i][j]);  // 读取用户输入,并存储到相应位置
        }
    }

    // 提示用户输入要查看的学生索引
    puts("input the index of student:");
    scanf("%d", &index);  // 读取用户输入的索引

    // 调用函数,获取指定学生的成绩指针
    float *p = getStudent(scores, index - 1);  // 将用户输入的索引转换为0-based索引

    printf("the student's score are: ");  // 输出提示信息
    // 循环输出指定学生的成绩
    for (int i = 0; i < N; ++i) {
        printf("%3.2f ", p[i]);  // 以两位小数的格式输出成绩
    }

    return 0;  // 主函数正常结束
}

17、在16题基础上输出不及格的课程和学生

#include <cstdio>

#define M 3  // 定义学生数量为3
#define N 4  // 定义每门成绩数量为4

// 定义一个函数,用于查找是否含有不及格成绩的学生
float* search(float (*p)[N]) {
    for (int i = 0; i < N; ++i) {
        // *p 表示当前学生的成绩数组的地址,*p + i 表示当前学生的第i门成绩的地址
        // *(*p + i) 表示当前学生的第i门成绩
        if (*(*p + i) < 60) {  // 如果某门成绩小于60(不及格),返回该学生的成绩数组的地址
            return *p;
        }
    }
    // 如果全部成绩都及格,返回空指针
    return NULL;
}

int main() {
    float scores[M][N];  // 定义一个二维数组,存储M个学生每门成绩的N个分数
    int index = 0;       // 未使用的变量,可能只是为了占位

    // 输入每个学生每门成绩
    for (int i = 0; i < M; ++i) {
        printf("Input the No.%d student's scores:\n", i + 1);  // 提示用户输入第i+1个学生的成绩
        for (int j = 0; j < N; ++j) {
            printf("Input the No.%d score: ", j + 1);  // 提示用户输入第j+1门成绩
            scanf("%f", &scores[i][j]);  // 读取用户输入,并存储到对应位置
        }
    }

    bool flag = true;  // 标记是否有不及格的学生

    // 检查每个学生是否有不及格的课程
    for (int i = 0; i < M; ++i) {
        float* point = search(scores + i);  // 调用search函数,传入第i个学生的成绩数组地址
        if (point != NULL) {  // 如果找到不及格的学生
            flag = false;  // 修改标记为false,表示存在不及格的学生
            printf("No.%d student has fail score. They are: ", i + 1);  // 输出提示信息
            for (int j = 0; j < N; ++j) {
                printf("%3.2f ", point[j]);  // 输出该学生的全部成绩,保留两位小数
            }
            printf("\n");  // 换行
        }
    }

    // 如果所有学生都及格,输出提示信息
    if (flag) {
        printf("No students have fail scores!\n");
    }

    return 0;  // 主函数正常结束
}

 18、使用指针数组的形式,将输入的若干字符串按照字母顺序从小到大输出

#include <cstdio>
#include <cstring>
#include <cstdlib>
#define N 5

// 折半插入排序函数
void sort(char* p[]) {
    int low, high, mid;  // 定义折半查找的变量
    char* temp;  // 临时存储当前需要插入的元素

    for (int i = 1; i < N; ++i) {  // 从第二个元素开始,逐个插入
        low = 0;                   // 有效范围的起始索引
        high = i - 1;              // 有效范围的结束索引
        temp = p[i];               // 保存当前要插入的元素

        // 使用折半查找确定插入位置
        while (low <= high) {
            mid = (low + high) / 2;  // 计算中间位置
            if (stricmp(p[mid], temp) > 0) {  // 如果中间元素大于当前元素,调整查找范围到左半部分
                high = mid - 1;
            } else {  // 如果中间元素小于或等于当前元素,调整查找范围到右半部分
                low = mid + 1;
            }
        }

        // 将插入位置之后的元素向后移动一位,为当前元素腾出空间
        for (int j = i - 1; j >= low; --j) {
            p[j + 1] = p[j];
        }

        // 将当前元素插入到正确的位置
        p[low] = temp;
    }
}

int main() {
    char* p[N] = { "basic", "FLLOW ME", "Greate wall", "FORTRAN", "computer design" };

    // 对字符串数组进行排序
    sort(p);

    // 输出排序结果
    puts("The result is:");
    for (int i = 0; i < N; ++i) {
        printf("%s ", p[i]);  // 按顺序输出每个字符串
    }
    puts("");  // 输出换行符

    return 0;
}

strcmp() 是 C 标准库中的一个字符串比较函数,用于比较两个字符串的内容。它的定义在 <string.h> 头文件中。

strcmp() 函数逐字符比较两个字符串 str1str2,按照字典顺序(ASCII 值)进行比较,直到遇到字符串的结束符 \0 或者找到不同的字符为止。

  • 如果返回值为 0,表示两个字符串相等。

  • 如果返回值为 负数,表示 str1 小于 str2(即 str1 在字典顺序中排在 str2 之前)。

  • 如果返回值为 正数,表示 str1 大于 str2(即 str1 在字典顺序中排在 str2 之后)。

  • strcmp 是大小写敏感的。如果需要忽略大小写,可以使用 stricmp 或类似的函数。

 19、多重指针

#include <cstdio>
#define N 5

int main(){
    char* name[N] = {"basic","FLLOW ME","Greate wall","FORTRAN","computer design"};
    char** p;  // 指向地址的地址(指针的指针)
    for (int i = 0; i < N; ++i) {
        p = name + i;
        printf("%s ", *p);
    }
}
1. char* name[N]
  • 定义了一个名为 name 的数组,数组的大小为 N(此处 N=5)。

  • name 是一个 指针数组,每个元素是一个 char* 类型的指针。

  • 每个元素存储了一个字符串的起始地址。例如:

    • name[0] 是字符串 "basic" 的起始地址

    • name[1] 是字符串 "FLLOW ME" 的起始地址

    • 依此类推。

2. char** p
  • 定义了一个指向指针的指针,类型为 char**

  • char** p 表示 p 是一个指针,它指向一个 char* 类型的指针

3. for (int i = 0; i < N; ++i)
  • 这是一个循环,用于遍历数组 name 的每个元素。

4. p = name + i
  • name + i 是数组 name 的第 i 个元素的地址。

    • 例如,当 i=0 时,name + 0 指向 name[0](即字符串 "basic" 的起始地址)。

    • i=1 时,name + 1 指向 name[1](即字符串 "FLLOW ME" 的起始地址)。

  • 这里将 p 指向了 name[i] 的地址。

5. printf("%s ", *p)
  • *p 是对 p 的解引用操作,表示 p 所指向的 char* 指针。

    • 例如,如果 p 指向 name[0],则 *p 就是 name[0],即字符串 "basic" 的起始地址。

  • printf("%s ", *p) 输出了 *p 指向的字符串。

原文地址:https://blog.csdn.net/weixin_56349691/article/details/145376630
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/526497.html

相关文章:

  • deepseek R1 14b显存占用
  • 【Block总结】HWD,小波下采样,适用分类、分割、目标检测等任务|即插即用
  • 【Block总结】CAA捕获远程上下文信息,增强特征提取的能力|即插即用
  • 哈希表实现
  • 缓冲区和c库的简单实现
  • 性能优化2-删除无效引用
  • kobject、kset和ktype的关系
  • 论文阅读(七):贝叶斯因果表型网络解释遗传变异和生物学知识
  • python | OpenCV小记(一):cv2.imread(f) 读取图像操作(待更新)
  • 春晚舞台上的智能革命:中美人形机器人技术对比与发展
  • 日志2025.1.30
  • 【深度分析】DeepSeek 遭暴力破解,攻击 IP 均来自美国,造成影响有多大?有哪些好的防御措施?
  • Spring AI 与企业级应用架构的结合
  • 举例说明python单利模式的必要性
  • 数论问题80
  • floodfill算法(6题)
  • Node.js——模块化(模块的基本概念、模块化的规范、包与NPM)
  • 傅里叶分析之掐死教程
  • Zookeeper入门部署(单点与集群)
  • 《Chart.js 饼图:深度解析与最佳实践指南》