首页 > 其他分享 >C语言基础语法_03

C语言基础语法_03

时间:2025-01-09 14:57:34浏览次数:3  
标签:03 arr int len C语言 语法 查找 数组 printf

5、函数

        函数就是程序中独立的功能,其实就是将程序打包,取一个名字,方便后面重复使用。函数的使用提高了代码的复用性和可维护性。 

/*
函数的定义:

返回值类型 函数名(形参1, 形参2……)
{
    函数体;
    return 返回值;
}
*/

        首先先定义一个简单的不带参数、不带返回值(其返回值其实为空)的函数例子,求10和20之和:

#include<stdio.h>


// 函数的定义
void sum()
{
    int num1 = 10, num2 = 20, sum = 0;
    sum = num1 + num2;
    printf("%d\n",sum);
}

int main()
{
    // 函数的调用
    sum();

    return 0;
}

        需要注意的是1、函数名在定义过程中不能重复,2、函数与函数之间是平级关系,不能嵌套定义,3、函数不调用就不执行,4、自定义函数写在main函数下面要在上方再次申明。这里再定义一个带参数的函数,求任意两数之和。

#include<stdio.h>


// 函数的定义
void sum(int num1,int num2)
{
    int sum = num1 + num2;
    printf("%d\n",sum);
}

int main()
{
    // 函数的调用
    sum(20, 30); //50

    return 0;
}

        其中int num1 和 int num2 为形式参数,20 和 30 为实际参数,形参和实参必须一一对应。函数除了参数之外,还有返回值的使用。返回值使用关键字 return ,它的作用包括结束函数和把后面的数据交给调用处。return下面不能编写代码,因为永远执行不到。如果书写 return 的后面没有跟具体的数据仅表示结束函数,还有一点要注意的时,返回值必须和函数名前返回值的类型相对应,如果没有返回值即为空,使用 void 。函数有返回值之后,函数的调用处使用变量接受返回值。求任意两数之和,并返回结果判断大小。

#include<stdio.h>

// 函数的定义
int sum(int num1,int num2)
{
    int sum = num1 + num2;
    return sum;
}

int main()
{
    // 函数的调用
    int result_1 = sum(20, 30);
    int result_2 = sum(30, 40);
    
    if(result_1 == result_2){printf("结果相等\n");}
    else if(result_1 > result_2){printf("结果1较大\n");}
    else{printf("结果2较大\n");}

    return 0;
}

6、数组

6.1、数组的知识 

         数组是一种容器,可以存储同种数据类型的多个值。数组的定义为

数据类型 数组名 [长度]
int arr [3];

        数组一旦定义之后,长度不可变,并且是连续的空间。数组在定义之后,还需要进行初始化,即给数组进行赋值,其格式为:

数据类型 数组名[长度] = {数据值,数据值……}
int arr[3] = {1,2,3};

        如果长度省略,数据值的个数就是数组长度。如果长度未省略,但是数据值的个数 <= 长度,空缺的位置将由默认值填充,整数用 0 填充,小数用 0.0 填充,字符用'\0'填充 (其实就是空白字符),字符串用NULL填充(就是什么都没有)。

        数组里面元素的访问包括获取和修改,其操作都利用到了索引。索引是数组的一个编号,也叫角标、下标、编号,从0开始,连续+1不间断。元素的获取一般赋值给一个变量,其格式为

变量 = 数组名[索引];
int num = arr[5];

元素的修改格式为

数组名[索引] = 数据值;
arr[5] =  10;
#include<stdio.h>

int main()
{
    //定义数组并初始化
    int arr[5] = {1,2,3,4,5};
    
    //获取索引为2,4的元素相加
    int sum = arr[2] + arr[4];
    printf("%d\n",sum);//8
    
    //修改最后一个元素为10
    arr[4] = 10;

    return 0;
}

        遍历是依次获取数组的每一个元素,对数组进行遍历:

#include<stdio.h>

