0


C语言数组,一维二维数组,字符串,函数传参中的数组,清空输入缓冲区,冒泡&选择排序

** 前言**:一二维数组字符串的使用与创建,字符串和数组如何传参给函数,字符串指针,字符串的输入与输出,清空输入缓冲区的方法,选择排序和冒泡排序。

一.一维数组

1.一维数组的创建与定义

(1).定义及初始化一维数组

    int arr1[10] = { 1,2,3,4 };//定义数组可以规定大小也可以不规定大小
    int arr2[] = { 1,2,3,4,5 };
    int arr3[5] = { 1,2,3,4,5 };
    char arr4[3] = { 'a','b','c' };
    char arr5[3] = { 'a',93,'c' };//93是ASCII码值,用%c类型打印会打印出ASCII值所对对应的字符串,同理,一个字符'a'用%d类型打印会打印出对应的ASCII码值,就像下段代码
    char arr6[] = "abcdefg";


一维数组在创建时可以不规定大小,如果不规定大小,则需要对数组初始化,即数组要有内容,这时候数组的大小(元素个数)就根据初始化的内容来确定,但我们需要注意以下代码的不同。

    char arr1[] = "abc";
    char arr2[3] = { 'a','b','c' };

从表面上看,arr1和arr2储存的内容都是'a' 'b' 'c' 三个字符,但实际上他们在内存中是这样分配的

在字符数组(字符串)的使用和创建中,要特别特别特别注意这个点。

2.一维数组的使用

int main()
{
    int arr[10] = { 0 };//这里数组不完全初始化
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        arr[i] = i;
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }

    return 0;
}

这里说明了数组的下标是从0开始的!!arr[i]就是具体的值。

大家也看到了两处画红线的i<10,建议大家都这么写,因为i<多少,这个多少就是数组的元素个数,简单明了。

一维数组的打印一般都是用for循环一个一个打印来实现的,同理,二维数组就用两个for循环

3.数组元素个数的计算和大小的计算

数组大小的计算一般用sizeof计算

数组的元素个数就是数组的大小(单位是字节)除以单个元素的大小

4.一维数组在内存中的储存

一维数组在内存中储存是连续的,由低地址存到高地址

5.避免数组越界

当我们这里写成i<=10,说明会打印总共11个数字,但是我们的数组大小只定义了10,这时数组就会越界,所以我们写程序时一定要

二.二维数组

1.二维数组的定义和初始化

二维数组的创建

    int arr1[3][4];
    char arr2[3][5];
    double arr3[2][4];

二维数组的初始化

    int arr1[3][4];
    int arr2[3][4] = { {1,2},{3,4} };
    int arr3[][3] = { {1,2,3},{4,5} };//二维数组只有在有初始化时,行才可以省略,列任何情况都不可省

2.二维数组的使用

二维数组就用两个for循环去操作,同样的,数组下标都是从0开始~

3.二维数组在内存中的储存

二维数组在内存中储存也是连续的

三.进阶

1.数组名或字符串名到底是什么

很明显,数组名和数组的地址都是首元素的地址,这三者地址的数值相同,但作用不同。

可以看到,str+1和&str[0]+1是一样的,他们的地址+1只会越过一个元素(4个字节),数组名和首元素地址可以等价。但是直接取出数组的地址再+1,这时候就是直接操作整个数组了,地址直接越过了整个数组16个字节,这一点非常需要注意。函数传参一般传的都是数组名,因为在函数内部我们要操作(改变或使用)这个数组肯定是一个一个元素使用。

2.字符串的输入与输出

(1) 注意这里的scanf("%s", str),这里str不用加&,这是因为str是字符串名,字符串名就是首元素地址,所以这里的str已经是一个地址了,不用我们自己加&。

(2)这里的printf("%s", str),也是同理,在打印字符串的时候,我们需要用的是%s类型,而%s需要接收的是地址,我们这里将字符串名str传给%s,也就是字符串首元素的地址,也是'a'的地址,printf就会从'a'开始打印,直到遇到'\0'终止符。

(3)而如果我们要打印字符串里的某个(单个)字符,就需要注意要用%c,字符串的下标也是从0开始的。

