C语言-05_函数和指针


目标

  • 1、C语言中函数的定义和调用

  • 2、C语言中函数阐述传递

  • 3、C语言中函数的返回值

  • 4、C语言中全局变量和局部变量

  • 5、C语言中指针的概念和使用

  • 6、C语言中指针运算

  • 7、C语言中指针和数组的关系

  • 8、C语言中指针当函数参数和返回值

  • 9、C语言中指针函数和函数指针

  • 10、C语言中命令行参数函数

一、C语言中函数的定义和调用

在C语言中,函数是一组可重复使用的代码,用于执行特定任务。函数的优势在于它可以随需求调用,减少代码的冗余性,并增强代码的可读性。

函数的基本组成包括:**返回类型 **, 函数名参数列表和函数体

  • 返回类型 : 函数可以返回一个值。返回类型是函数返回值的数据类型。如果函数不反悔任何值,则其返回值为void。
  • 函数名:这是表示函数的唯一名称,函数名和参数列表一起构成了函数的签名。、
  • 参数列表:参数是传递给函数的值。参数列表包括参数的类型、类型、参数的数量,参数是可选的,也就是说,函数可以不包含参数
  • 函数体:函数体包含了定义函数行为的一组语句。

1.1 函数的定义

//	返回类型是 int ,函数名为add 参数列表是两个int类型的参数
int add(int num1 , int num2)
{
    int sum = num1 + num;
    return sum;
}

一旦我们定义了函数,就可以在其他地方调用它。要调用函数,我们只需要编写函数名和在括号中传递适当的参数。例如:

int main()
{
    int result = add(5 , 10);
    printf("两数之和等于 %d\n",result);
    return 0;
}

在这个例子中,add(5, 10)是函数调用,510是传递给add函数的参数,add函数返回15,我们将这个返回值赋值给result变量,然后打印出来。

二、C语言中函数阐述传递

C语言中函数参数传递基本上有两种方式:值传递和引用传递。

  • 值传递:在值传递方式中,被掉函数的形式参数作为被调用函数的局部变量处理,即在栈中开辟了内存空间,以存放主调函数传递过来的实参值,这个空间值只在函数调用的过程中有效,也就是说,值传递的方式,函数接收的是参数的副本,函数内部对参数的任何改变,都不会影响到实际参数的值
#include <stdio.h>

void change(int x) {
    x = 20;
    printf("Inside change() x=%d\n", x);
}

int main() {
    int x = 10;
    change(x);
    printf("Outside change() x=%d\n", x);
    return 0;
}

在这个示例中,即使在change函数中修改了x的值,但在main函数中打印x时,其值还是原来的值10,这就是值传递。

  • 引用传递(Pass by Reference): 在引用传递方式中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这次存放的是主调函数放进来实参的变量地址。被调函数对形参的任何操作都被处理成间接寻址,即对实参操作。

示例代码:

#include <stdio.h>

void change(int *x) {
    *x = 20;
    printf("Inside change() x=%d\n", *x);
}

int main() {
    int x = 10;
    change(&x);
    printf("Outside change() x=%d\n", x);
    return 0;
}

在这个示例中,我们传递了x的地址给change函数,然后在change函数中修改了x的值。在main函数中,我们看到x的值已经被修改为20,这就是引用传递。

  • 形式参数 : 形式参数也成为形参,是函数定义时声明的参数。形参只在函数定义时有意义,在函数内部形参作为局部变量使用。
  • 实际参数:实际参数也成为实参,实在函数调用时实际传给函数的参数。实参可以是常量、变量或者是表达式,无论实参是任何形式的量,在进行函数调用时,他们都必须有确定的值,以便把这些值传给形参。

实例:

void myFunction(int x) {    // 这里的int x就是形式参数
    printf("%d", x);
}

int main() {
    int y = 10;
    myFunction(y);   // 这里的y就是实际参数
    return 0;
}

在这个例子中,myFunction的定义中int x就是形式参数,当我们在main函数中调用myFunction(y)时,y就是实际参数。

函数在被调用时,实际参数的值会被复制给形式参数。如果形式参数在函数体内发生改变,并不会影响实际参数的值。但如果你使用了指针作为形式参数,那么你可以在函数体内改变实际参数的值,这是因为你传递的是实际参数的地址,而不是实际参数的值。

三、C语言中函数的返回值

C语言中的函数可以返回一个值给调用他的函数或者是主程序。返回值可以是任何基本类型,也可以是街头提或者枚举类型。当然,函数也可以设定为不返回任何值,这种类型的函数被声明为"void"

函数的返回值

四、C语言中全局变量和局部变量

五、C语言中指针的概念和使用

6.1 指针中的一些基础概念知识

