ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C语言知识

C语言知识 数组内存数组存储数据的内存存储是连续的格式存储类型 数据类型 数组名【常量表达式】存储类型auto\statsic\register\const\volatile\extern数据类型基类型构造类型空类型指针类型数组名满足命名规则即可数组初始化函数memset\bzeromemset只能对整数清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] arr0,加一就跳过一个数据类型arr1表示跳过一个整个数组数组排序方式数组排序有多种方法这里列举一些常见的排序算法它们各有特点适用于不同的场景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] {01234} //定义数组并部分初始话,剩余未定义的用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}; //部分初始化剩余元素使用\00填充 char str[]{A,B};//省略数组长度默认长度是实际元素的个数 2./*字符数组的字符串初始化*/ char str[5]{ABCD}; //字符串的全部初始化计算机默认添加\0 char str[5]ABCD; char str[5]A; //部分初始化剩余元素使用\00填充 char str[]AB;//省略数组长度默认长度是实际元素的个数,算\0 3./*字符数组的错误初始化*/ char str[5]; strabc; 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.hstrlen(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); }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); }strcmp(const char *s1, const char *s2): 比较两个字符串s1和s2。如果s1和s2相等返回0如果s1小于s2按字典序返回负数如果s1大于s2返回值是s1-s2 ASCII。 s1s2对应位置的字符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(ab) puts(ab); else if(strcmp(a,b)0 )//else if(ab) puts(ab); else if(strcmp(a,b)0)//else if(ab) puts(ab); //非函数实现字符串比较 char s1[100]hello; char s2[100]helLo; int i0; while(s1[i]s2[i]) { if(s1[i]\0) break; i; } int subs1[i]-s2[i]; if(sub0) printf(%s%s\n,s1,s2); else if(sub0) printf(%s%s\n,s1,s2); else if(sub0) printf(%s%s\n,s1,s2); return 0; }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); }strncpy(char *dest, const char *src, size_t n): 将字符串src的前n个字符复制到dest中。与strcpy不同即使src不足n个字符dest也会被填充至n个字符剩余部分通常填充\0但不会超出n。需要注意如果源字符串中没有遇到\0且复制了n个字符dest可能不会被正确地以\0结束。strncat(char *dest, const char *src, size_t n): 类似于strcat但是只追加src的前n个字符到dest的末尾。同样确保dest有足够的空间。strstr(const char *haystack, const char *needle): 在字符串haystack中搜索第一次出现字符串needle的位置并返回该位置的指针。如果未找到则返回NULL。strtok(char *str, const char *delim): 用于将字符串分割成一系列子字符串基于分隔符delim进行切割。它会修改原字符串str并返回指向第一个子字符串的指针。后续调用strtok应传递NULL作为第一个参数以便继续分割剩余的部分。注意strtok是不线程安全的因为它使用静态变量来记住上次分割的位置。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 注意函数省略存储类型默认是extern2.数据类型基类型(int float double char),构造类型(数组结构体共用体)空类型(void),指针类型 如果数据类型void,无返回值函数 如果数据类型!void,有返回值函数3.函数名满足命名规范即可 函数名一般驼峰命名法下划线链接 Output output_fun4.():函数的标志,不可以省略5.参数列表可有可无如果没有则默认是无参函数如果存在则称为有参函数参数列表如果是多个则使用逗号隔开6.{} 不允许省略分类1.库函数库函数系统自带的函数IO函数scanf\printf\getchar\putchar\gets\puts字符函数strlen\strcat\strcpy\strcmp\strcarcmp数学函数pow\sqrtmain函数一个程序只能有一个mian函数main函数可以调用任意函数但是任意函数不允许调用main2.自定函数存储类型 函数名 接收的参数 函数体 viod function {} int...函数内return返回的数据类型是什么返回类型就要写什么注意自定义函数要在主函数上面若再下面则需要开头声明否则报错全局变量和局部变量全局变量局部变量作用域全局变量在程序的任何地方都是可见的可以在文件的任何部分被访问和修改。它们的作用域是整个程序跨越所有函数和代码块。局部变量的作用域仅限于定义它们的函数、方法或代码块内部。这意味着它们只能在其定义的上下文中被访问。生命周期全局变量的生命周期与程序的运行周期相同。它们在程序启动时被创建在程序结束时被销毁。局部变量的生命周期与其所在的作用域相关联。当函数或代码块被执行时局部变量被创建执行完毕后这些变量就会被销毁内存被回收。存储位置通常存储在全局数据区或静态存储区这使得它们在程序的整个生命周期中都存在。存储在栈区栈空间在函数调用时分配函数返回时自动释放。使用方式可以在程序的任何地方直接使用无需特别的声明或传递。仅能在定义它们的函数或代码块内部使用如果需要在其他函数中使用需要通过参数传递或返回值等方式。同名变量不能重复可以重复默认值全局变量可能会被赋予默认值如C/C中整型默认为0指针默认为NULL。通常不会自动初始化如果没有明确赋值其内容是未定义的随机值使用前必须手动初始化。结构体结构体Struct是C语言中一种复合数据类型允许程序员将不同类型的数据组合在一起形成一个单独的实体或记录。这种数据组织方式非常灵活能够更加高效、直观地管理和操作复杂的数据结构。下面是结构体的一些核心概念和作用概念定义结构体结构体由关键字struct开始后面跟着结构体的名称可选接着是包含在一对大括号{}中的成员列表。每个成员可以是基本数据类型如int,float,char等或其他结构体类型。struct ExampleStruct { int id; char name[50]; float score; };声明结构体变量定义结构体之后需要声明结构体变量来存储具体的数据。这可以像声明其他基本类型变量一样进行。struct ExampleStruct student1;初始化结构体结构体变量可以在声明时被初始化或者通过赋值操作来初始化。struct ExampleStruct student2 {1, Alice, 95.5};作用数据封装结构体提供了一种方式将相关数据项封装在一起便于管理和操作。这对于描述现实世界中的对象如学生信息、图书记录、员工详情等非常有用。提高代码可读性和可维护性通过命名结构体成员代码变得更加清晰易懂。当查看代码时可以直接从结构体成员名了解各部分数据的意义而不必依赖注释或上下文推断。减少参数传递在函数间传递结构体变量时可以一次性传递一组相关数据避免了为每个单独的数据成员分别传递参数的繁琐。节省内存空间虽然结构体成员之间可能会有对齐填充padding但在很多情况下将相关数据整合在一个结构体内相比分散存储能更有效地利用内存尤其是在处理大量同类数据时。复用性和灵活性一旦定义了结构体类型就可以在程序的多个地方声明该类型的变量甚至定义指向该结构体的指针或数组从而实现代码的复用和灵活的数据操作。
返回列表