int main()
{
    //定义数组并初始化
    int arr[5] = {1,2,3,4,5};
    
    //遍历数组进行打印
    for(int i = 0; i < 5; i++){printf("%d\n",arr[i]);}

    return 0;
}

        那么数组在内存中是怎样存在的?首先简单介绍一下内存,当软件运行时,临时存储数据的就叫内存。内存中的数据只有点击保存之后,程序到对应位置取出数据,保存到硬盘,如果不保存再次打开会消失,比如记事本。那么如何把数据保存到内存中,又怎么把内存的对应位置数据取出来?

        操作系统为了方便快速管理内存空间,把内存以字节(8个bit)为单位划分成很多小格子,每个字节都有自己的独立编号,这个编号就是内存地址(连续、唯一、不重复)。内存地址在不同位数操作系统不一样,在32位操作系统,内存地址以32位二进制表示,地址范围从0000 0000 0000 0000 0000 0000 0000 0000 到 1111 1111 1111 1111 1111 1111 1111 1111(2的32次方),最大内存4294967296字节,即4GB。在64位操作系统,内存地址以64位二进制表示,最大内存地址2的64次方,最大支持内存为17179TB。

        C语言中的内存地址,假如申请一个 int 变量,就是在内存中申请4个小格子(字节)。以第一个格子的地址为准,是变量的地址,也叫首地址,再结合变量类型就可以获取所有的数据。在代码中,通过取地址符 &变量名; 获取变量的地址。

#include<stdio.h>

int main()
{
    //获取变量的内存地址
    int a = 10 ;
    printf("%p\n",&a); //0000 0000 0061 FE1C
    return 0;
}

        int 类型变量 a 的内存地址为 0000 0000 0061 FE1C ,即为64位操作系统的内存地址,因为这里用16进制进行表示的,内存地址有16位,即16*4 = 64个二进制位。数组在内存中的表示如图,其中arr表示第一个字节的首地址,索引代表偏移量,索引结合数据类型可以确定后面数据的地址。

        在实际的代码中运行可以看到内存的规则和上面表示的相同。数组的首地址和第一个元素的第一个格子的内存地址相同,索引就是偏移量,索引每加一,由于是int数据,内存地址加4。数据长度的计算也可以得出:总长度/数据类型占用的字节个数(判断数据所占字节的大小可以使用sizeof函数 )。

#include<stdio.h>

int main()
{
    //获取数组的内存地址
    int arr[] = {1,2,3} ;
    printf("%p\n",&arr);   //0000 0000 0061 FE14
    printf("%p\n",&arr[0]);//0000 0000 0061 FE14
    printf("%p\n",&arr[1]);//0000 0000 0061 FE18
    printf("%p\n",&arr[2]);//0000 0000 0061 FE1C
    
    int len = sizeof(arr) / sizeof(arr[0]);
    printf("%d\n",len); // 3
    
    return 0;
}

         需要注意的一点是,数组作为参数传入函数时,传入的是数组的首地址。简单来说,在下面代码中 main 函数中定义的 arr 代表的是完整的数组,其字节长度为20,是五个 int 类型数据的长度。arr 作为参数传入函数时,传入的是 arr 的首地址,其字节长度为8,是64位操作系统的内存地址长度。

#include<stdio.h>

void printfun(int arr[])
{
    printf("%zu\n",sizeof(arr));
}

int main()
{
    int arr[] = {1,2,3,4,5} ;
    printf("%zu\n",sizeof(arr)); //20
    printfun(arr);//8

    return 0;
}

        再做一个简单的实验,对 main 函数中定义的数组进行取地址,并打印;再对传入函数的arr进行打印,这里没有取地址,原因是传入参数arr本身就是地址。发现两者相同,说明传入的参数就是数组的首地址。 

#include<stdio.h>

void printfun(int arr[])
{
    printf("%p\n",arr);
}

int main()
{
    int arr[] = {1,2,3,4,5} ;
    printf("%p\n",&arr); //000000000061FE00
    printfun(arr);//000000000061FE00

    return 0;
}

         最后用一张图简单地说明了两者的关系。

        当知道这些机制之后,再编写一个函数遍历整个数组,注意的是知道首地址无法进行遍历,要知道数据的长度(就是内部每个数据的地址偏移量),首地址结合地址偏移量从数组中取出每一个数据:

#include<stdio.h>

void printfun(int arr[],int len)
{
    for(int i = 0; i < len; i++){printf("%d ",arr[i]);}
}

int main()
{
    int arr[] = {1,2,3,4,5} ;
    int len = sizeof(arr) / sizeof(arr[0]);
    printfun(arr, len);//1 2 3 4 5 

    return 0;
}

 6.2、数组的练习

练习1:求包括五个元素的数组的最大值

#include<stdio.h>

