前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【重拾C语言】九、再论函数(指针、数组、结构体作参数;函数值返回指针、结构体;作用域)

【重拾C语言】九、再论函数(指针、数组、结构体作参数;函数值返回指针、结构体;作用域)

作者头像
Qomolangma
发布2024-07-30 08:52:41
1090
发布2024-07-30 08:52:41
举报
文章被收录于专栏:C语言深度学习

前言

【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)_QomolangmaH的博客-CSDN博客

https://blog.csdn.net/m0_63834988/article/details/133580009?spm=1001.2014.3001.5501 前文介绍了函数的基础知识,包括如何定义函数、函数的调用形式和过程、函数结果的返回以及函数原型的使用等。本文将再论函数,主要介绍指针、数组、结构体等作参数;函数值返回指针、结构体,以及C语言作用域相关知识。

九、再论函数

9.1 参数

9.1.1 参数的传递规则

C语言只有一种参数类别——值参。值参意味着:

  • 参数要求:赋值兼容
  • 形实结合:
    • 计算实参表达式的值;
    • 把实参值按赋值转换规则,转换成形参的类型;
    • 把转换后的实参值送入形参变量中。
  • 执行函数:
    • 值参表示形参本身,它是函数内的一个局部变量,已经与实参脱离关系(无关)了。在函数内对形参的赋值不影响实参。
  • 结束返回: 实参值无任何变化,还是调用函数之前的值。
代码语言:javascript
复制
#include <stdio.h>

void square(int num) {
    num = num * num;  // 修改形参的值
    printf("Inside the function: %d\n", num);
}

int main() {
    int number = 5;
    printf("Before the function call: %d\n", number);
    square(number);  // 调用函数并传递实参
    printf("After the function call: %d\n", number);
    
    return 0;
}
9.1.2 指针作参数

一般意义上,如果函数的形参是指针类型,对应调用时,相应实参也应是指针类型表达式

代码语言:javascript
复制
#include <stdio.h>

void square(int *num) {
    *num = (*num) * (*num);  // 修改形参指针所指向的值
    printf("Inside the function: %d\n", *num);
}

int main() {
    int number = 5;
    printf("Before the function call: %d\n", number);
    square(&number);  // 调用函数并传递实参的地址
    printf("After the function call: %d\n", number);
    
    return 0;
}

输出:

使用指针作为形参来传递实参的地址。在函数内部,通过解引用指针并修改指针所指向的值,实现了对实参的修改。

9.1.3 数组作参数

在C语言中,数组名实际上是一个指针,表示数组首元素的地址。因此,当将数组名作为实参传递给函数时,实际上传递的是数组名的指针值。

在函数调用时,数组名作为实参传递给函数的形参,只传递了数组名的值,也就是数组的首地址。函数内部并不会为形参开辟数组的存储空间,而只会为形参开辟一个指针变量的空间,用于存储传递进来的实参数组的地址。

代码语言:javascript
复制
#include <stdio.h>

void printArraySize(int arr[]) {
    printf("Size of the array inside the function: %lu\n", sizeof(arr));
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int array[] = {1, 2, 3, 4, 5};
    printf("Size of the array in the main function: %lu\n", sizeof(array));
    printArraySize(array);

    return 0;
}

输出:

将数组array作为实参传递给printArraySize函数,可以看到,在printArraySize函数内部,sizeof(arr)的结果是8(64位系统上),而不是数组的实际大小。这是因为在函数调用过程中,只传递了数组名的指针值,而不是整个数组的值。

如上述代码所示,数组作为形参时,可以省略数组形式参数最外层的尺寸

错误示例:

代码语言:javascript
复制
void printMatrix(int matrix[][], int rows)
代码语言:javascript
复制
void printMatrix(int matrix[3][], int rows)
代码语言:javascript
复制
void printMatrix(int matrix[3][][3], int rows)

正确示例:

代码语言:javascript
复制
#include <stdio.h>

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

int main() {
    int matrix[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
    int rows = sizeof(matrix) / sizeof(matrix[0]);
    
    printf("Matrix elements in the main function:\n");
    printMatrix(matrix, rows);
    
    return 0;
}
9.1.4 结构体作参数
a. 直接用结构体变量作函数参数
代码语言:javascript
复制
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void printPoint(struct Point p) {
    printf("Point coordinates: (%d, %d)\n", p.x, p.y);
}

int main() {
    struct Point point = {3, 5};

    printf("Point coordinates in the main function:\n");
    printPoint(point);

    return 0;
}
  • Point结构体,包含两个整型成员变量xy
  • printPoint函数,接收一个Point类型的结构体作为参数,并在函数内部打印结构体的坐标值。
  • main函数中,创建一个名为pointPoint结构体变量,并初始化其xy成员变量的值。然后,调用printPoint函数,将point作为参数传递给它。
  • 输出:
b. 用指向结构体变量的指针作函数参数
代码语言:javascript
复制
#include <stdio.h>

struct Point {
    int x;
    int y;
};

void printPoint(struct Point* p) {
    printf("Point coordinates: (%d, %d)\n", p->x, p->y);
}

int main() {
    struct Point point = {3, 5};

    printf("Point coordinates in the main function:\n");
    printPoint(&point);

    return 0;
}
  • printPoint函数,接收一个指向Point类型结构体的指针作为参数
  • main函数中,调用printPoint函数,将&pointpoint的地址)作为参数传递给它
  • 输出结果与方法a相同:

