C语言知识

数组

        内存:数组存储数据的内存存储是连续的

        格式:存储类型 数据类型 数组名【常量表达式】

        存储类型:auto\statsic\register\const\volatile\extern

        数据类型:基类型,构造类型,空类型,指针类型

        数组名:满足命名规则即可

        数组初始化函数:memset\bzero

memset只能对整数清0和-1
#include <string.h>//头文件
void *memset(void *s, int c, size_t n);//函数原型
//参数含义:void *s:数组名     int c:清的值     size_t n:数组的字节大小
//使用格式:
    int arr[5];
    memset(arr,0,sizeof(arr));
 bzero:清0
     #include <strings.h>//头文件
    void bzero(void *s, size_t n);//函数原型
    bzero(arr,sizeof(arr));//使用格式

一维数组

 基础知识:

        1.数组的下表从0开始,数组长度为n,则下表[0,n-1]

        2.数组元素引用 arr[0]第一个元素 arr[1]第二个元素 如果n个元素,最后一个元素arr[n-1]

        3.数组的越界访问 int arr[4]={11,22,33,44};  printf("%d",arr[4]);

数组地址:

        1.数组名表示数组的首地址,也就是第一个元素的地址,数组名是一个常量,不可以自增或自减

        2.&arr[0] = arr+0,加一就跳过一个数据类型;&arr+1表示跳过一个整个数组

 数组排序方式:

数组排序有多种方法,这里列举一些常见的排序算法,它们各有特点,适用于不同的场景:

1. 冒泡排序:通过重复遍历数组,比较相邻元素并在必要时交换它们的位置,直到没有需要交换的元素为止。这是一种简单但效率较低的排序方法,平均时间复杂度为O(n^2)。

2. 选择排序:在每一轮遍历中找到数组中最小(或最大)的元素,并将其放到正确的位置上。这个过程重复进行,直到整个数组排序完成。时间复杂度同样为O(n^2)。

3. 插入排序:将数组分为已排序和未排序两部分,依次从未排序部分取出元素,插入到已排序部分的适当位置,以保持已排序部分始终有序。适合小规模数据或基本有序的数据集,时间复杂度最坏情况下为O(n^2),最好情况下为O(n)。

4. 快速排序:采用分治法策略,选择一个“基准”元素,将数组分为两部分,一部分的所有元素都比基准小,另一部分的所有元素都比基准大,然后递归地对这两部分继续进行快速排序。平均时间复杂度为O(n log n),但在最坏情况下会退化到O(n^2)。

5. 归并排序:也是一种分治算法,将数组分成两半分别排序,然后将两个有序的半部分合并成一个有序的数组。由于其稳定的性能和O(n log n)的时间复杂度,常用于大量数据的排序。

6. 堆排序:利用堆这种数据结构所设计的一种排序算法,首先将数组构造成一个最大堆(或最小堆),然后不断移除堆顶元素并调整堆的结构,最终得到一个有序的数组。时间复杂度为O(n log n)。

7. 希尔排序:是插入排序的一种更高效的版本,通过将原始数据分割成若干子序列,先使每个子序列基本有序,再对全体记录进行一次直接插入排序。它通过加大初始元素之间的间隔来改进插入排序,时间复杂度与使用的间隔序列有关,但通常优于O(n^2)。

8. 计数排序、基数排序、桶排序:这三种排序算法都是非比较排序,特别适合于整数排序,且当输入数据的范围不是很大时非常高效。计数排序基于元素的值统计每个值的出现次数;基数排序按照数字的每一位进行排序,从最低位到最高位或相反;桶排序则是将数组分布到有限数量的桶里,每个桶再分别排序。这些算法在最好情况下的时间复杂度可以达到O(n)。

注意点:

        1.如果数组越界访问的内存没有被占用,则可以访问,结果是随机值

        2.如果数组越界访问的内存被占用,存储不重要的数据,可以访问,结果随机值

        3.如果数组越界访问的内存被占用,存储重要的数据,不可以访问,结果段错误

代码实现:

