day09

一、值传递与地址传递

1.1 值传递

1> 值传递的原理

2> 说明:

值传递过程中,形参实参操作的是不同的内存空间

当普通变量作为函数参数传递时,是单向的值传递,仅仅只是将实参的值,复制一份给形参使用,形参的改变不会影响到

实参的值

3> 代码实现

#include<myhead.h> //定义交换函数 void swap_1(int m, int n) { //完成两个数的交换 int temp = m; m = n; n = temp; printf("交换后,swap_1:: m = %d, n = %d\n", m, n); } /*********************主程序*********************/ int main(int argc, const char *argv[]) { //定义两个变量 int num = 520; int key = 1314; //调用交换函数 swap_1(num, key); printf("交换后,main:: num = %d, key = %d\n", num, key); return 0; }

1.2 地址传递

1> 地址传递原理图

2> 说明:

当实参传递的是数组名、指针或变量的地址(&num)时,可以理解成是地址传递

地址传递过程中,形参与实参操作的是同一块内存空间

形参对内存空间进行改变时,实参也跟着一起改变

3> 程序实现

#include<stdio.h>/

/定义冒泡排序函数

void sort_up(int brr[], int n)

{

printf("sizeof(brr) = %ld\n", sizeof(brr));

// //进行冒泡排序

for(int i=1; i<n; i++)

{

for(int j=0; j<n-i; j++)

{

if(brr[j] > brr[j+1]) {

int temp = brr[j]; brr[j] = brr[j+1]; brr[j+1] = temp;

}

}

}

printf("排序结束\n");

}

/****************主程序********************/

int main(int argc, const char *argv[])

{

//定义一个一位数组并初始化

int arr[5] = {2,8,1,6,7};

printf("sizeof(arr) = %ld\n", sizeof(arr)); //20

//调用冒泡排序函数,完成升序排序

sort_up(arr, 5);

//输出排序后的结果

printf("排序后:");

for(int i=0; i<5; i++) {

printf("%d\t", arr[i]);

}

printf("\n");

return 0;

}

二、递归函数

2.1 递归的概念

所谓递归,就是一个函数直接或间接的形式调用自身,这样的函数调用,我们称为递归调用

#include<stdio.h>

//定义一个讲故事函数

void say_story() {

printf("从前有座山,山里有个庙,庙里有个老和尚给小和尚讲故事,故事里说:");

say_story();

}

int main(int argc, const char *argv[]) {

//调用讲故事函数

say_story();

return 0;

}

2.2递归条件

1> 递归出口:用于终止递归进行进行的条件

2> 递归表达式:能够让递归继续进行的函数调用

2.3 递归思想

当直接解决规模比较大的问题不能进行时,需要先解决规模较小的原理一致的问题后,大的问题得以解决时,可以考虑使用递归

逐层分解,逐层合并

2.4 案例解析

1> 求n!结果

2> 求斐波那契数列第n项的值

1 1 2 3 5 8 13 21 。。。

3> 使用递归函数实现,输入一个数,输出该数据的每一位

三、指针概述


3.1    指针相关概念


1>    引入目的:能够从地址的角度,找到内存中的数据,而不是以变量的角度去找,效率较高
2>    指针:就是内存地址编号
3>    指针变量:由于指针这个地址编号很难记忆,我们引入指针变量存储指针
存储地址的变量称为指针变量
4>    指针变量中,指针存储地址,作为一个特殊的数据类型,其大小是固定的 8 字节

3.2    指针变量的定义


1>    定义格式:数据类型 * 指针名;


例如:int * ptr;


2>    指针变量的初始化


1、使用一个相同数据类型的变量的地址为其进行初始化
        int num = 520;
        int * ptr = &num;              //将num的地址赋值个指针变量 ptr   //定义一个指针变量ptr,指向num

2、使用一个已经初始化了的指针变量给一个新的指针变量进行初始化


        int * qtr = ptr;              //此时表示两个指针变量同时存储了num的地址   int *qtr = &num;

3、使用地址的0 就是 NULL为其初始化
        int *wtr = NULL;            