9.2 函数值

9.2.1 返回指针值

函数可以返回指针作为其返回值,这样可以在函数外部访问函数内部创建的变量或数据。

函数返回类型不允许是数组类型和函数类型、共用体类型,除此之外允许一切类型, 当然允许指针类型,带回指针值的函数的函数定义说明符形式是:

代码语言:javascript
复制
类型名 *函数名( 形参列表 )
代码语言:javascript
复制
#include <stdio.h>

int* getArray() {
    static int arr[] = {1, 2, 3, 4, 5};
    return arr;
}

int main() {
    int* ptr = getArray();
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(ptr + i));
    }
    
    return 0;
}

输出:

9.2.2 返回结构体值

函数的计算结果可能是一个结构体值。在C语言中,有两种途径能够把该结构体值通过函数名字带回到主调函数。

a. 返回结构体值
  • 函数的结果类型是结构体类型
  • 直接把一个结构体值带回调用函数的主程序
代码语言:javascript
复制
#include <stdio.h>

struct Point {
    int x;
    int y;
};

struct Point createPoint(int x, int y) {
    struct Point point;
    point.x = x;
    point.y = y;
    return point;
}

int main() {
    struct Point p = createPoint(3, 4);
    
    printf("Point coordinates: (%d, %d)\n", p.x, p.y);
    
    return 0;
}
b. 返回结构体指针
  • 函数的结果类型是指向结构体类型变量的指针类型
代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>

struct Point {
    int x;
    int y;
};

struct Point* createPoint(int x, int y) {
    struct Point* point = malloc(sizeof(struct Point));
    point->x = x;
    point->y = y;
    return point;
}

int main() {
    struct Point* p = createPoint(3, 4);
    
    printf("Point coordinates: (%d, %d)\n", p->x, p->y);
    
    free(p);
    
    return 0;
}
  • 函数createPoint()接受两个参数,并动态分配内存来创建一个Point类型的结构体变量。然后,它将给定的坐标值分配给结构体的成员,并返回指向该结构体的指针。
  • main()函数中,调用createPoint()函数来创建一个Point结构体,并使用指针访问结构体的成员来打印坐标值。最后,使用free()函数释放了动态分配的内存,以避免内存泄漏。

9.3 作用域

9.3.1 局部量和全局量
  • 局部变量(Local Variables)是在函数内定义的变量,包括形参。它们的作用范围限定在所属的函数内部。另外,定义在复合语句内部的变量的作用范围则限定在该复合语句内部。
  • 全局变量(Global Variables)则是在函数以外定义的变量,它们不从属于任何特定的函数。全局变量的作用范围从定义处开始,一直延伸到整个源文件的结束,包括各个函数。
代码语言:javascript
复制
#include <stdio.h>

// 全局变量
int globalVariable = 10;

void myFunction()
{
    // 局部变量
    int localVar = 20;
    
    printf("局部变量:%d\n", localVar);
    printf("全局变量:%d\n", globalVariable);
}

int main()
{
    myFunction();
    
    // 尝试访问局部变量和全局变量
    // 这里不能访问局部变量localVar,会导致编译错误
    printf("全局变量:%d\n", globalVariable);
    
    return 0;
}
9.3.2 作用域

作用域是指在程序中标识符有效的区域。在C语言中,每个源程序编译单位(例如源文件),每个函数定义、函数原型以及复合语句都构成一个作用域区域。在一个标识符的作用域内,可以使用该标识符,并且使用的是相应声明的标识符。这意味着在不同的作用域中可以使用相同名称的标识符,因为它们处于不同的作用域,互相之间不会产生冲突。

a. 文件作用域(全局作用域)

在函数之外定义的标识符具有文件作用域,它们在整个源文件中可见,在文件中的任何位置都可以使用这些标识符。

b. 函数作用域

在函数内部定义的标识符具有函数作用域,它们只在函数内部可见,在函数外部无法使用这些标识符。

c. 块作用域

在复合语句(代码块)内部定义的标识符具有块作用域,它们只在该代码块内可见。当代码块执行完毕后,其中定义的标识符就不再可见。

d. 函数原型作用域

函数原型中声明的标识符具有函数原型作用域,它们只在函数原型所在的作用域中可见。函数原型作用域主要用于函数声明中的参数。

e. 代码示例
代码语言:javascript
复制
#include <stdio.h>

// 文件作用域(全局作用域)
int globalVariable = 10;

void function1()
{
    // 函数作用域
    int localVariable = 20;
    printf("Local variable: %d\n", localVariable);
    printf("Global variable: %d\n", globalVariable);
}

void function2()
{
    // 函数作用域
    int localVariable = 30;
    printf("Local variable: %d\n", localVariable);
    printf("Global variable: %d\n", globalVariable);
}

int main()
{
    // 块作用域
    {
        int blockVariable = 40;
        printf("Block variable: %d\n", blockVariable);
    }

    function1();
    function2();

    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 九、再论函数
    • 9.1 参数
      • 9.1.1 参数的传递规则
      • 9.1.2 指针作参数
      • 9.1.3 数组作参数
      • 9.1.4 结构体作参数
    • 9.2 函数值
      • 9.2.1 返回指针值
      • 9.2.2 返回结构体值
    • 9.3 作用域
      • 9.3.1 局部量和全局量
      • 9.3.2 作用域
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档