#include <stdio.h>
#include <strings.h>
int main()
{
    //定义数组并初始化
    int arr[5] = {0,1,2,3,4};

    //定义数组并部分初始话,剩余未定义的用0填充
    int arr1[5] = {0,1,2}

    //省略数组长度,即[]内不填长度,编译器会自动根据元素个数来分配字节大小
    int arr2[] = {0,1,2,3};//即分配16个字节

    //计算数组长度和字节大小
    int length = sizeof(arr2)/sizeof(int);//长度为4
    int len = sizeof(arr2)/sizeof(arr2[0]);//长度为4
    int size = sizeof(arr2)//大小为16
    
    memset(arr2,0,sizeof(arr2));//arr1内元素清零
    bzero(arr1,0);//arr2内元素清零

    for (int i = 0; i < len; i++) 
    {
		printf("%p\n", &arr[i])//查看数组地址
    }
    
//相邻俩个相差4个字节
//000000722E0FF888
//000000722E0FF88C
//000000722E0FF890
//000000722E0FF894
//000000722E0FF898
//000000722E0FF89C
//000000722E0FF8A0
//000000722E0FF8A4
//000000722E0FF8A8
//000000722E0FF8AC
————————————————
    return 0;
}

       


二维数组

与一维数组相差不大,地址也是连续的,二维数组的元素可以用一维方式打印。

int arr [ ] [ ];

#include <sdtio.h>
int main()
{
//数组定义方式
1.定义以及全部初始化
    int arr[2][4]={{1,2,3,4},{5,6,7,8}}//行的全部初始化
    int arr[2][4]={1,2,3,4  ,5,6,7,8};//列的全部初始化
    int arr[][4]={1,2,3,4};  //当初始化时,可以省略二维数组的第一维  1行
    int arr[][4]={{1,2},{3,4}};//2行
2.部分初始化,剩余元素使用0填充
    int arr[2][4]={{1,2,0,0},{5,0,0,0}}//行的部分初始化
    int arr[2][4]={1,2,3,0,0,0,0,0,0};//列的全部初始化
    int arr[2][4]={0}
3.二维数组的单个赋值
    int arr[2][3];
    arr[0][0]=11;
    arr[0][1]=22;
}

一维字符数组 

定义:

一维字符数组通常用来存储字符串。

char myArray[10]; // 定义一个最多能存储9个字符加1个结束符'\0'的字符数组

这里,char表示数组元素的类型是字符,myArray是数组名,10指定了数组的大小,即它可以存储10个字符。注意,由于字符串在C语言中是以\0(空字符)结束的,所以实际上这个数组能存储的最大字符串长度为9个字符。

初始化:

1./*字符数组的单字符初始化*/
    char str[5]={'A','B','C','D','E'}; //全部初始化
    char str[5]={'A','B'}; //部分初始化,剩余元素使用'\0'=0填充
    char str[]={'A','B'};//省略数组长度,默认长度是实际元素的个数
2./*字符数组的字符串初始化*/
    char str[5]={"ABCD"}; //字符串的全部初始化,计算机默认添加\0   
    char str[5]="ABCD";
    char str[5]="A"; //部分初始化,剩余元素使用'\0'=0填充
    char str[]="AB";//省略数组长度,默认长度是实际元素的个数,算\0
3./*字符数组的错误初始化*/
    char str[5];
    str="abc";
    str[5]="abc"
4./*字符数组的赋值*/
    char str[5];
    str[0]='a';
    str[1]='b';  

字符数组长度和字符串长度的理解

                                        数组长度   字符串长度
char a[5]={'1','2','3','4','5'}; ×        5         随机值
char b[5]={'1','2','3'};         √        5         3
char c[]={'1','2'};              ×        2         随机值
char d[100]="hello";             √        100       5 
char e[]="123";                  √        4         3
char f[]="123\0abc";             √        8         3

字符数组长度sizeof:计算\0

字符串长度strlen:实际字符的个数,不计算\0

 注:系统一般对相同类型的数组申请空间时,一般空间连续。


字符串函数族