6.1.1 指针的作用
  • 使程序简洁、高效:指针可以直接访问内存,提高性能,减少内存使用。
  • 表示复杂数据结构:指针是实现链表、树等复杂数据结构的关键。
  • 动态内存分配:运行时需要动态分配或释放内存时,必须使用指针。
  • 返回多个函数值:通过传递指针给函数,函数可以修改并返回多个结果。
6.1.2 指针、内存、地址的概念
  • 内存:内存是计算机存储空间中,每个变量都占用的一定内存空间。
  • 地址:内存中每个自己都拥有唯一的地址,这个地址是内存空间的标识符
  • 指针:指针是一个特殊的变量,他存储了内存的地址,通过这个地址,我们可以直接访问内存中的数据

指针就是链接内存和地址的桥梁,通过指针我们可以直接的操作内存中的数据,这是C语言中非常重要和强大的特性

其关系如图所示,其实指针也只是一个变量,这个变量存储的类型有一点点特殊,存储的是我们其他变量的地址。我们只需要知道一个变量是由地址和数据组成,而我们的指针变量所能做的就是存储你这个普通变量的地址并能进行操作。
在这里插入图片描述

6.2 指针的概念

在C语言中,指针就是一个变量,其值为另一个变量的内存地址,也就是它指向该地址的变量。指针允许以直接和间接的方式通过引用内存地址来访问和操作内存中存储数据。

6.2.1 指针变量说明

指针变量的一般形式如下:

type * v = NULL;

在这里,

  • type:表示指针变量所指向的变量的数据类型。它可以是任何的数据类型,例如 int, char, double, float 等。
  • *:表示这是一个指针变量。
  • v:是指针变量的名称。

6.3 指针的使用

在C语言中要使用指针,我们首先需要声明一个指针,然后可以通过地址操作符(&)来获取变量的地址并将其存储在指针中,然后通过解引用操作符(*)来获取存储该地址中的值

以下是指针的基本步骤:

6.3.1 指针使用的基本步骤

步骤如下

  • 声明指针
  • 为指针分配地址
  • 使用访问变量值
  • 修改指针指向的变量的值

声明指针:在C语言中,使用星号(*)来声明一个指针。

int * p;

为指针分配地址:我们可以使用“&”来获取一个变量的地址,然后将该地址分配给指针。例如:

int i = 0;
int * p;
p = &i;	   //存地址

使用指针访问变量值:使用星号(*)前缀可以访问变量的值。这称为解引用

printf("%d",*p);  //去数据

修改指针指向的变量的值:你也可以使用指针来修改它所指向的变量的值

int i = 0;
int * p;
p = &i;
printf("%d",*p);

#include <stdio.h>

int main()
{
    int a = 0;
    int * p = &a;

    *p = 5;

    printf("打印地址: p = %p a = %p" , p , &a);
    
    printf("打印数据: p = %d a = %d" , *p, a);
    return 0;
}

六、C语言中指针运算

指针的运算就是以指针变量中所存放的值(地址量)作为运算量而进行运算。指针运算的知识就是地址的计算。

指针运算的种类是有限制的,只能进行算术运算关系运算,以及我们前面所讲过的赋值,这里称为赋值运算。

如下表:

6.1 算术运算

不同类型的两个指针实行加减是无意义的:

px+n 表示实际内存单元的地址量是:(px)+sizeof(px的类型)*n;

px+n 表示实际内存单元的地址量是:(px)-sizeof(px的类型)*n;

指针算术运算:

运算符用法描述
+p + n指针p向前(向高地址)移动n个同类型元素的位置
-p - n指针p向后(向低地址)移动n个同类型元素的位置
-p - q计算两个指针pq之间相差多少个同类型元素的位置

指针的自增与自减运算:

运算符用法描述
++++pp++指针p向前(向高地址)移动一个同类型元素的位置
----pp--指针p向后(向低地址)移动一个同类型元素的位置

注意:这些运算符直接改变指针本身的值。具体来说,++p--p首先改变指针的值,然后返回新的指针值;p++p--先返回原始的指针值,然后改变指针的值。

例程

 	#include <stdio.h>

int main()
{
    int arr[] = {10,20,30,40,50};

    int * p = arr;
    

    //打印地址
    printf("打印地址: p = %p , arr = %p \n " , &p , arr);
    printf("打印数据: p = %d\n",*p);
    /*加法运算*/
    p = p + 1;
    printf("****************加法计算******************\n");
    printf("打印地址: p = %p , arr = %p \n" , &p , arr);
    printf("打印数据: p = %d\n",*p);

    /*减法运算*/
    p = p - 1;
    printf("****************加法计算******************\n");
    printf("打印地址: p = %p , arr = %p \n" , &p , arr);
    printf("打印数据: p = %d\n",*p);

    /*指针差*/
    printf("****************指针差******************\n");
    int * p_3 = &arr[3];
    int * p_4 = &arr[1];
    int i = p_3 - p_4;
    printf("指针差%d\n",i);

    /*自增运算*/
    p++;
    printf("****************加法计算******************\n");
    printf("打印地址: p = %p , arr = %p \n" , &p , arr);
    printf("打印数据: p = %d\n",*p);

    /*减法运算*/
    j
    p--;
    printf("****************加法计算******************\n");
    printf("打印地址: p = %p , arr = %p \n" , &p , arr);
    printf("打印数据: p = %d\n",*p);

    return 0;
}