4、注意:没有初始化的指针变量不能直接使用,因为该指针变量中存储了一个随机地址,如果对其进行更改,可能会导致系统瘫痪
5、野指针:指向非法内存的指针称为野指针
        产生情况:
            1)定义指针时,没有为其进行初始化
            2)  指向了一个内存空间,但是随着程序的进行,该内存空间被回收了,那么该指针也是野指针(悬空指针)
            3) 数组下标越界时,访问的地址也是野指针


3>    指针变量的使用:使用运算符 ‘ * ’来取得指针变量中的值,我们也称该运算为 取值运算


1、指针变量在定义时,*号只是身份的象征,表示该语句定义的是指针变量
2、指针变量使用是,*号表示一个运算,取值运算,就是取得该指针变量指向的内存空间中的值
3、总结一下 * 号在C语言中的用途
        1)    表示乘号,是一个双目运算符
        2)     定义指针时,是身份的象征,表示定义的是指针变量
        3)    使用指针时,是取值运算符,表示取得指针变量指向的内存空间中的值
4、总结一下 & 在C语言中的用途
        1)    一个&表示双目运算符 按位 与运算
        2)    两个&&表示双目运算符 逻辑 与运算
        3)    一个&表示单目运算符 取址运算
5、&与*在指针的方面,互为逆运算
                int num = 520;
                int *ptr = &num;
        1)    *&num    ==> *(&num)    ==>    *(ptr)    ==> num
        2)    &*ptr    ==> &(*ptr)      ==>    &(num)    ==> ptr
        3)    *&ptr    ==>*(&ptr)       ==>    *(ptr的二级地址)    ==>    ptr
        4)    &*num    ==>报错

#include<stdio..h>

int main(int argc, const char *argv[])

{

int num = 520;

printf("num = %d\n", num); //num的值

printf("&num = %p\n", &num); //输出变量的地址

//定义指针变量指向num

int *ptr = &num //该语句执行后:num <==> *ptr

printf("ptr = %p\n", ptr); //得到的是num的地址

//通过ptr获取num的值

printf("*ptr = %d\n", *ptr); //输出的是num的值

//解释解释什么叫等价

num = 1314;

printf("*ptr = %d\n", *ptr); //输出的是num的值

*ptr = 999;

printf("num = %d\n", num); //num的值

return 0;

}

4> 指针的大小与指针的类型之间的关系

不同数据类型的指针所占内存空间都是一样的,都是指针的大小,32位系统下为4字节,64位系统下为 8字节

指针的数据类型存在的意义,不是为了给指针分配内存空间的,而是为了指针偏移使用的

不同类型的指针,在进行指针偏移时,偏移的大小跟指针的数据类型有关

指针每偏移一个单位,内存空间就会偏移一个数据类型大小的字节数

指针指向普通变量时,指针的偏移是没有意义的,但是,指针指向数组时,指针的偏移就有了现实的意义,表示指向上一个元素或者下一个元素的地址

#include<stdio.h>

int main(int argc, const char *argv[])
{
    char value_1 = 'H';      //定义字符数据
    short value_2 = 520;     //定义短整形数据
    int value_3 = 1314;       //定义整形数据
    double value_4 = 3.14;    //定义双精度浮点型数据

    //定义指针指向普通变量
    char *ptr1 = &value_1;
    short *ptr2 = &value_2;
    int * ptr3 = &value_3;
    double *ptr4 = &value_4;

    printf("ptr1 = %p\n", ptr1);
    printf("ptr2 = %p\n", ptr2);
    printf("ptr3 = %p\n", ptr3);
    printf("ptr4 = %p\n", ptr4);

    printf("*************************************\n");
    printf("ptr1+1 = %p\n", ptr1+1);
    printf("ptr2+1 = %p\n", ptr2+1);
    printf("ptr3+1 = %p\n", ptr3+1);
    printf("ptr4+1 = %p\n", ptr4+1);
    return 0;
}
5>    指针可以使用的运算
关系运算: == 判断两个指针是否指向同一个内存地址,ptr==NULL,判空指针
数加运算:表示对指针进行偏移
取值运算(*)、取址运算(&)

二、指针指向普通变量作为函数参数
1>    指针作为函数的参数进行数据传递时,不一定是地址传递
1 使用递归实现 求 n 的 k 次方