使用以下函数需要用头文件<string.h>

  1. strlen(const char *str): 返回字符串str的长度,不包括结束的空字符\0。这是一个常用于计算字符串长度的函数。
     

    #include <stdio.h>
    int main ()
    {
        char str[128] = "";
        printf("请输入字符串:");
        scanf("%s",str);
        printf("1:字符串长度为%d\n",strlen(str));
    
        int i;
        for (i = 0; str[i] !='\0'; i++);
        printf("2:字符串个数为%d\n",i);
    
        char* p = str;
        int count = 0;
        while (*p != '\0')
        {
            p++;
            count++;
        }
        printf("3:字符串长度为%d\n",count);
    }

  2. strcat(char *dest, const char *src): 将字符串src追加到字符串dest的末尾。要求dest有足够的空间来容纳合并后的字符串(包括src的长度和一个额外的\0)。此操作会改变dest的内容。

    #include <stdio.h>
    int main()
    {
        printf("字符串追加-----------------------------\n");
        /* 初始化两个字符串常量 */
        char str1[128] = "hello";
        char str2[128] = "world";
        /* 分别声明两个指针变量,指向这两个字符串的起始位置 */
        char *p = str1;
        char *q = str2;
        /* 遍历str1,找到其结尾的空字符,为后续复制str2的字符做准备 */
        while (*p != '\0')
        {
            p++;
        }
        /* 从str2的起始位置开始,逐个字符复制到str1的末尾,直到str2的结尾 */
        while (*q != '\0')
        {
            *p = *q;
            p++;
            q++;
        }
        /* 在str1的末尾添加空字符,以表示字符串的结束 */
        *p = '\0';
        /* 打印合并后的字符串 */
        printf("tr1 = %s\n", str1);
    }

  3. strcmp(const char *s1, const char *s2): 比较两个字符串s1s2。如果s1s2相等,返回0;如果s1小于s2(按字典序),返回负数;如果s1大于s2,返回值是s1-s2 ASCII。 s1,s2对应位置的字符ASCII值的大小对比,并非总字符长度。
     

    非函数实现:
    int main(int argc, const char *argv[])
    {
        char a[10]="";
        char b[10]="";
        gets(a);
        gets(b);
        printf("%d\n",strcmp(a,b));
    
    //判断ab的大小关系
        if(strcmp(a,b)>0 )//if(a>b)
            puts("a>b");
        else if(strcmp(a,b)<0 )//else if(a<b)
            puts("a<b");
        else if(strcmp(a,b)==0)//else if(a==b)
            puts("a==b");
    //非函数实现字符串比较
        char s1[100]="hello";
        char s2[100]="helLo";
    
        int i=0;
    
        while(s1[i]==s2[i])
        {
            if(s1[i]=='\0')
                break;
            i++;                                 
        }
        int sub=s1[i]-s2[i];
        if(sub>0)
            printf("%s>%s\n",s1,s2);
        else if(sub<0)
            printf("%s<%s\n",s1,s2);
        else if(sub==0)
    
            printf("%s>%s\n",s1,s2);
        return 0;
    }
    

  4. strcpy(char *dest, const char *src): 将字符串src复制到dest中。要求dest有足够的空间来容纳整个src字符串(包括结束的\0)。此操作会覆盖dest原来的内容。
     

    #include <stdio.h>
    int main()
    {
         printf("字符串拷贝----------------------------------\n");
        char str1[128] = "hello_world ! 98765";
        char str2[128] = "0123456789";
        char *p = str1;
        char *q = str2;
        while (*p != '\0') // 要赋值的依次判断
        {
            *q = *p;
            p++;
            q++;
        }
        *q = '\0';//将最后一个字符赋值为'\0'
        printf("tr2 = %s\n", str2);
    }

  5. strncpy(char *dest, const char *src, size_t n): 将字符串src的前n个字符复制到dest中。与strcpy不同,即使src不足n个字符,dest也会被填充至n个字符(剩余部分通常填充\0),但不会超出n。需要注意,如果源字符串中没有遇到\0且复制了n个字符,dest可能不会被正确地以\0结束。
     

  6. strncat(char *dest, const char *src, size_t n): 类似于strcat,但是只追加src的前n个字符到dest的末尾。同样,确保dest有足够的空间。

  7. strstr(const char *haystack, const char *needle): 在字符串haystack中搜索第一次出现字符串needle的位置,并返回该位置的指针。如果未找到,则返回NULL。

  8. strtok(char *str, const char *delim): 用于将字符串分割成一系列子字符串,基于分隔符delim进行切割。它会修改原字符串str,并返回指向第一个子字符串的指针。后续调用strtok应传递NULL作为第一个参数,以便继续分割剩余的部分。注意,strtok是不线程安全的,因为它使用静态变量来记住上次分割的位置。

  9. strcasecmp(const char *s1, const char *s2):不区分大小写的字符串比较函数,使用方法和3一致。



二维数组字符

与一维类似 ,将前面学习透彻,此节可游刃有余。

简单介绍:

格式:存储类型        char        数组名        [常量表达式1]        [常量表达式2];

1.常量表达式1:第一维,表示行,表示字符串的个数

2.常量表达式2:第二维,表示列,表示每个字符串的字节大小

        char a;//存储1个字符

        char str  [10];//存储10个字符,一个字符串

        char str  [3]  [5];//存储5个字符串,每个字符串10个字符

一维初始化:char arr[3] = " ";
二维初始化:char arr[3][5] = {""};//大括号不能丢
    char a[3][5]={"ABCD","abcd","1234"};

 用的不多且简单。

函数

C语言又称为函数语言,C语言的基本单位是函数。

函数:实现特定功能的代码块

函数的优点:成程序更简洁,运行速度加快