int main()
{
    int arr[] = {25,11,15,55,9};
    
    //max的默认值一定是数组里面的数
    int max = arr[0];
    int len = sizeof(arr) / sizeof(arr[0]);

    //遍历数组进行比较
    for(int i = 1; i < len; i++)
    {max = max > arr[i] ? max : arr[i];}
    
    printf("%d\n",max); //55
    return 0;
}

练习2:生成10个1到100的随机数存入数组,求出所有数据的和

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int main()
{
    // 1.定义数组
    int arr[10] = {0};
    int len = sizeof(arr) / sizeof(arr[0]);

    // 2.生成10个1~100的随机数
    srand(time(NULL));
    for(int i = 0; i < len; i++)
    {
        int num = rand() % 100 + 1;
        arr[i] = num;
        printf("%d\n",arr[i]);
    }

    // 3.累加求和
    int sum = 0;
    for(int i = 0; i < len; i++){sum += arr[i];}
    printf("%d\n",sum);
    
    return 0;
}

生成10个1到100的随机数存入数组,要求数据不能重复,求出所有数据的和,求出所有数据的平均数,统计有多少个数据比平均值小。

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int contain(int arr[], int len, int num)
{
    for(int i = 0; i < len; i++ )
    {
        if(num == arr[i]){return 1;}
    }
    return 0;
}

int main()
{
    // 1.定义数组
    int arr[10] = {0};
    int len = sizeof(arr) / sizeof(arr[0]);

    // 2.生成10个1~100的随机数,并且不相同
    srand(time(NULL));
    for(int i = 0; i < len; )
    {
        int num = rand() % 100 + 1;
        int flag = contain(arr, len, num);
        if (!flag)
        {
            arr[i] = num;
            i++;
        }
    }

    for(int i = 0; i < len; i++)
    {
        printf("%d\n",arr[i]);
    }
    
    // 3.累加求和
    int sum = 0;
    for(int i = 0; i < len; i++){sum += arr[i];}
    printf("和为:%d\n",sum);

    // 4.求平均数
    float avg = sum / 10.0;
    printf("平均数为:%.2f\n",avg);

    // 5.统计比平均数少的个数
    int count = 0;
    for(int i = 0; i < len; i++)
    {
        if(arr[i] < avg){ count++; }
    }
    printf("比平均数少的个数为:%d\n",count);

    return 0;
}

练习3:键盘录入5个数据并存入数组,要求遍历数组,反转数组,再遍历数组。

#include<stdio.h>

void printfun(int arr[], int len)
{
    for(int i = 0; i < len; i++)
    {
        printf("%d\n", arr[i]);
    }
}

int main()
{
    // 1.定义数组
    int arr[5] = {0};
    int len = sizeof(arr) / sizeof(arr[0]);

    // 2.键盘录入数据
    for(int i = 0; i < len; i++)
    {
        printf("请输入第%d个数字", i + 1);
        scanf("%d",&arr[i]);
    }
    printfun(arr, len);

    // 3.反转数组
    for(int i = 0; i < len; i++)
    {
        if(i < len-1-i)
        {
            int temp = arr[i];
            arr[i] = arr[len-1-i];
            arr[len-1-i] = temp;
        }
        else{break;}
    }

    /*
    int i = 0;
    int j = len - 1;
    while(i < j)
    {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        i++;
        j--;
    }
    */
   
    printfun(arr, len);

    return 0;
}

6.3、数组的常见算法

        常见算法分为两种,一个是查找算法,另一个就是排序算法。

6.3.1、查找算法        

        查找算法分为七种,分别是基本查找、二分查找、插值查找、分块查找、哈希查找、树表查找和斐波那契查找,只介绍前四种。基本查找也叫顺序查找,是有一堆数据,要查找其中的某一个数据:将数据放到数组中,遍历数组查找,如果找到返回数据索引,如果没有返回-1(因为不存在-1索引)。

#include<stdio.h>

int order(int arr[], int len, int num)
{
    for(int i = 0; i < len; i++)
    {
        if(num == arr[i]){return i;}
    }
    return -1;
}