#include <stdio.h>
#include <string.h>
float sum(float n, int k)
{
    if (k == 0)//阶数为0
    {
        return 1;
    }
    else if(k>0)
    {
        return n * sum(n, k - 1);//递归到下一个
    }
    else
    {
        return sum(n,k+1)/n;
    }
    
}
int main()
{
    float n = 0.0;//定义一个浮点型来存储结果
    int k = 0;//次方
    printf("请输入N\n");
    scanf("%f", &n);
    printf("请输入次方k\n");
    scanf("%d", &k);
    printf("%f\n", sum(n, k));
    return 0;
}

2使用递归实现 strlen 的功能

#include <stdio.h>
#include <string.h>
int lens(char *b)
{
    if (*b == '\0')//不是末尾
        return 0;
    else
    {
        return 1 + lens( b+1);//递归
    }
}
int main()
{
    char str[30] = " ";//定义一个字符串
    char *b = str;//定义一个指向str的指针b
    printf("请输入一个字符串\n") ;
    fgets(str, sizeof(str), stdin);
    printf("%d\n",lens(b)-1);
    return 0;
}


3   使用递归实现汉诺塔问题

#include <stdio.h>
#include <string.h>
int lens(int N)
{
    if(N==1)//阶数为1
    {
        return 1;
    }
    else
    {
        return 2*lens(N-1)+1;//递归
    }
    
}
int main()
{
    int N=0;//定义阶数
    printf("请输入汉诺塔阶数\n");
    scanf("%d",&N);
    printf("次数%d\n",lens(N));
    return 0;
}


4 定义一个函数将一个字符串从大到小排序

#include <stdio.h>
#include <string.h>
void dd(char *str)
{
    int i = 0;
    while (str[i] != '\0')//不是末尾
    {
        int j = i + 1;//下1一个元素
        while (str[j] != '\0')
        {
            if (str[i] < str[j])//降序排序
            {
                char w = str[i];
                str[i] = str[j];
                str[j] = w;
            }
            j++;
        }
        i++;
    }
}

int main()
{
    char str[200] = "";
    printf("请输入一个字符串\n");
    fgets(str, sizeof(str), stdin);
    dd(str);
    printf("%s", str);
    return 0;
}

5  实现一个函数,用于检查一个字符串是否是回文字符串(正序和反序都相同)

#include <string.h>
#include <stdio.h>
int dd(char *str)//定义回文字符串函数
{
    int flag = 1;//回文串标识符
    for (int i = 0; i <= strlen(str) / 2; i++)//遍历str数组的一半
    {
        int j = strlen(str) - i -2;//找到倒数的元素下标
        if (str[i] != str[j])//不相等
        {
            flag = 0;//标识符置1
            break;//退出循环
        }
    }
    if (flag == 1)
    {
        return 1;//是回文串
    }
    else
    {
        return 0;//不是回文串
    }
}

int main()
{
    char str[200] = "";
    printf("请输入一个字符串\n");
    fgets(str, sizeof(str), stdin);
    int e = dd(str);//接收回文串函数返回值
    if (e == 1)
    {
        printf("字符串为回文串\n");
    }
    else
    {
        printf("字符串不为回文串\n");
    }

    return 0;
}

6  使用指针完成判断自己的主机存储多字节整数时,是大端存储还是小端存储

#include <stdio.h>

int main() {
    int num = 1;
    char *ptr = (char *)&num;

    if (*ptr == 1) {
        printf("小端存储");
    } else {
        printf("大端存储");
    }

    return 0;
}

7.记录一个文本有多少单词

#include <string.h>
#include <stdio.h>
int ss(char *str)//定义单词数函数
{
    int flag=1;//单词标识符
    int q=0;//单词数
    for(int i=0;i<strlen(str);i++)
    {
        if((str[i]<='Z'&&str[i]>='A'||str[i]<='z'&&str[i]>='a'))//str[i]为字母时执行
        {

            if(flag==1)

          {
            q++;//单词数+1
            flag =0;//标识符置0

          }
        }
        else //找到其他字符
        {
            flag =1;//标识符置1
        }
    }
    return q;
}

int main()
{
    char str[200] = "";//定义一个字符串
    printf("请输入一个字符串\n");
    fgets(str, sizeof(str), stdin);
    printf("单词数为%d\n",ss(str));
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值