6.2关系运算

关于指针的关系运算,需要注意以下几个问题

  1. 指针关系运算应当在同类型和同数据区域的指针间进行。具有不同数据类型的指针之间的关系运算没有意义,指向不同数据区域(例如,一个指针指向堆,另一个指向栈)的两个指针之间,关系运算也没有意义。
  2. 指针与整数之间的关系运算没有意义,不能进行大于、小于等判断,但是可以与0进行等于或不等于的比较,用于判断指针是否为空。这是因为在C语言中,NULL指针通常被定义为0。

指针关系运算

运算符用法描述
==p == q如果指针pq指向同一位置则返回真
!=p != q如果指针pq指向不同位置则返回真
<p < q如果指针p位于q之前则返回真
>p > q如果指针p位于q之后则返回真
<=p <= q如果指针p位于q之前或与q同位置则返回真
>=p >= q如果指针p位于q之后或与q同位置则返回真
#include <stdio.h>

int main()
{
    int arr[] = {10,20,30,40,50};
    int * p_1 = arr;
    int * p_2 = arr;

    printf("打印数据:p_1 = %d , p_2 = %d , arr = %d\n" , *p_1 , *p_2 ,* arr);
    printf("打印地址:p_1 = %p , p_2 = %p , arr = %p\n" , p_1 , p_2 , arr);

    /*大于小于判断*/
    p_1 = p_1 + 1;
    
    if(p_2 > p_1)
    {
        printf("打印数据:p_1 = %d , p_2 = %d , arr = %d\n" , *p_1 , *p_2 ,* arr);
        printf("打印地址:p_1 = %p , p_2 = %p , arr = %p\n" , p_1 , p_2 , arr);
        printf("p_2 > p_1");
    }
    //p_1 = p_1 - 1;
    //p_2 = p_2 + 1;
    else if(p_2 < p_1)
    {
        printf("打印数据:p_1 = %d , p_2 = %d , arr = %d\n" , *p_1 , *p_2 ,* arr);
        printf("打印地址:p_1 = %p , p_2 = %p , arr = %p\n" , p_1 , p_2 , arr);
        printf("p_2 > p_1");
    }

    return 0;
}

七、C语言中指针和数组的关系

在 C 语言中,指针和数组有着紧密的联系。理解这种联系有助于我们更好地理解如何在 C 程序中使用数组和指针。

7.1 数组名作为指针

在 C 语言中,数组名是一个常量指针,它指向数组的第一个元素。例如,如果你有一个数组 int arr[5],那么 arr 就是指向 arr[0] 的指针。你可以通过对数组名进行解引用操作来获取第一个元素的值,例如 *arr 就是 arr[0] 的值。

int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", *arr);  // 输出: 1
7.2 使用指针访问数组元素:

你可以使用指针来访问和操作数组的元素。你可以通过对指针进行增减操作来移动指针,并使用解引用操作符来访问指针当前指向的元素。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  // p 指向数组的第一个元素
for (int i = 0; i < 5; i++) {
    printf("%d\n", *p);  // 输出当前元素的值
    p++;  // 指针向前移动
}
7.3 数组作为函数参数

当数组作为函数参数时,它会被自动地转化为指向数组第一个元素的指针。这意味着函数内部不能直接获取到数组的长度,因为指针不保存数组的长度信息。为了在函数内部处理数组,你通常需要将数组的长度作为另一个参数传入。

void printArr(int *arr, int size)·-
    for (int i = 0; i < size; i++) {
        printf("%d\n", arr[i]);
    }
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printArr(arr, 5);
    return 0;
}

总的来说,理解指针和数组的关系有助于我们编写更高效、更灵活的 C 代码。

八、C语言中指针当函数参数和返回值

在 C 语言中,我们可以使用指针作为函数的参数,也可以使用指针作为函数的返回值。这种使用指针的方式可以使我们的代码更加灵活,特别是在处理数组,字符串,动态内存分配等问题时。

  1. 指针作为函数参数: 通过将指针作为函数参数,我们可以实现在函数内部修改外部变量的值。此外,当我们需要在函数中处理数组,字符串等数据结构时,也通常会使用指针作为参数。
 #include <stdio.h>