3.函数中的数组传参

(1)自己定义一个数组接收

//这里我们自己再定义了一个数组int a[]来接收传过来的数组
void test(int a[])//void是返回类型,我们这里不需要返回什么东西,所以用void
{
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        printf("%d ", a[i]);
    }
}

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

我们在函数test中自己创建了一个数组int a[]来接收,并将数组a的内容打印出来。

(2)创建一个指针来接收

void test(int* p)//自己定义了一个指针*p来接收
{
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        p[i] = 6;
    }
}

int main()
{
    int i = 0;
    int arr[5] = { 1,2,3,4,5 };
    test(arr);//尝试将数组的每一个值都改成6
    for (i = 0; i < 5; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

为什么可以用指针来接收传过来的数组呢?因为前面说过,数组名就是首元素地址,所以也可以用指针来接收~ 学过指针之后,我们就会知道,在数组中

    int arr[5] = { 1,2,3,4,5 };
    arr[i]=*(arr+i)

所以在上面test(arr)传参时,我们将数组名arr也就是首元素地址传过去,我们可以用指针int*p接收,在函数中,可以直接把p当作数组名用,我们用arr[i]时,arr放在arr[i]哪,p就放在哪,也就是p[i],在test中直接用就可以。

4.常量字符串(字符串指针)

我们知道数组名或者字符串名都表示首元素地址,同理,那这个首元素的地址我们就可以储存到指针里,所以这里创建了一个指针str1指向了首元素字符'a',所以在printf中,我们知道%s后面需要对应的是一个地址,所以直接将地址(指针)str1放进去就ok了,printf就会从str1开始往后打印,直到遇到'\0'。

5.字符输入与输入缓冲区常见问题&清空输入缓冲区的方法

int main()
{
    int num;
    char ch;
    scanf("%d", &num);
    scanf("%c", &ch);
    return 0;
}

当我们尝试运行这段代码

会发现当我们输入20给num后,按下回车后想要给ch输入,这时候会发现按下回车后程序就结束了。

在解决这个bug之前,我们需要了解一个概念,叫输入缓冲区,我们的数据在传给参数的过程中,要先经过输入缓冲区,然后再一起传给参数

当我们输入20后,20就被传入到了输入缓冲区,按下回车,这时20就顺利地从输入缓冲区赋值给了num,但是这个回车就留在了输入缓冲区,因为在ascii表中,回车也相当于一个字符,有对应的键值(ASCII),是13,所以轮到向ch输入的时候,scanf直接就从输入缓冲区把剩下的那个回车给读取了,所以就跳过了scanf("%s",&ch),这一点要非常注意,回车也相当于是一个字符

那怎么解决这个bug呢,在我们scanf后,可以把输入缓冲区给清空,这里介绍几种办法。

(1)

int main()
{
    int num;
    char ch;
    scanf("%d", &num);
    setbuf(stdin, NULL);
    scanf("%c", &ch);
    return 0;
}

在一次scanf输入后,加入一句setbuf(stdin, NULL),stdin是标准输入流,也就是键盘输入,这句话的意思就是将键盘输入流指针改为空指针,可以理解成将输入缓冲区置空,个人觉得这个方法比较便捷。


(2)

int main()
{
    int num;
    char ch;
    scanf("%d", &num);
    fflush(stdin);
    scanf("%c", &ch);
    return 0;
}

在一次scanf输入后,加入一句fflush(stdin),stdin是标准输入流,也就是键盘输入,fflush就是把标准输入流清空,这个方法也不错,但是在vs编译器中不起作用,其它还是可以的。


(3)

int main()
{
    int num;
    char ch;
    char c;
    scanf("%d", &num);
    c = getchar();
    scanf("%c", &ch);
    printf("%d\n", num);
    printf("%c\n", ch);
    return 0;
}

多定义一个字符c去接收这个回车

四.冒泡排序&选择排序

1.冒泡排序

冒泡排序就是元素间两两比较,较大的就把它往后放

#include <stdio.h>
void Bubble_sort(int* arr,int sz)//这里用指针*arr接收传过来的数组名,sz为数组元素个数
{
    for (int i = 0; i < sz - 1; i++)//sz为元素个数,前面讲过元素个数为sz,那就i<sz,这里的sz-1是因为有sz个元素,需要sz-1躺排序,-1是因为排到剩下第一个数时就不用继续排了。
    {
        for (int j = 0; j < sz - 1 - i; j++)//因为下面对数组下标会用到j+1,所以这里j<sz-1//可以想象一下,经过0趟排序时,需要比较sz-1-0对数,经过一趟比较后,就变成需要比较sz-1-1=9对数,再经过一趟比较,就变成需要比较sz-1-2对,所以这里j<sz-1-i,也可以理解成一趟排序后减少一个需要排序的数,0趟排序减少0个需要排序的数。
        {
            if (arr[j] > arr[j + 1])//如果arr[j]比arr[j+1]大,就把它往后放
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }

    }
}
int main()
{
    int arr[] = { 2,3,4,3,5,7,9,4,6,7,1,3 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    Bubble_sort(arr,sz);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

10个数,只要比较10-1趟。

2.冒泡排序的优化

假如我们需要排序的数组有10个元素,则需要排9趟,但我们发现它在排了3趟的时候,它就已经有序了,那我们也没有再继续浪费算力去给它排序,我们只需要在每趟排序前,加上一个标志flag

#include <stdio.h>
void Bubble_sort(int* arr,int sz)//这里用指针*arr接收传过来的数组名,sz为数组元素个数
{
    for (int i = 0; i < sz - 1; i++)//sz为元素个数,前面讲过元素个数为sz,那就i<sz,这里的sz-1是因为有sz个元素,需要sz-1躺排序,-1是因为排到剩下第一个数时就不用继续排了。
    {

        int flag = 1;//定义一个标志flag=1

        for (int j = 0; j < sz - 1 - i; j++)//因为下面对数组下标会用到j+1,所以这里j<sz-1//可以想象一下,经过0趟排序时,需要比较sz-1-0对数,经过一趟比较后,就变成需要比较sz-1-1=9对数,再经过一趟比较,就变成需要比较sz-1-2对,所以这里j<sz-1-i,也可以理解成一趟排序后减少一个需要排序的数,0趟排序减少0个需要排序的数。
        {
            if (arr[j] > arr[j + 1])//如果arr[j]比arr[j+1]大,就把它往后放
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;

                flag = 0;//若有排序到,则证明还无序,flag=0

            }
        }

        if (flag == 1)//若flag==1,则证明已经有序
        {
            break;//跳出循环,完成排序
        }

    }
}
int main()
{
    int arr[] = { 2,3,4,3,5,7,9,4,6,7,1,3 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    Bubble_sort(arr,sz);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

3.选择排序

选择排序和冒泡排序差不多,假设数组arr有10个元素,选择排序就是在后9个元素中找出最小值(符合条件)的元素的下标,并和第1个元素交换数值,然后在剩下的9个元素中,进行这样的操作。

完整代码

void SelectSort(int* arr)//选择排序
{
    for (int i = 0; i < 10; i++)
    {
        int min = i;
        for (int j = i + 1; j < 10; j++)//在第i个元素的后面开始找最小值
        {
            if (arr[j] < arr[min])
            {
                min = j;
            }
        }
        if (i != min)//如果下标min发生了改变,就交换数值
        {
            int tmp = arr[i];
            arr[i] = arr[min];
            arr[min] = tmp;
        }
    }
}

int main()
{
    int arr[10] = { 8,3,4,5,7,9,4,6,7,1 };
    SelectSort(arr);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

五.结语

可能总结的不是很到位,后面的结构体、函数、指针会再提及数组,数组和指针、函数的关系还是很大的~

✨看完了不妨点个赞收藏吧~

✨您的支持就是博主创作的最大动力

✨再次感谢您的耐心阅读!有不懂的欢迎在评论区讨论哦~

❤️感谢您的支持!!!!!


本文转载自: https://blog.csdn.net/weixin_62244902/article/details/122225816
版权归原作者 iHikko 所有, 如有侵权,请联系我们删除。

“C语言数组,一维二维数组,字符串,函数传参中的数组,清空输入缓冲区,冒泡&amp;选择排序”的评论:

还没有评论