C 中的指针 - 函数
0. 按值调用与按引用调用
看看下面的程序。
#include <stdio.h>
int multiply(int x, int y){
int z;
z = x * y;
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(x,y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
该函数multiply()
接受两个int
参数并将它们的乘积返回为int
。在函数调用中,我们将实际参数和(of )multiply(x,y)
的值传递给。x
y
main()
multiply()
实际参数的值被传递或复制到形式参数 x
and y
( of multiply()
)。和ofx
与的不同。这可以通过打印他们的地址来验证。y
multiply()
main()
#include <stdio.h>
int multiply(int x, int y){
printf("Address of x in multiply() = %d\n", &x);
printf("Address of y in multiply() = %d\n", &y);
int z;
z = x * y;
return z;
}
main(){
int x = 3, y = 5;
printf("Address of x in main() = %d\n", &x);
printf("Address of y in main() = %d\n", &y);
int product = multiply(x,y);
printf("Product = %d\n", product);
}
/* Output */
Address of x in main() = 6422040
Address of y in main() = 6422036
Address of x in multiply() = 6422000
Address of y in multiply() = 6422008
Product = 15
由于我们在新位置创建了存储值,因此会消耗内存。如果我们能够在不浪费空间的情况下执行相同的任务不是更好吗?
通过引用调用可以帮助我们实现这一目标。我们将变量的地址或引用传递给不创建副本的函数。使用解引用运算符*
,我们可以访问存储在这些地址中的值。
我们也可以使用引用调用重写上面的程序。
#include <stdio.h>
int multiply(int *x, int *y){
int z;
z = (*x) * (*y);
return z;
}
main(){
int x = 3, y = 5;
int product = multiply(&x,&y);
printf("Product = %d\n", product);
/* prints "Product = 15" */
}
1. 指针作为函数参数
在本节中,我们将研究各种使用指针将int
、char
、 数组和字符串作为参数的程序。
#include <stdio.h>
void add(float *a, float *b){
float c = *a + *b;
printf("Addition gives %.2f\n",c);
}
void subtract(float *a, float *b){
float c = *a - *b;
printf("Subtraction gives %.2f\n",c);
}
void multiply(float *a, float *b){
float c = *a * *b;
printf("Multiplication gives %.2f\n",c);
}
void divide(float *a, float *b){
float c = *a / *b;
printf("Division gives %.2f\n",c);
}
main(){
printf("Enter two numbers :\n");
float a,b;
scanf("%f %f",&a,&b);
printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");
char operation = '0';
scanf(" %c",&operation);
printf("\nOperating...\n\n");
switch (operation) {
case 'a':
add(&a,&b);
break;
case 's':
subtract(&a,&b);
break;
case 'm':
multiply(&a,&b);
break;
case 'd':
divide(&a,&b);
break;
default:
printf("Invalid input!!!\n");
}
}
我们创建了四个函数 、add()
和subtract()
来multiply()
对divide()
两个数字a
和执行算术运算b
。a
和的地址b
被传递给函数。在函数内部,*
我们访问了值并打印了结果。
类似地,我们可以使用指向数组第一个元素的指针将数组作为参数。
#include <stdio.h>
void greatestOfAll( int *p){
int max = *p;
for(int i=0; i < 5; i++){
if(*(p+i) > max)
max = *(p+i);
}
printf("The largest element is %d\n",max);
}
main(){
int myNumbers[5] = { 34, 65, -456, 0, 3455};
greatestOfAll(myNumbers);
/* Prints :The largest element is 3455" */
}
由于数组本身的名称是指向第一个元素的指针,因此我们将其作为参数发送给函数greatestOfAll()
。在函数中,我们使用循环和指针遍历数组。
#include <stdio.h>
#include <string.h>
void wish(char *p){
printf("Have a nice day, %s",p);
}
main(){
printf("Enter your name : \n");
char name[20];
gets(name);
wish(name);
}
name
在这里,我们使用指针传递字符串wish()
并打印消息。
2. 指针作为函数返回
#include <stdio.h>
int* multiply(int *a, int *b){
int c = *a * *b;
return &c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
该函数multiply()
需要两个指向 的指针int
。它还返回一个指向int
存储产品地址的指针。
人们很容易认为输出会是 15。但事实并非如此!
当multiply()
被调用时,暂停的执行main()
和内存现在被分配用于 的执行multiply()
。当其执行完成后,分配给的内存multiply()
被释放。
因此,虽然c
(local to main()
) 存储了产品的地址。由于该内存已被释放,因此无法保证其中的数据。
那么这是否意味着函数不能返回指针?不!!!
我们可以做两件事。将地址存储在堆或全局部分中,或者声明变量以使其static
值保持不变。
静态变量可以简单地通过在声明变量时在数据类型之前使用关键字 来创建。static
为了在堆中存储地址,我们可以使用库函数malloc()
和calloc()
动态分配内存。以下程序将解释这两种方法。
#include <stdio.h>
#include <stdlib.h>
/* Using malloc() */
int* multiply(int *a, int *b){
int *c = malloc(sizeof(int));
*c = *a * *b;
return c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
/* Using static keyword */
#include <stdio.h>
int* multiply(int *a, int *b){
static int c;
c = *a * *b;
return &c;
}
main(){
int a= 3, b = 5;
int *c = multiply (&a,&b);
printf("Product = %d",*c);
}
两种方法都返回输出 15。
3. 函数指针
就像指向不同数据类型的指针一样,我们也有一个指向函数的指针。指向函数的指针或函数指针存储函数的地址。尽管它没有指向任何数据。它指向函数中的第一条指令。
声明函数指针的语法是 -
/* Declaring a function */
returnType functionName(parameterType1, pparameterType2, ...);
/* Declaring a pointer to function */
returnType (*pointerName)(parameterType1, parameterType2, ...);
pointerName = &functionName; /* or pointerName = functionName; */
下面的示例将展示如何使用它。
int* multiply(int *a, int *b)
{
int *c = malloc(sizeof(int));
*c = *a * *b;
return c;
}
main()
{
int a=3,b=5;
int* (*p)(int*, int*) = &multiply; /* or int* (*p)(int*, int*) = multiply; */
int *c = (*p)(&a,&b); /* or int *c = p(&a,&b); */
printf("Product = %d",*c);
}
p
函数指针的声明multiply()
可以读作(遵循运算符优先级) -p
是一个指向函数的指针,以两个int
eger 指针(或两个指向int
)作为参数,并返回一个指向 的指针int
。
由于函数的名称也是指向函数的指针,因此&
没有必要使用 。此外,*
从函数调用中删除不会影响程序。
4. 函数指针数组
我们已经了解了如何创建指向 等的指针数组int
。char
类似地,我们可以创建指向函数的指针数组。
在这个数组中,每个元素将存储一个函数的地址,其中所有函数都是相同的类型。也就是说,它们有相同的类型和没有。参数和返回类型。
我们将修改这里讨论的程序。add()
我们将、subtract()
、的地址存储在一个multiply()
数组divide()
中,通过下标进行函数调用。
#include <stdio.h>
void add(float *a, float *b){
float c = *a + *b;
printf("Addition gives %.2f\n",c);
}
void subtract(float *a, float *b){
float c = *a - *b;
printf("Subtraction gives %.2f\n",c);
}
void multiply(float *a, float *b){
float c = *a * *b;
printf("Multiplication gives %.2f\n",c);
}
void divide(float *a, float *b){
float c = *a / *b;
printf("Division gives %.2f\n",c);
}
main(){
printf("Enter two numbers :\n");
float a,b;
scanf("%f %f",&a,&b);
printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n");
char operation = '0';
scanf(" %c",&operation);
void (*p[])(float* , float*) = {add,subtract,multiply,divide};
printf("\nOperating...\n\n");
switch (operation) {
case 'a':
p[0](&a,&b);
break;
case 's':
p[1](&a,&b);
break;
case 'm':
p[2](&a,&b);
break;
case 'd':
p[3](&a,&b);
break;
default:
printf("Invalid input!!!\n");
}
}
这里的声明可以理解为 -p
是一个指向函数的指针数组,以两个float
指针作为参数并返回 void。
5. 将函数指针作为参数
与任何其他指针一样,函数指针也可以传递给另一个函数,因此称为回调函数或被调用函数。它被传递到的函数称为调用函数。
更好的理解方法是查看qsort()
,它是 C 中的内置函数。它用于对整数、字符串、结构体等数组进行排序。 的声明是qsort()
-
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));
qsort()
有四个参数 -
void
指向数组开头的指针- 不。元素数
- 每个元素的大小
- 一个函数指针,它接受两个
void
指针作为参数并返回一个int
函数指针指向一个比较函数,如果第一个参数分别大于、等于或小于第二个参数,则该比较函数返回大于、等于或小于零的整数。
下面的程序展示了它的用法。
#include <stdio.h>
#include <stdlib.h>
int compareIntegers(const void *a, const void *b)
{
const int *x = a;
const int *y = b;
return *x - *y;
}
main(){
int myArray[] = {97,59,2,83,19,97};
int numberOfElements = sizeof(myArray) / sizeof(int);
printf("Before sorting - \n");
for(int i = 0; i < numberOfElements; i++)
printf("%d ", *(myArray + i));
qsort(myArray, numberOfElements, sizeof(int), compareIntegers);
printf("\n\nAfter sorting - \n");
for(int i = 0; i < numberOfElements; i++)
printf("%d ", *(myArray + i));
}
/* Output */
Before sorting -
97 59 2 83 19 97
After sorting -
2 19 59 83 97 97
由于函数名本身就是一个指针,因此我们可以将其写compareIntegers
为第四个参数。
在本文中,我们了解了指针与函数的各种用例。指针如何既可以作为参数又可以作为返回值,尽管后者要小心一些。我们还将关于与函数一起使用指针的想法扩展到函数指针以及它们在 C 中的使用方式。