int main()
{
    // 基本查找

    // 1、定义数组
    int arr[] = {15,9,88,54,16};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 2.定义查找数据
    int num = 54;

    // 3.数据查找
    int result = order(arr, len, num);
    printf("%d\n",result);

    return 0;
}

        二分查找也叫折半查找,提高了查找的效率,前提条件是数组中的数据必须是有序的,如果数据是无序的,先排序再用二分查找得到的索引没有实际意义,只能确定当前数字在数组中是否存在,因为排序之后数字的位置就可能发生变化了,其核心逻辑是每次排除一半的查找范围:

        1.定义min和max表示当前要查找的范围 ;

        2.定义mid在min和max之间,一般来说mid = \frac{min + max}{2}

        3.如果要查找的元素在mid的左边,缩小范围时,min不变,max等于mid减1;

        4.如果要查找的元素在mid的右边,缩小范围时,max不变,min等于mid加1;

不断循环一直到找到数据为止,即min = max。

#include<stdio.h>

int binarySearch(int arr[], int len, int num)
{
    //1.确定查找的范围
    int min = 0, max = len-1;

    //2.利用循环不断进行查找
    while(min <= max)
    {
        int mid = (min + max) / 2;
        if(arr[mid] > num){max = mid - 1;}
        else if(arr[mid] < num){min = mid + 1;}
        else{return mid;}
    }

    //3.如果min>max,表示数据不存在,返回-1
    return -1;
}

int main()
{
    // 二分查找

    // 1、定义数组
    int arr[] = {9,15,16,33,38,42,48,59,68,75};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 2.定义查找数据
    int num = 59;

    // 3.数据查找
    int result = binarySearch(arr, len, num);
    printf("%d\n",result); //7

    return 0;
}

        二分查找的mid每次都是在最中间定义的,所以能不能让mid更加靠近要查找的数据?所以提出插值查找,将mid的计算换成下面公式:

mid = min + \frac{(key - arr[min]) * (max - min)}{arr[max] - arr[min]}

代码其他的部分保持不变,但是这种方法要求数据尽可能发布要均匀。

        已经介绍的算法基本查找是数据没有如何顺序,二分查找和插值查找是要求数据一定要有顺序,这两种数据的类型比较极端,更多的是无序中存在有序的数据。对于这种数据使用分块查找,其核心思路是先确定要查找的元素在哪一块,然后在块内挨个查找,分块的原则:

        1、前一块中的最大数据,小于后一块中的所有数据(块内无序,块间有序);

        2、块数数量一般等于数字的个数开根号,比如:16个数字一般分为4块左右。

6.3.2、排序算法

        排序算法就是把乱序数据排成有序算法,可以从小到大,也可以从大到小。排序算法包括冒泡排序和选择排序。冒泡排序就是相邻的数据两两比较,小的放前面,大的放后面。如图,第一轮循环结束后,最大值已经找到,在数组最右边,第二轮循环只要在剩余元素找到最大值就可以了,即第二轮少循环一次。以此类推,注意的是如果数组有n个数据,总共只要执行 n-1 轮代码就行。

#include<stdio.h>

int main()
{
    // 冒泡排序

    // 1、定义数组
    int arr[] = {3,5,2,1,4};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 2.冒泡排序升序
    for(int j = 0; j < len-1; j++)
    {
        for(int i = 0; i < len-1; i++)
        {
            if(i == len -1 - j){continue;}
            
            if(arr[i] > arr[i+1])
            {
                int temp = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = temp;
            }
        }
    }

    for(int i = 0; i < len; i++)
    {
        printf("%d ",arr[i]);
    }
    // 1 2 3 4 5 
    return 0;
}

        选择排序是从0索引开始,拿着每一个索引上的元素跟后面的元素进行一一比较,小的放前面,大的放后面,以此类推。第一轮如下图所示,0索引数据和后面每一个元素进行一一比较,小的放前面,大的放后面,第一轮结束后,最小的数据已经确定,第二轮循环从1索引开始以此类推。同样地,注意的是如果数组有n个数据,总共只要执行 n-1 轮代码就行。

#include<stdio.h>