void add(int* p) {    //引用传递
    (*p)++;  // increment the value of variable pointed by p
}

int main() {
    int a = 10;
    printf("Before: %d\n", a);
    add(&a);
    printf("After: %d\n", a);
    return 0;
}

这个例子中,add函数接收一个整数指针作为参数,然后增加该指针所指向的变量的值。

  1. 指针作为函数返回值: 如果函数需要返回数组,字符串,或者其他的复杂数据类型,那么通常会使用指针作为函数的返回值。
 #include <stdio.h>

int* getArr() {     //指针函数
    static int arr[3] = {10, 20, 30};
    return arr;
}

int main() {
    int* arr = getArr();
    for (int i = 0; i < 3; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

这个例子中,getArr函数返回一个指向整型数组的指针。

注意事项:函数返回的指针必须指向静态数据或者动态分配的内存,不能返回指向栈上数据的指针,因为当函数执行完毕后,局部变量(存储在栈上)的生命周期就结束了,其内存区域可能会被其他的数据覆盖。

以上,就是在 C 语言中使用指针作为函数参数和返回值的基本用法。

九、C语言中指针函数和函数指针

在 C 语言中,指针函数和函数指针是两个不同的概念,它们的含义和用法如下:

  1. 指针函数 (Pointer Function):指针函数其实就是返回指针的函数。在这种情况下,函数的返回类型是一个指针类型。
 char* getString() {
    char* str = "Hello, World!";
    return str;
}

int main() {
    char* str;
    str = getString();
    printf("%s\n", str);
    return 0;
}

在上述代码中,getString是一个返回char类型指针的函数,也被称为指针函数。

  1. 函数指针 (Function Pointer):函数指针是一个指针,它指向了一个函数。这样我们就可以像使用普通函数一样来使用这个指针。
void hello() {
    printf("Hello, World!\n");
}

int main() {
    void (*funcPtr)();  // declare a function pointer
    funcPtr = hello;  // assign hello function to funcPtr
    funcPtr();  // call the function via the function pointer
    return 0;
}

在上述代码中,funcPtr就是一个函数指针,它被用来指向hello函数。然后通过funcPtr()来调用hello函数。

这两者虽然名字类似,但其实是两个完全不同的概念。指针函数是一种特殊类型的函数,它的返回值是一个指针。函数指针则是指向函数的指针,它可以被用来调用所指向的函数。

十、C语言中命令行参数函数

在 C 语言中,我们可以通过命令行为程序提供参数,这些参数会在 main 函数中以特定的形式被处理。一个处理命令行参数的 main 函数的形式如下:

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

这里的两个参数分别是:

  1. argc (参数的数量):这是一个整型,代表命令行参数的数量。当没有任何参数时,argc 的值为 1,因为程序的名称本身也被视作一个参数。
  2. argv (参数的值):这是一个指向字符串的指针数组,其中包含了每个参数的具体值。argv[0] 是程序的名称,argv[1] 是第一个参数,以此类推。

比如,你有一个程序叫做 program,你通过命令行以这种方式运行它:./program arg1 arg2。在这种情况下,argc 的值为 3,argv[0]./programargv[1]arg1argv[2]arg2

这是一个简单的处理命令行参数的例子:

 #include <stdio.h>

int main(int argc, char *argv[]) {
    printf("程序名称: %s\n", argv[0]);
    
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            printf("参数 %d: %s\n", i, argv[i]);
        }
    } else {
        printf("没有提供参数.\n");
    }
    
    return 0;
}

这个程序会打印出它自己的名称以及所有的命令行参数。如果没有提供任何参数,它将输出 “没有提供参数.”。

应用场景

命令行参数在许多情况下都非常有用。这是因为它们提供了一种灵活的方式,让你可以在启动程序时向其提供一些信息。这意味着你可以控制程序的行为,而无需修改和重新编译代码。

以下是一些具体的使用场景:

  1. 配置选项:你可以使用命令行参数来控制程序的行为。例如,许多命令行工具都有诸如 -v--verbose 的参数,这些参数可以控制程序显示更多的输出信息。
  2. 输入和输出文件:你可以使用命令行参数来指定输入和/或输出文件的路径。例如,编译器就需要一个源代码文件作为输入,并将生成的机器代码写入到另一个文件中。
  3. 参数化测试:如果你正在编写一个需要进行大量测试的程序,那么命令行参数可能会派上用场。你可以编写一个脚本来运行你的程序,每次使用不同的参数,这样就可以自动化测试过程。
  4. 环境设置:例如,你可能需要指定一些环境特定的设置,比如数据库的地址、密码等。

总的来说,命令行参数提供了一种灵活的方式来影响程序的行为,而无需每次都去改变和重新编译代码。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值