【重拾C语言】五、模块化程序设计——函数(定义、调用、参数传递、结果返回、函数原型;典例:打印字符图形、验证哥德巴赫猜想)_QomolangmaH的博客-CSDN博客
https://blog.csdn.net/m0_63834988/article/details/133580009?spm=1001.2014.3001.5501 前文介绍了函数的基础知识,包括如何定义函数、函数的调用形式和过程、函数结果的返回以及函数原型的使用等。本文将再论函数,主要介绍指针、数组、结构体等作参数;函数值返回指针、结构体,以及C语言作用域相关知识。
C语言只有一种参数类别——值参。值参意味着:
#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;
}
一般意义上,如果函数的形参是指针类型,对应调用时,相应实参也应是指针类型表达式
#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;
}
输出:
使用指针作为形参来传递实参的地址。在函数内部,通过解引用指针并修改指针所指向的值,实现了对实参的修改。
在C语言中,数组名实际上是一个指针,表示数组首元素的地址。因此,当将数组名作为实参传递给函数时,实际上传递的是数组名的指针值。
在函数调用时,数组名作为实参传递给函数的形参,只传递了数组名的值,也就是数组的首地址。函数内部并不会为形参开辟数组的存储空间,而只会为形参开辟一个指针变量的空间,用于存储传递进来的实参数组的地址。
#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位系统上),而不是数组的实际大小。这是因为在函数调用过程中,只传递了数组名的指针值,而不是整个数组的值。
如上述代码所示,数组作为形参时,可以省略数组形式参数最外层的尺寸
错误示例:
void printMatrix(int matrix[][], int rows)
void printMatrix(int matrix[3][], int rows)
void printMatrix(int matrix[3][][3], int rows)
正确示例:
#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;
}
#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
结构体,包含两个整型成员变量x
和y
。printPoint
函数,接收一个Point
类型的结构体作为参数,并在函数内部打印结构体的坐标值。main
函数中,创建一个名为point
的Point
结构体变量,并初始化其x
和y
成员变量的值。然后,调用printPoint
函数,将point
作为参数传递给它。#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
函数,将&point
(point
的地址)作为参数传递给它函数可以返回指针作为其返回值,这样可以在函数外部访问函数内部创建的变量或数据。
函数返回类型不允许是数组类型和函数类型、共用体类型,除此之外允许一切类型, 当然允许指针类型,带回指针值的函数的函数定义说明符形式是:
类型名 *函数名( 形参列表 )
#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;
}
输出:
函数的计算结果可能是一个结构体值。在C语言中,有两种途径能够把该结构体值通过函数名字带回到主调函数。
#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;
}
#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()
函数释放了动态分配的内存,以避免内存泄漏。#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;
}
作用域是指在程序中标识符有效的区域。在C语言中,每个源程序编译单位(例如源文件),每个函数定义、函数原型以及复合语句都构成一个作用域区域。在一个标识符的作用域内,可以使用该标识符,并且使用的是相应声明的标识符。这意味着在不同的作用域中可以使用相同名称的标识符,因为它们处于不同的作用域,互相之间不会产生冲突。
在函数之外定义的标识符具有文件作用域,它们在整个源文件中可见,在文件中的任何位置都可以使用这些标识符。
在函数内部定义的标识符具有函数作用域,它们只在函数内部可见,在函数外部无法使用这些标识符。
在复合语句(代码块)内部定义的标识符具有块作用域,它们只在该代码块内可见。当代码块执行完毕后,其中定义的标识符就不再可见。
函数原型中声明的标识符具有函数原型作用域,它们只在函数原型所在的作用域中可见。函数原型作用域主要用于函数声明中的参数。
#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;
}