int main()
{
    // 冒泡排序

    // 1、定义数组
    int arr[] = {3,5,2,1,4};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 2.冒泡排序升序
    for(int j = 0; j < len-1; j++)
    {
        for(int i = j+1; i < len; i++)
        {
            if(arr[j] > arr[i])
            {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }

    for(int i = 0; i < len; i++)
    {
        printf("%d ",arr[i]);
    }
    // 1 2 3 4 5 
    return 0;
}

        简单来说,以升序排序为前提,冒泡排序在末端依次确定最大值,顺序排序在前端依次确定最小值。

标签:03,arr,int,len,C语言,语法,查找,数组,printf
From: https://blog.csdn.net/m0_62883696/article/details/144948251

相关文章

  • C语言gets()被删除了,改用fgets()!
    在《C语言的五套标准:C89、C99、C11、C17和C23》一节里提到,作为上世纪70年代的产物,C语言历经了多个版本的迭代,增加很多新语法的同时,也剔除了当下认为不再适用的语法,这其中就包括gets()函数。gets()算得上是C语言里“元老级”的输入函数,C语言诞生的时候就有它了。直到C1......
  • 一文搞清楚C语言布尔类型(bool)
    有些场景中,变量的取值只有两种情况。比如说,用变量sex存储人的性别,它的值就只有两种情况,分别是“男”和“女”。通过前面的学习,读者已经掌握了很多种数据类型,比如char、short、int、long、float、double等。那么,变量sex的类型应该是什么呢?在C99标准发布之前,对于只有两......
  • C# 语法中级
    总目录C#语法总目录C#语法中级lambda表达式1.捕获外部变量2.捕获迭代变量匿名类型匿名方法异常相关1.枚举器2.可枚举对象3.迭代器3.迭代器语义4.yieldbreak语句5.组合序列可空类型1.Nullable<T>结构体lambda表达式编译器在内部将lambda表达......
  • C|C语言中的语法总结
    随着时间的不断推进,小编的c语言之路也快接近尾声了,虽然c的语言很底层,但是c仍然对小编的能力给到了提升,在即将与它告别的时间,小编也会用指尖能触及的键盘将它记录下来,也方便后面到来的新手小白可以快速学习,小编也会用自己最精简的理解将所有基础语法记录下来!那么让我们一起来看......
  • C语言的关键字typedef与结构体的类型别名
    在C语言中,typedef是一个非常有用的关键字,它用于为数据类型创建新的名称(别名)。这不仅可以提高代码的可读性,还可以简化复杂类型声明的使用。基本用法typedef的基本语法结构如下:typedefexisting_typenew_type_name;这里,existing_type是已经存在的数据类型,可以是基础数据类型......
  • C++/C语言的内存管理之虚拟内存
    C++/C语言的内存管理之虚拟内存一、虚拟内存1、组成2、特点3、目的二、栈区1、特点2、缺点三、堆区1、特点2、缺点3、相关四、全局静态区1、特点五、常量区1、特点六、代码区1、特点一、虚拟内存1、组成(1)栈区(Stack):存放局部变量、函数的参数。编译器自动分配和......
  • ASE200N03-ASEMI中低压N沟道MOS管ASE200N03
    编辑:llASE200N03-ASEMI中低压N沟道MOS管ASE200N03型号:ASE200N03品牌:ASEMI封装:TO-252最大漏源电流:160A漏源击穿电压:30V批号:最新RDS(ON)Max:1.8mΩ引脚数量:3沟道类型:N沟道MOS管芯片尺寸:MIL漏电流:恢复时间:ns芯片材质:封装尺寸:如图特性:中低压MOS管、N沟道MOS管工作结温:-5......
  • [rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(03):图像的导入、显示、调整(暨image
    前言本文是关于iced库的部件介绍,iced库是基于rust的GUI库,作者自述是受Elm启发。iced目前的版本是0.13.1,相较于此前的0.12版本,有较大改动。本合集是基于新版本的关于分部件(widget)的使用介绍,包括源代码介绍、实例使用等。环境配置系统:window10平台:visualstudiocode语言:rust......
  • C语言中两个不同类型的结构体相互幅值
    在C语言中,如果两个结构体的定义(字段名称、数量、顺序、类型)不一样,就不能直接使用=运算符进行整体赋值,需要逐个字段进行赋值或者通过其它手段进行“转换”。1.字段一一对应赋值1.1手动逐个赋值假设有如下两个结构体:typedefstruct{intid;charname[20]......
  • 【C语言】打印菱形
    目录前言一、题目介绍1.1问题描述1.2输入描述1.3 输出描述1.4示例 二、基本思路三、代码实现3.1基本框架3.2pirnt函数的实现四、效果展示 五、完整代码前言在牛客网刷题时碰到的一道题,基础语法篇中的BC8牛牛的字符菱形,我相信在其它地方也有类似的......