格式

优点:成程序更简洁,运行速度加快

函数的定义格式:

格式: 存储类型 数据类型 函数名(参数列表) //函数头 { //函数体 函数体; }

1.存储类型:auto\static\extern\register\const\volatile 注意:函数省略存储类型默认是extern

2.数据类型:基类型(int float double char),构造类型(数组,结构体,共用体),空类型(void),指针类型 如果数据类型=void,无返回值函数 如果数据类型!=void,有返回值函数

3.函数名:满足命名规范即可 函数名一般驼峰命名法,下划线链接 Output output_fun

4.():函数的标志,不可以省略

5.参数列表:可有可无,如果没有则默认是无参函数,如果存在则称为有参函数,参数列表如果是多个则使用逗号隔开

6.{} 不允许省略

分类

1.库函数

库函数:系统自带的函数

IO函数:scanf\printf\getchar\putchar\gets\puts

字符函数:strlen\strcat\strcpy\strcmp\strcarcmp

数学函数:pow\sqrt

main函数:一个程序只能有一个mian函数,main函数可以调用任意函数,但是任意函数不允许调用main

2.自定函数

存储类型  函数名       (接收的参数)   函数体
viod     function    ()            {}
int...                

 函数内return返回的数据类型是什么,返回类型就要写什么;

 注意:

        自定义函数要在主函数上面,若再下面,则需要开头声明,否则报错;

全局变量和局部变量 

全局变量局部变量
作用域全局变量在程序的任何地方都是可见的,可以在文件的任何部分被访问和修改。它们的作用域是整个程序,跨越所有函数和代码块。局部变量的作用域仅限于定义它们的函数、方法或代码块内部。这意味着它们只能在其定义的上下文中被访问。
生命周期全局变量的生命周期与程序的运行周期相同。它们在程序启动时被创建,在程序结束时被销毁。局部变量的生命周期与其所在的作用域相关联。当函数或代码块被执行时,局部变量被创建,执行完毕后,这些变量就会被销毁,内存被回收。
存储位置通常存储在全局数据区或静态存储区,这使得它们在程序的整个生命周期中都存在。存储在栈区,栈空间在函数调用时分配,函数返回时自动释放。
使用方式可以在程序的任何地方直接使用,无需特别的声明或传递。仅能在定义它们的函数或代码块内部使用,如果需要在其他函数中使用,需要通过参数传递或返回值等方式。
同名变量不能重复

可以重复

默认值全局变量可能会被赋予默认值,如C/C++中,整型默认为0,指针默认为NULL。通常不会自动初始化,如果没有明确赋值,其内容是未定义的随机值,使用前必须手动初始化。

结构体

结构体(Struct)是C语言中一种复合数据类型,允许程序员将不同类型的数据组合在一起,形成一个单独的实体或记录。这种数据组织方式非常灵活,能够更加高效、直观地管理和操作复杂的数据结构。下面是结构体的一些核心概念和作用:

概念

  1. 定义结构体:结构体由关键字struct开始,后面跟着结构体的名称(可选),接着是包含在一对大括号{}中的成员列表。每个成员可以是基本数据类型(如int, float, char等)或其他结构体类型。

    struct ExampleStruct {
        int id;
        char name[50];
        float score;
    };
  2. 声明结构体变量:定义结构体之后,需要声明结构体变量来存储具体的数据。这可以像声明其他基本类型变量一样进行。

    struct ExampleStruct student1;
  3. 初始化结构体:结构体变量可以在声明时被初始化,或者通过赋值操作来初始化。

    struct ExampleStruct student2 = {1, "Alice", 95.5};

作用

  1. 数据封装:结构体提供了一种方式,将相关数据项封装在一起,便于管理和操作。这对于描述现实世界中的对象(如学生信息、图书记录、员工详情等)非常有用。

  2. 提高代码可读性和可维护性:通过命名结构体成员,代码变得更加清晰易懂。当查看代码时,可以直接从结构体成员名了解各部分数据的意义,而不必依赖注释或上下文推断。

  3. 减少参数传递:在函数间传递结构体变量时,可以一次性传递一组相关数据,避免了为每个单独的数据成员分别传递参数的繁琐。

  4. 节省内存空间:虽然结构体成员之间可能会有对齐填充(padding),但在很多情况下,将相关数据整合在一个结构体内相比分散存储能更有效地利用内存,尤其是在处理大量同类数据时。

  5. 复用性和灵活性:一旦定义了结构体类型,就可以在程序的多个地方声明该类型的变量,甚至定义指向该结构体的指针或数组,从而实现代码的复用和灵活的数据操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值